I am using a GroovyShell to run Groovy scripts in a JVM. The GroovyShell is using several AST customizers and some predefined methods defining a DSL. But I found by hasard that it was possible to write "run()" in any groovy script, making it to run in an infinite loop.
Does anyone know how to disallow calling "run()" in a script that is already running ?
I tried to override the script's invokeMethod and set a flag to disable the run method when the script is running... but whatever I do, I can still call this run() method in the script, it seems without going trough the script's invokeMethod.
Thanks in advance for your help
Add this line to every script you execute:
this.metaClass.run { null }
Related
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'm using the Groovy Grails Tool Suite to practice Groovy. I want to run a Groovy Shell, but when I create a new shell and try to run it, I get this error:
Could not find $jarName on the class path. Please add it manually
What does this mean, and how do I resolve this?
I believe this is happening because JLine can't be found on your classpath. I submitted a PR to make the error message in this case actually useful.
I had a similar problem with this exact same message, but the reason was that I was attempting to run the script without specifying which script to run. Ensure you have the script open in the editing window and trying running it again - that got rid of the message for me.
Now, I am writting a Groovy script to invoke other's interface. But I need change my current working path when running the script. I know it is not possible in Java. Is it possible in Groovy?
If you can run other script as separate process, you can give ProcessBuilder parameter working dir:
def processBuilder=new ProcessBuilder(command)
processBuilder.directory(new File("Working dir"))
def process = processBuilder.start()
or
command.execute(null, new File("Working dir"))
so that process will switch to your new folder and execute it there.
As Groovy runs on JVM, the same restrictions apply. Unfortunately it is not possible.
Changing the current working directory in Java?
JDK bug
Java/groovy doesn't really "Have" a working directory as far as I can tell. The shell that launched groovy has one and any child "commands" inherit from that shell diretly.
Java also seems to read the current directory of the shell and store it in "user.dir". This is used as a base for the "File" object so if you System.setProperty("user.dir", "c:/windows") it will change future invocations of new File(".") but will not change the parent shell directory (and therefore not the child directories).
Here are three "Work-Arounds" that may work for different scenarios:
1) I KIND OF overcame this for a very specific task... I wanted to implement "cd" as a groovy script. It was only possible because all my scripts were already being "wrapped" in a batch file. I made it so that my script could create a file called "afterburner.cmd" that, if it existed, would be executed when the script exits. There was some batch file trickery to make this work.
A startup cmd file could also "Set" the current directory before invoking your groovy script/app.
By the way, Having a startup cmd has been much more helpful than I'd thought it would be--It makes your environment constant and allows you to more easily deploy your "Scripts" to other machines. I even have mine compile my scripts to .classes because it turned out to be faster to compile a .groovy to a .class and start the .class with "Java" than it was to just run the script with "groovy"--and usually you can skip the compile step which makes it a LOT faster!
2) For a few small commands, you might write a method like this:
def currentDir = "C:\\"
def exec(command, dir = null) {
"cmd /c cd /d ${dir?:currentDir} && $command".execute().text
}
// Default dir is currentDir
assert exec("dir").endsWith("C:\\>")
// different dir for this command only
assert exec("dir", "c:\\users").endsWith("C:\\users")
// Change default dir
currentDir = "C:\\windows"
assert exec("dir").endsWith("C:\\windows")
it will be slower than "".execute() if "cmd" is not required.
3) Code a small class that maintains an "Open" command shell (I did this once, there is a bit of complexity), but the idea is:
def process="cmd".execute()
def in=process.in
def out=process.out
def err=process.err
Now "in" is an input stream that you could spin off/read from and "out" is an output stream that you can write commands to, keep an eye on "err" to detect errors.
The class should write a command to the output, read the input until the command has completed then return the output to the user.
The problem is detecting when the output of any given command is complete. In general you can detect a "C:..." prompt and assume that this means that the command has finished executing. You could also use a timeout. Both are pretty fallible. You can set that shell's prompt to something unique to make it much less fallible.
The advantage is that this shell can remain open for the entire life of your app and can significantly increase speed since you aren't repeatedly creating "cmd" shells. If you create a class (let's call it "CommandShell") that wraps your Process object then it should be really easy to use:
def cmd=new CommandShell()
println cmd.execute("cd /d c:\\")
println cmd.execute("dir") // Will be the dir of c:\
I wrote a groovy class like this once, it's a lot of experimenting and your instance can be trashed by commands like "exit" but it's possible.
you can wrap it up in a dir block.
eg :
dir('yourdirectory') {
codeblock
}
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.
I have a couple of post build actions that I run on my Hudson build. They are currently bash scripts, and if I want to fail the biuld from them, I just return a non-zero value. Is there a way to fail the build from another scripting language (such as groovy)? Do I just write a script that also returns non-zero? Or is there a particular plugin I need to enable this?
thanks,
Jeff
A way to do this programmatically within the Groovy script is:
throw new hudson.AbortException("Something was in my eye...")
The Groovy Postbuild Plugin seems to be what you need, and has a method called buildFailure() which sets the build result to FAILURE.
If your post build action is a standard build step (like a shell script). it is enough to exit that shell script with a non-zero value. I don't know if all scripting languages allow you to return a non-zero return value, that will then become the return value of the script (if you don't call exit or an equivalent command specifically in a script than the return value of the last executed command becomes the return value of the script).
If you have troubles with this approach, there is always the option to use the Log Parser Plugin to fail a build on error.