javagroovyclassloaderurlclassloadergroovyclassloader

How can I execute groovy scripts in an isolated classloader?


I'm trying to run groovy scripts in an isolated classloader so that they are not executed in the context of the calling class' dependencies.

    Path log4j = Paths.get("..../lib/log4j-1.2.17.jar");
    Path groovy = Paths.get("..../lib/groovy-all-2.1.3.jar");
    RootLoader rootLoader = new RootLoader(new URL[] { log4j.toUri().toURL(), groovy.toUri().toURL() }, null);
    GroovyScriptEngine engine = new GroovyScriptEngine(".../src/main/resources", rootLoader);
    engine.run("Standalone.groovy", "");
Standalone.groovy:
import org.apache.log4j.BasicConfigurator
import org.apache.log4j.Logger

Logger logger = Logger.getLogger(getClass())
BasicConfigurator.configure()
logger.info("hello world")
pom.xml excerpt:
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>2.1.3</version>
    </dependency>

Any variation on the above that I've tried always results in

Exception in thread "main" groovy.lang.GroovyRuntimeException: Failed to create Script instance for class: class Standalone. Reason: java.lang.ClassCastException: Standalone cannot be cast to groovy.lang.GroovyObject
    at org.codehaus.groovy.runtime.InvokerHelper.createScript(InvokerHelper.java:443)
    at groovy.util.GroovyScriptEngine.createScript(GroovyScriptEngine.java:564)
    at groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:551)
    at groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:537)

I've tracked this back to groovy.util.GroovyScriptEngine#loadScriptByName where the script is parsed into a Class<T>, where T is the name of the script itself.

My theory is that this is caused by binary incompatibility between the groovy runtime running in the calling class and the groovy runtime running in a standalone class loader, due to the way groovy creates synthetic classes out of scripts through reflection.

Any ideas about how this can be accomplished?


Solution

  • try to create GroovyScriptEngine not directly but through rootLoader.loadClass() and call engine.run through reflection.