Thursday, May 03, 2012

Dynamic Java Compilation

In some Unit Test project, we had a need for a dynamic code generation, naturally followed by a compilation.
The goal was to assemble several Unit Tests into a single Test Suite. The natural path is to write a Test Suite class, compile it, and run it. The problem happens when you want to automate this kind of process... There is in Java 1.6 a class named javax.tools.JavaCompiler, that can help. The steps are the following ones:
  • Generate the java code, in a String (StringBuffer, byte array, CharSequence, what not)
  • Compile the code - into a class
  • Load the compiled code with the ClassLoader so it is available in the classpath
  • Run the generated class
The default behavior of the JavaCompiler is to write the compiled class on the disc, and this is not what we wanted, as the directory you run your code from might very well be a read-only directory. There is actually a way to keep the compiled class in memory, as illustrated by the attached file.
There is some frustrating restriction though. The JavaCompiler cannot be used with a custom ClassLoader. This means that a class generated by a dynamic compilation as described above cannot be used for any further dynamic compilation. For example, you generate ClassA. Then you generate ClassB, extending ClassA, generated and loaded. The compilation of ClassB throws a ClassNotFoundException about ClassA...
You cannot pass your own ClassLoader to the JavaCompiler, for reasons I will not discuss here.
That is something to keep in mind. If this issue remains, a workaround would be to generate JVM byte code, using libraries like ASM; it works very well, but this makes the code a bit more difficult to debug, and beside, Java and JVM byte code are two (very) different languages.

No comments:

Post a Comment