Compile Groovy class at runtime in Java

I can successfully compile Groovy in Java at runtime and save it to the database and pull it out. I cannot compile a Groovy class if it has inner classes or inner enumeration. Has anyone successfully compiled Groovy code like this and included inner classes / enums and was able to get the script out of the class?

For example, I want to download the following “Test” script containing inner classes and run the script at runtime.

Compiler code:

public byte[] compileGroovyScript(final String className, final String script) { byte[] compiledScriptBytes = null; CompilationUnit compileUnit = new CompilationUnit(); compileUnit.addSource(className, script); compileUnit.compile(Phases.CLASS_GENERATION); for (Object compileClass : compileUnit.getClasses()) { GroovyClass groovyClass = (GroovyClass) compileClass; compiledScriptBytes = groovyClass.getBytes(); } return compiledScriptBytes; } 

Code to pull script out:

 public Class getGroovyScript(final String className, final byte[] script) { Class clazz = null; try (GroovyClassLoader classLoader = new GroovyClassLoader(this.getClass().getClassLoader())) { clazz = classLoader.defineClass(className, script); } catch (IOException e) { } catch (Exception e) { } return clazz; } 

Code to run the script:

 Class groovyClass = app.getGroovyScript(className, compiledScript); TestScript script = (TestScript) groovyClass.newInstance(); System.out.println(script.getMessage()); 

Groovy script:

 import com.groovy.groovy.TestScript class Test implements TestScript { String getMessage() { [1..10].each(){ println it } return "Jello" } } 
+7
java groovy runtime-compilation
source share
5 answers

It’s not clear from the description why you are compiling yourself. If you can just let Groovy do this for you, then all of this can simply be simplified like this:

 String script = // string containing the script you want to parse GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); Class theParsedClass = groovyClassLoader.parseClass(script); 
+5
source share

Well, it may be a little late, but hopefully this will help the next person. I think you need to save a list for each groovy class, then cl.defineClass and finally cl.loadClass. I think groovy sometimes compiles to the list of classes mainly as shown below, when I add source (), add one class and then iterate over all the generated classes from this single file.

This is the code I'm running at the moment (although I have not tried saving and reloading later)

  GroovyClassLoader cl = new GroovyClassLoader(); CompilationUnit compileUnit = new CompilationUnit(); compileUnit.addSource(scriptCode.getClassName(), scriptCode.getScriptSourceCode()); compileUnit.compile(Phases.CLASS_GENERATION); compileUnit.setClassLoader(cl); GroovyClass target = null; for (Object compileClass : compileUnit.getClasses()) { GroovyClass groovyClass = (GroovyClass) compileClass; cl.defineClass(groovyClass.getName(), groovyClass.getBytes()); if(groovyClass.getName().equals(scriptCode.getClassName())) { target = groovyClass; } } if(target == null) throw new IllegalStateException("Could not find proper class"); return cl.loadClass(target.getName()); 

pay attention to the cl.defineClass call, which puts the class in the class loader, so when it is looked up (enumeration or inner class), it will be there.

and now I think that you don’t need to create your own classloader (although you avoid the useless defineClass until you need it with your own classloader, which can be useful and more efficient).

+3
source share

Here, any error handling is required to simplify this procedure, but this is probably what you want:

 public byte[] compileGroovyScript(final String className, final String script) { byte[] compiledScriptBytes = null; CompilationUnit compileUnit = new CompilationUnit(); compileUnit.addSource(className, script); compileUnit.compile(Phases.CLASS_GENERATION); List classes = compileUnit.getClasses(); GroovyClass firstClass = (GroovyClass)classes.get(0); compiledScriptBytes = firstClass.getBytes(); return compiledScriptBytes; } 
0
source share

Depending on your requirements, you can provide access to inner classes, and you can do this with something like this, which will find a class with a matching name instead of accepting the first class:

 public byte[] compileGroovyScript(final String className, final String script) { byte[] compiledScriptBytes = null; CompilationUnit compileUnit = new CompilationUnit(); compileUnit.addSource(className, script); compileUnit.compile(Phases.CLASS_GENERATION); for (Object compileClass : compileUnit.getClasses()) { GroovyClass groovyClass = (GroovyClass) compileClass; if(className.equals(groovyClass.getName())) { compiledScriptBytes = groovyClass.getBytes(); break; } } return compiledScriptBytes; } 
0
source share

I am in it myself, but just by executing the java compiler on demand at runtime, I believe that you are facing the same problem that I solved in this code

https://github.com/deanhiller/webpieces/tree/master/runtimecompile/src/main/java/org/webpieces/compiler/api

webpieces / runtimecompile is a reusable java compiler on demand using the eclipse compiler.

Now, for groovy, I think you are working in this case

 1. you compile ONE script 2. this results in 'multiple' class file objects (I think) just like mine did 3. This is where you need to store EACH in the database SEPARATELY 4. Then you need a classloader that tries to lookup the 'inner classes' when jvm asks for it 5. finally you do a yourclassLoader.loadApplicationClass (much like the one in CompileOnDemandImpl.java in the project above 6. To be clear, step 5 causes step 4 to happen behind the scenes (and that is what is confusing). 

If you run the AnonymousByteCacheTest test case, it pretty much does something similar.

you don’t need to install ANYTHING to run the build on this project, just clone it and the “./gradlew test” and transfer both “./gradlew eclipse” or “./gradlew idea” and it generates IDE files so you can go through him.

It is very similar. I am trying to get a version of groovy that works further on its own.

0
source share

All Articles