You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@groovy.apache.org by Marc Paquette <ma...@mac.com> on 2015/11/14 16:31:45 UTC
Reloading of script with GroovyClassLoader
Hello all,
I am using `GroovyClassLoader` to execute groovy scripts at runtime and I want modifications to the script text (stored on the filesystem) to be picked up on subsequent execution of the script but I don't want the script to be re-compiled each time. From my understanding, creating a `GroovyClassLoader` with a `CompilerConfiguration` where `recompileGroovySource` is set to true should give this outcome, but it does not seems to work the way I am using it.
I expect the following script to pass, but the second assert fails, anybody can tell me what I am doing wrong (or why my assumptions on how this is supposed to work are false) ?
----
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.runtime.InvokerHelper
class MyGcl {
public static void main(String[] args) {
CompilerConfiguration config
config = new CompilerConfiguration()
config.setRecompileGroovySource(true)
GroovyClassLoader gcl = new GroovyClassLoader(getClassLoader(), config)
Binding binding = new Binding()
File fooFile = new File("foo.groovy")
fooFile.text = '''
alpha='abc'
num='123'
alpha
'''
def result = InvokerHelper.createScript(gcl.parseClass(fooFile), binding).run()
assert 'abc' == result
fooFile.text = fooFile.text + '''
num
'''
result = InvokerHelper.createScript(gcl.parseClass(fooFile), binding).run()
assert '123' == result
}
}
----
Thanks in advance,
Marc
Re: Reloading of script with GroovyClassLoader
Posted by Marc Paquette <ma...@mac.com>.
Answering my own question in case it helps others.
My ultimate goal is to be able to execute DSL scripts at run-time from the filesystem, supporting reloading, import customizers and a base script class.
First, `GroovyShell` does not do caching, so it always re-parse the scripts. `GroovyClassLoader` supports reloading, but with `loadClass` methods, not `parseClass` (found it by inspecting the source code). But `loadClass` is for classes, not for scripts (it is possible to use it with scripts, but much more complicated). `GroovyScriptEngine` is more suited to doing this with scripts. To support reloading, its `CompilerConfiguration` must have `recompileGroovySource` set to true. Since it has no constructor taking a compiler configuration but exposes the `config` through getters/setters, one can get reloading by setting the compiler configuration after instance creation of `GroovyScriptEngine`. However, when a script is actually executed, the compiler configuration in effect is not inherited from our instance of `GroovyScriptEngine`, thus customization of the `scriptBaseClass` or import customizers added to the `compilationCustomizers` are not available for the script.
The solution, as found while looking at `groovy.util.GroovyScriptEngineReloadingTest#testCompilerConfigurationInheritance` in groovy sources, is to first create a `GroovyClassLoader` with our `CompileConfiguration` properly setup and use that as the parent class loader when instantiating `GroovyScriptEngine`. This way, the customized compiler configuration is used when checking if a script should be reloaded and when running scripts.
Here is a script showing all of this. Note the `Thread.sleep(1000)` statements : on my system, file modification time has a granularity of a second, so each change to the script file needs to be at least one second after the last one or the previous compiled version of the script will be used.
----
import org.codehaus.groovy.control.CompilerConfiguration
class MyGse {
static abstract class MyBaseScript extends Script {
String hello() { return 'Je suis Paris' }
}
public static void main(String[] args) {
CompilerConfiguration config = new CompilerConfiguration()
config.setRecompileGroovySource(true)
config.scriptBaseClass = MyBaseScript.name
GroovyScriptEngine gse = new GroovyScriptEngine(".", new GroovyClassLoader(getClassLoader(), config))
Binding binding = new Binding(alpha:'abc', num:'123')
File fooFile = new File("foo.groovy")
fooFile.text = 'alpha'
def result = gse.run(fooFile.name, binding)
assert 'abc' == result
Thread.sleep(1000)
fooFile.text = 'num'
result = gse.run(fooFile.name, binding)
assert '123' == result
Thread.sleep(1000)
fooFile.text = 'hello()'
assert 'Je suis Paris' == gse.run(fooFile.name, binding)
Thread.sleep(1000)
}
}
----
Drop this in a file named 'MyGse.groovy', cd to its parent directory and run it with `groovy MyGse.groovy`.
Hope this helps someone,
Marc
> Le 2015-11-14 à 10:31, Marc Paquette <ma...@mac.com> a écrit :
>
> Hello all,
>
> I am using `GroovyClassLoader` to execute groovy scripts at runtime and I want modifications to the script text (stored on the filesystem) to be picked up on subsequent execution of the script but I don't want the script to be re-compiled each time. From my understanding, creating a `GroovyClassLoader` with a `CompilerConfiguration` where `recompileGroovySource` is set to true should give this outcome, but it does not seems to work the way I am using it.
>
> I expect the following script to pass, but the second assert fails, anybody can tell me what I am doing wrong (or why my assumptions on how this is supposed to work are false) ?
>
>
> ----
> import org.codehaus.groovy.control.CompilerConfiguration
> import org.codehaus.groovy.runtime.InvokerHelper
> class MyGcl {
>
> public static void main(String[] args) {
> CompilerConfiguration config
> config = new CompilerConfiguration()
> config.setRecompileGroovySource(true)
> GroovyClassLoader gcl = new GroovyClassLoader(getClassLoader(), config)
> Binding binding = new Binding()
>
> File fooFile = new File("foo.groovy")
> fooFile.text = '''
> alpha='abc'
> num='123'
> alpha
> '''
> def result = InvokerHelper.createScript(gcl.parseClass(fooFile), binding).run()
> assert 'abc' == result
>
> fooFile.text = fooFile.text + '''
> num
> '''
> result = InvokerHelper.createScript(gcl.parseClass(fooFile), binding).run()
> assert '123' == result
> }
> }
> ----
>
> Thanks in advance,
> Marc
>