Monday, 24 March 2008

Java 6 compiler API tutorial

One of the new features in Java 6 is compiler API. It let’s you compile any Java source code file on the fly and after compilation you can load compiled class using a class loader and start using it. Because Java compiler doesn’t know anything about classes compiled on the fly, so you can use them only using java reflections.

In the first step we need to prepare a class that is going to be compiled using Compiler API. I prepared the following simple class:

package com.blogspot.mike_java;

public class ToBeCompiled {

public void callMe() {
System.out.println("Method callMe has been called");
}

}

And saved it to the directory “d:\javafiles\src” (absolute path to the ToBeCompiled.java file is “d:\javafiles\src\com\blogspot\mike_java\ ToBeCompiled.java”) and prepared directory for compiled files “d:\javafiles\bin”. The next step is to prepare a class that compiles ToBeCompiled class, loads it and runs method callMe() on an instance of it. I prepared in my favorite IDE Eclipse following class:

package com.blogspot.mike_java;

public class CompilerAPITest {

public static void main(String[] args) {
// source code is going to be here
}
}


All following source code lines will be in the main method of the CompilerAPITest class.

At first we need to get instance of JavaCompiler and StandardJavaFileManager:

JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = jc.getStandardFileManager(null, null, null);


The next step is to get Iterable collection of files to be compiled and prepare options for compiler (you can find list of compiler options at http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html):

File javaFile = new File("d:/javafiles/src/com/blogspot/mike_java/ToBeCompiled.java");
Iterable fileObjects = sjfm.getJavaFileObjects(javaFile);
String[] options = new String[]{"-d", "d:/javafiles/bin"};


The next step is to compile Iterable collection of java files and close file manager:

jc.getTask(null, null, null, Arrays.asList(options), null, fileObjects).call();
sjfm.close();
System.out.println("Class has been successfully compiled");


After this step all compiled classes would be saved in directory “d:\javafiles\bin”. The next step is to load compiled class using class loader and run method callMe. At first we need to load compiled class:

URL[] urls = new URL[]{ new URL("file://d:/javafiles/bin/") };
URLClassLoader ucl = new URLClassLoader(urls);
Class clazz = ucl.loadClass("com.blogspot.mike_java.ToBeCompiled");
System.out.println("Class has been successfully loaded");


And get the method callMe using reflections:

Method method = clazz.getDeclaredMethod("callMe", null);


Finally we need to create new instance of the just loaded class and call method callMe on it:

Object object = clazz.newInstance();
method.invoke(object, null);


You can download source codes at http://rapidshare.com/files/102030988/compiler_api.zip. I hope that this tutorial was useful for you.

2 comments:

Anonymous said...

A very useful, precise tutorial on Compiler API.

Thank you for posting the article and the source code!

Anonymous said...

gr8 article...
thanks a lot for posting it,was very helpful...