10

Is there a way for a running Java program to compile Java source code (passed as a string)?

Class newClass = Compiler.compile ("class ABC { void xyz {etc. etc. } }");

Ideally, any classes referenced by the passed-in source code would be resolved by the program's class loader.

Does something like this exist?

durron597
  • 31,968
  • 17
  • 99
  • 158
Tony the Pony
  • 40,327
  • 71
  • 187
  • 281
  • I found this example, which exactly what I need. http://www.ibm.com/developerworks/java/library/j-jcomp/index.html – psksvp Oct 24 '12 at 23:48

5 Answers5

10

Sure. Have a look at the JavaCompiler class and the other classes in the javax.tools package.

They've been around since Java 1.6.

Here is some example code.

(As pointed out by @Sergey Tachenov in the comments, it needs JDK to be installed as the necessary tools.jar file comes with JDK but not JRE.)

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 4
    I think it is worth noting that it doesn't work with Sun's JRE only, it needs JDK to be installed as the necessary tools.jar file comes with JDK but not JRE. – Sergei Tachenov Dec 16 '10 at 17:34
  • I tried this example, I'm having a `java.lang.ClassNotFoundException: Test` on `Class> clazz = Class.forName("Test");` ! – bachr Jan 21 '14 at 10:17
  • Is the output-directory of the compilation on the class path? – aioobe Jan 21 '14 at 12:02
4

What you need is a class that extends JavaFileObject

import java.net.URI;

import javax.tools.SimpleJavaFileObject;

public class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

    public JavaSourceFromString( String name, String code) {
        super( URI.create("string:///" + name.replace('.','/') 
               + Kind.SOURCE.extension),Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

Which can be used as follows:

JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
if( jc == null) throw new Exception( "Compiler unavailable");

String code = "public class CustomProcessor { /*custom stuff*/ }";
JavaSourceFromString jsfs = new JavaSourceFromString( "CustomProcessor", code);

Iterable<? extends JavaFileObject> fileObjects = Arrays.asList( jsfs);

List<String> options = new ArrayList<String>();
options.add("-d");
options.add( compilationPath);
options.add( "-classpath");
URLClassLoader urlClassLoader =
         (URLClassLoader)Thread.currentThread().getContextClassLoader();
StringBuilder sb = new StringBuilder();
for (URL url : urlClassLoader.getURLs()) {
    sb.append(url.getFile()).append(File.pathSeparator);
}
sb.append( compilationPath);
options.add(sb.toString());

StringWriter output = new StringWriter();
boolean success = jc.getTask( output, null, null, options, null, fileObjects).call(); 
if( success) {
    logger.info( LOG_PREFIX + "Class has been successfully compiled");
} else {
    throw new Exception( "Compilation failed :" + output);
}
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
MonoThreaded
  • 11,429
  • 12
  • 71
  • 102
2

Depends on what you want to do. If you just want to run some code you could use BeanShell. It's not a java compiled class, but is very usefull to make something flexible

Plínio Pantaleão
  • 1,229
  • 8
  • 13
1

Javassist can generate and load at runtime classes and methods from Strings of source code. It is also possible to dump in the file system the generated class if you need to.

Currently there are minor limitations in the code you can pass in those strings, for example it cannot include generics, enumerations, or autoboxing and inboxing of primitives. More information here:

http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/

Sergio
  • 8,532
  • 11
  • 52
  • 94
1

You could try my essence jcf library which does this. When running in debug you can have the source written to a file so you can step into the code. Otherwise, it does everything in memory. It wraps the JavaCompiler in tools.jar

It takes a String, compiles and loads it into the current class loader and returns the Class. It handles nested/inner classes.

http://vanillajava.blogspot.com/2010/11/more-uses-for-dynamic-code-in-java.html

Note: I haven't got this working in OSGi. ;)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130