On-the-fly, in-memory java code compilation for Java 5 and Java 6

Ran Biron picture Ran Biron · Mar 5, 2009 · Viewed 11.5k times · Source

How can I compile java code from an arbitrary string (in memory) in Java 5 and Java 6, load it and run a specific method on it (predefined)?

Before you flame this, I looked over existing implementations:

  • Most rely on Java 6 Compiler API.
  • Those that don't, rely on tricks.
  • Yes, I checked out commons-jci. Either I'm too dense to understand how it works, or it just doesn't.
  • I could not find how to feed the compiler my current class path (which is quite huge).
  • On the implementation that worked (in Java 6), I could not find how to correctly load inner classes (or inner anonymous classes).
  • I'd quite like it if the entire thing was in-memory, as the thing runs on multiple environments.

I'm sure this has been solved before, but I can't find anything that looks even half-production quality on google (except jci, which, as I've said before, I haven't managed to use).

Edit:

  • I looked over JavaAssist - I need inner classes, Java 5.0 language level support and compiling with the entire classpath. Also, I'd like to create new classes on the fly. I might be mistaken, but I couldn't find how to do this with JavaAssit.
  • I'm willing to use a file-system based solution (calling javac) but I don't know how to divine the classpath, nor how to later load the files (which are not in my classpath) with a special classloader that can be recycled for multiple invocations. While I do know how to research it, I'd prefer a ready solution.

Edit2: For now, I'm content with BeanShell "evaluate". Apparently it does everything I need it to (get a string, evaluate it in the context of the 'current' classpath. It does miss some of Java 5 features, but it can use enums (not define) and compiled 'generic' (erased) classes, so it should be enough for what I want.

I don't want to mark the answer as accepted yet since I do hope for a better solution to come up.

Edit3: Accepted the beanshell suggestion - it really works wonderfully.

Answer

Yuval Adam picture Yuval Adam · Mar 5, 2009

JCI looks fine. This code snippet should be your base:

JavaCompiler compiler = new JavaCompilerFactory().createCompiler("eclipse");

MemoryResourceReader mrr = new MemoryResourceReader();
mrr.add("resource name string", yourJavaSourceString.getBytes());

MemoryResourceStore mrs = new MemoryResourceStore();

CompilationResult result = compiler.compile(sources, mrr, mrs);

// don't need the result, unless you care for errors/warnings
// the class should have been compiled to your destination dir

Any reason this should not work?


Edit: added a MemoryResourceStore to send the compiled class output to memory, like requested.

Also, setting javac settings, like classpath in your case, can be done via setCustomArguments(String[] pCustomArguments) in JavacJavaCompilerSettings class.