Java 6 Compiler API (JSR 199)
Java 6 Mustang introduces the JavaCompiler API to call javac directly from your source code. This can be used for on the fly generation of new classes. To make things fast, the File system required by javac can be implemented in memory. Here I propose an implementation which can be easily called from every application.
package ch.olsen.products.util.compile;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject.Kind;public class OnTheFlyCompiler {
private static JavaFileObject generateJavaSource(final String fileName, final String source) {
return new SimpleJavaFileObject(toURI(fileName), Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean
ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
return source;
}};
}static class RAMJavaFileObject extends SimpleJavaFileObject {
RAMJavaFileObject(String name, Kind kind) {
super(toURI(name), kind);
}ByteArrayOutputStream baos;
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}@Override
public InputStream openInputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return new ByteArrayInputStream(baos.toByteArray());
}@Override
public OutputStream openOutputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return baos = new ByteArrayOutputStream();
}}
public static interface FileSpec {
String getFileName();
String getSource();
}public static class FileSpecImpl implements FileSpec {
String fileName;
String source;
public FileSpecImpl(String fileName, String source) {
this.fileName = fileName;
this.source = source;
}
public final String getFileName() {
return fileName;
}
public final String getSource() {
return source;
}
}public static ClassLoader onTheFlyCompile(final FileSpec fileSpec) throws Exception {
return onTheFlyCompile(Arrays.asList(fileSpec));
}
@SuppressWarnings(”unchecked”)
public static ClassLoader onTheFlyCompile(final ListfileSpecs) throws Exception { if ( System.getSecurityManager() != null ) {
java.util.PropertyPermission perm = new java.util.PropertyPermission(”java.endorsed.dirs”, “read”);
System.getSecurityManager().checkPermission(perm);
}final Map
output =
new HashMap(); final JavaCompiler compiler =
ToolProvider.getSystemJavaCompiler();final DiagnosticCollector
diagnostics =
new DiagnosticCollector(); final JavaFileManager jfm = new
ForwardingJavaFileManager(
compiler.getStandardFileManager(diagnostics, Locale.getDefault(), Charset.defaultCharset())) {@Override
public JavaFileObject getJavaFileForOutput(Location location,
String name,
Kind kind,
FileObject sibling) throws IOException {
JavaFileObject jfo = new RAMJavaFileObject(name, kind);
output.put(name, jfo);
return jfo;
}};
final List
files = new ArrayList ();
for ( FileSpec fs : fileSpecs )
files.add(generateJavaSource(fs.getFileName(), fs.getSource()));String ops[] = buildClassPath(OnTheFlyCompiler.class.getClassLoader());
System.out.println(”On the fly compiler: calling with opstions: “+ ops[0]+ ” ” + ops[1]);CompilationTask task = compiler.getTask(
null, jfm, diagnostics, Arrays.asList(ops), null,
files);
if (! task.call()) {
for(Diagnostic dm : diagnostics.getDiagnostics())
System.err.println(dm);
throw new RuntimeException(”Could not compile”);
}System.out.println(”On the fly compiler: generated classes: “+ output.keySet());
ClassLoader cl = new OnTheFlyClassLoader(output);
String cp[] = buildClassPath(cl);
System.out.println(”On the fly compiler: generated class loader with classpath: “+cp[1]);return cl;
/*Class> c = Class.forName(”just.generated.Hello”, false, cl);
c.getMethod(”main”, String[].class)
.invoke(null, new Object[] {args});*/}
static class OnTheFlyClassLoader extends ClassLoader {
final Mapoutput;
public OnTheFlyClassLoader(final Mapoutput) {
super(OnTheFlyCompiler.class.getClassLoader());
this.output = output;
}
@Override
protected Class> findClass(String name) throws
ClassNotFoundException {
JavaFileObject jfo = output.get(name);
if (jfo != null) {
byte[] bytes = ((RAMJavaFileObject)
jfo).baos.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
}
//return super.findClass(name);
throw new ClassNotFoundException();
}
}private static String[] buildClassPath(ClassLoader cl) {
Listclasspath = new ArrayList ();
if ( cl instanceof URLClassLoader ) {
URLClassLoader cl2 = (URLClassLoader)cl;
for (URL jar : cl2.getURLs()) {
classpath.add(jar);
}
}
String classPath = System.getProperty(”java.class.path”);
for (String path : classPath.split(File.pathSeparator)) {
try {
classpath.add(new URL(”file:”+path));
} catch (MalformedURLException e) {
System.err.println(”Wrong url: “+e.getMessage());
e.printStackTrace();
}
}StringBuffer sb = new StringBuffer();
for (URL jar : classpath) {
sb.append(jar.getPath());
sb.append(File.pathSeparatorChar);
}
String ops[] = new String[] {”-cp” , sb.toString() };
return ops;
}
private static URI toURI(String name) {
try {
return new URI(name);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}@Deprecated
/**
* just as an example. do not use it
*/
static JavaFileObject generateJavaSourceCode() {final String source =
“package just.generated;\n” +
“public class Hello {\n” +
“public static void main(String… args) {\n” +
“System.out.println(new Object() {\n” +
“public String toString() {\n” +
“return \”just hello!\”;\n” +
“}\n” +
“});\n” +
“}\n” +
“}”;
return generateJavaSource(”Hello.java”, source);
}}
To use it you just have to call something like this:
public static void test1() throws Exception {
ClassLoader cl = OnTheFlyCompiler.onTheFlyCompile(
new OnTheFlyCompiler.FileSpecImpl(”Test1.java”,
“package ch.olsen.test; ” +
“public class Test1 implements ch.olsen.test.TestCompiler.Interface1 { ” +
“ public String say() {” +
“ return \”hello compiled on the fly\”; ” +
“ }” +
“}”
));
Class clazz = cl.loadClass(”ch.olsen.test.Test1″);
Interface1 i = (Interface1)clazz.newInstance();
System.out.println(i.say());
}public interface Interface1 {
String say();
}
Hope it is helpful!
posted in Uncategorized | 0 Comments