In my app, I load a script file from the classpath into a String, then pass it into the ScriptEngine. Howerver, the breakpoint set in the script file doesn't trigger. How can I make it work? I use Intellij IDEA.
ScriptEngine engine = ...;
String script = FileUtils.readFileToString(file);
Bindings bindings = engine.createBindings();
Object result = engine.eval(script, bindings);
Since the ScriptEngine.eval() method only takes the script as a String or as a generic Reader, I don't think it is possible to achieve this. The GroovyScriptEngineImpl class will generate a script name and compile it to a class at runtime, which will make it hard (impossible?) for the debugger to know which breakpoint(s) are associated with the running script.
It might not be a solution for you, but if you instead invoke the script using GroovyShell, then it pretty much works out of the box.
Example:
File file = new File(scriptDir, "ScriptToRun.groovy");
Binding binding = new Binding();
Object result = new GroovyShell(binding).evaluate(file);
Just remember to set the correct package in the script if it is not located at the root.
Related
I'm working on a Kotlin multi-platform project, and I need my JS tests to run on Node.js but with custom command line arguments (specifically I need node to run with the --expose-gc flag, because some tests need to trigger garbage collection).
Looking at the documentation for the Gradle Kotlin JS DSL I didn't find any mention of how to do that; does anyone know whether it's at all possible and how?
Unfortunately can not answer your question directly, but there is some suggestion to help you with reverse engineering.
Let's start from some example. We have Gradle tasks to run our project using webpack's dev server such as browserDevelopmentRun, browserProductionRun (not sure if multi-platform projects have it, but JS projects do). We can add:
println(tasks.named("browserProductionRun").get().javaClass)
to build.gradle.kts to find out the exact class used for this task. When we sync Gradle, it outputs:
org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack_Decorated
Now we know the exact class of this task so we can investigate its API. The auto completion or navigating inside of the KotlinWebpack class helps us to find out that it has a helpful nodeArgs property to configure NodeJS arguments for it, so we can set them, for example:
tasks.named("browserProductionRun", org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack::class).get().nodeArgs.add("--trace-deprecation")
Getting back to your question.
In your case I guess you need to investigate the browserTest task. Let's get some info about it by adding:
println(tasks.named("browserTest").get().javaClass)
to build.gradle.kts - a-ha - it seems to be of the org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest_Decorated type. Let's check what's inside. Open KotlinJsTest.kt somehow - for example by typing its name into the window being opened by CMD + Shift + O (make sure to select "All Places" here) or just by typing its name somewhere in build.gradle.kts and navigating inside it.
The only interesting thing I see inside this open class is the following block:
override fun createTestExecutionSpec(): TCServiceMessagesTestExecutionSpec {
val forkOptions = DefaultProcessForkOptions(fileResolver)
forkOptions.workingDir = npmProject.dir
forkOptions.executable = nodeJs.requireConfigured().nodeExecutable
val nodeJsArgs = mutableListOf<String>()
return testFramework!!.createTestExecutionSpec(
task = this,
forkOptions = forkOptions,
nodeJsArgs = nodeJsArgs,
debug = debug
)
}
So maybe it can work out to create your own extension of this class, override its createTestExecutionSpec method and provide nodeJsArgs as you need inside it. After that you'll be needing to declare another Gradle task to launch tests inside build.gradle.kts which will use this new extended class.
Could anyone tell me how to invoke an action automation script from an automation script in Maximo. I have achieved this before but can't remember/find the procedure used to call an action script.
When invoking another script, it doesn't matter if it is an action script or a script just sitting out there with no launch points (or even one already triggering on its own otherwise with, say, an object save launch point). The below code assumes Jython and Maximo of at least 7.6.0.x (I forget exactly what version added this feature).
First, you do need to make sure you import java.util.HashMap into your script. Then, you start by creating a new HashMap. This is the script context that you will put all of your implicit variables into for the other script. You then use the "invokeScript" method of the implicit service or script context (if this is in an integration script) object. The first parameter of that method is the name of the new script you want to invoke and the second parameter is your HashMap that is the context for the new script.
from java.util import HashMap
subScriptCtx = HashMap()
subScriptCtx.put("mbo", woMbo)
subScriptCtx.put("var1", someObject)
subScriptCtx.put("var2", MXServer.getMXServer().getDate())
...
service.invokeScript("OTHERSCRIPTNAME", subScriptCtx)
I have been modifying the default groovy template that the Email Ext plugin supplies.
Firstly, I had to modify the JUnitTestResult and need to format it accordingly to my need. I found in the it.JUnitTestResult, it is a reference to the ScriptContentBuildWrapper class. And then I was able to format the JUnitTestResult according to my need.
Now I am facing a second difficulty:
Along with those contents, I need to append more content from a file that resides in the job workspace. How to access the files that reside in the workspace directory.
I would be interested to know how I can access the build context object. Whats the java class name and things like that.
Just use build which returns an AbstractBuild
Try -
build.workspace
Which returns the FilePath of the directory where the build is being built.
See AbstractBuild.getWorkspace.
Tip: in Groovy, you can avoid the "get" and use field-like access notation.
Depending on which version of email-ext you are using, you can use the tokens provided to get access to things, so if you look at the token help, you'll see lots of tokens. These can be used in the groovy templates to do the same thing. For instance, the FILE token can be used in the Groovy by doing FILE(path: 'path/to/file') and it will replace with the contents of the file (only works on files that are below the workspace).
The build object is not available directly in all groovy scripts (e.g. groovy build script, groovy system build script, groovy post-build script, groovy script as evaluated in email-ext). The most portable way of obtaining build object in groovy script for a running build is:
import hudson.model.*
def build = Thread.currentThread().executable
Then you can get workspace and access files inside like this:
workspace = build.getEnvVars()["WORKSPACE"]
afilename = workspace + "/myfile"
afile = new File(afilename);
// afile.write "write new file"
// afile << "append to file"
// def lines = afile.readLines()
Given the following directory structure:
/home/some/random/foler/myScript.grooy
... how can I programmatically obtain the path to myScript.grooy parent directory right in the script itself?
Ultimately I'm trying to read in several files from the same directory the script is in.
EDIT: trying to run it on Windows 7, Groovy 2.0.1, groovy console
Well, the solution is in Java's File class:
println new File(".").absolutePath
If you want to obtain every groovy script in the same directory, maybe you could use some of other facilities in the Groovy JDK, like eachFile:
def files = []
new File(".").eachFile {
if (it.name.endsWith(".groovy") ) files << it
}
println files
If you want the running script name, well, you've got a problem (https://issues.apache.org/jira/browse/GROOVY-1642)
Accordingly to that JIRA, this is the current workaround (which doesn't always work):
URL scriptUrl = getClass().classLoader.resourceLoader
.loadGroovySource(getClass().name)
I'm using GroovyShell as an "expression evaluator/engine" inside my program. It accepts two inputs: (a) one or more init scripts (b) user-defined script. Both are then concatenated at runtime as big chunk of script (text) and feed to the shell.
String initScripts = getFromDB()
String userScript = getFromUser()
def shell = new GroovyShell()
output = shell.evaluate(initScripts + userScript)
The above code will run in a loop, where the contents of userScript will vary.
So far, initScripts only contain variable definitions (e.g. def $yyyy = new Date().format('yyyy')) which might be referenced in userScript (e.g. print "$yyyy 001").
Is there any more efficient approach for this? (E.g. reusing the shell, how?) Because right now it's very slow.
Edit: Groovy is a must. Please don't recommend other scripting engine.
Edit: I'm thinking whether GroovyShell can do this (pseudo-code):
def shell = new GroovyShell()
shell.evaluate(initScripts)
for each userScript in DB {
shell.put(userScript )
def result = shell.evaluateThat()
println "Result is $result"
}
Is this possible? (Last time I googled it's not possible, but I'm hoping I'm wrong)
You can cache the GroovyShell, you don't need to create a new one always:
final static GroovyShell shell = new GroovyShell()
Also if you run one Script many times you may cache them too. You can create a Script with GroovyShell.parse(String scriptText), use Script.run() to run the script.
This section of the documentation might help too, instead of scripts you can also create groovy objects dynamically.
I guess you could avoid the weight of building a full groovy environment each time.
Since Java 6, there is a scripting API support in Java, which allows you to use lightweight scripting engines.
As an example, see this page in groovy website explaining how to start a groovy script in a Java application using GroovyScriptEngineImpl.
notice you may loose some groovy goodnesses, like maybe Groovy grape, but you'll be able to
reuse your script engine
ensure your script evals in application context (eventually benefitting from Java objects usage)
EDIT one important thing to notice is that neither GroovyScriptEngineImpl nor GroovyShell can guarantee you any kind of thread safety, as any groovy script is free to spawn any number of thread. In fact, the only way you could guarantte thread safety would be by installing a SecurityManager forbidding thread operations. In fact, even that wouldn't guarantee thread safety (as this thread safety could only be achieved by ensuring all your Java code base is thread safe).
I end up doing this:
def shell = new GroovyShell()
shell.evaluate(initScripts)
for( i in 1..count )
{
output = shell.evaluate(userScripts);
}
And just to be safe, you can put shell in ThreadLocal or pool.