Using GroovyShell as "expression evaluator/engine" (or: How to reuse GroovyShell)
Asked Answered
T

3

9

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)

Theorbo answered 16/3, 2011 at 9:16 Comment(0)
V
7

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.

Vinita answered 16/3, 2011 at 19:25 Comment(4)
Is GroovyShell stateless and thread safe? Some people can use the program at the same time with different script (that's for sure, since userScript might be different per user, even per invocation).Theorbo
Yes it is thread safe, and stateless (but as i see it doesn't create defensive copies if not using the default constructor - shouldn't be a problem in your case) svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/…Vinita
I think you can reuse the shell object, but I do not think you can reuse the script object. Script instances use a Map for binding, which is not thread safe.Durrell
I think GroovyShell is marginally threadsafe given that it takes a Binding object as constructor which can be changed.Avestan
A
1

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).

Alabama answered 16/3, 2011 at 9:29 Comment(0)
T
0

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.

Theorbo answered 17/3, 2011 at 6:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.