Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convince GroovyShell to maintain state over eval() calls?

I'm trying to use Groovy to create an interactive scripting / macro mode for my application. The application is OSGi and much of the information the scripts may need is not know up front. I figured I could use GroovyShell and call eval() multiple times continually appending to the namespace as OSGi bundles are loaded. GroovyShell maintains variable state over multiple eval calls, but not class definitions or methods.

goal: Create a base class during startup. As OSGi bundles load, create derived classes as needed.

like image 679
basszero Avatar asked Dec 15 '25 21:12

basszero


1 Answers

I am not sure about what you mean about declared classes not existing between evals, the following two scripts work as expected when evaled one after another:

class C {{println 'hi'}}
new C()

...

new C()

However methods become bound to the class that declared them, and GroovyShell creates a new class for each instance. If you do not need the return value of any of the scripts and they are truly scripts (not classes with main methods) you can attach the following to the end of every evaluated scrips.

Class klass = this.getClass()
this.getMetaClass().getMethods().each {
  if (it.declaringClass.cachedClass == klass) {
    binding[it.name] = this.&"$it.name"
  }
}

If you depend on the return value you can hand-manage the evaluation and run the script as part of your parsing (warning, untested code follows, for illustrative uses only)...

String scriptText = ...
Script script = shell.parse(scriptText)
def returnValue = script.run()
Class klass = script.getClass()
script.getMetaClass().getMethods().each {
  if (it.declaringClass.cachedClass == klass) {
    shell.context[it.name] = this.&"$it.name"
  }
}
// do whatever with returnValue...

There is one last caveat I am sure you are aware of. Statically typed variables are not kept between evals as they are not stored in the binding. So in the previous script the variable 'klass' will not be kept between script invocations and will disappear. To rectify that simply remove the type declarations on the first use of all variables, that means they will be read and written to the binding.

like image 189
shemnon Avatar answered Dec 17 '25 13:12

shemnon



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!