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 <mar...@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
> 

Reply via email to