How to compile and run java source code in memory -
this question has answer here:
i want treat string java file compile , run it. in other words, use java script language.
to better performance, should avoid writing .class
files disk.
this answer 1 of blogs, compile , run java source code in memory.
here 3 source code files.
memoryjavacompiler.java
package me.soulmachine.compiler; import java.io.ioexception; import java.io.printwriter; import java.io.writer; import java.lang.reflect.method; import java.util.arraylist; import java.util.list; import java.util.map; import javax.tools.*; /** * simple interface java compiler using jsr 199 compiler api. */ public class memoryjavacompiler { private javax.tools.javacompiler tool; private standardjavafilemanager stdmanager; public memoryjavacompiler() { tool = toolprovider.getsystemjavacompiler(); if (tool == null) { throw new runtimeexception("could not java compiler. please, ensure jdk used instead of jre."); } stdmanager = tool.getstandardfilemanager(null, null, null); } /** * compile single static method. */ public method compilestaticmethod(final string methodname, final string classname, final string source) throws classnotfoundexception { final map<string, byte[]> classbytes = compile(classname + ".java", source); final memoryclassloader classloader = new memoryclassloader(classbytes); final class clazz = classloader.loadclass(classname); final method[] methods = clazz.getdeclaredmethods(); (final method method : methods) { if (method.getname().equals(methodname)) { if (!method.isaccessible()) method.setaccessible(true); return method; } } throw new nosuchmethoderror(methodname); } public map<string, byte[]> compile(string filename, string source) { return compile(filename, source, new printwriter(system.err), null, null); } /** * compile given string source , return bytecodes map. * * @param filename source filename used error messages etc. * @param source java source string * @param err error writer diagnostic messages written * @param sourcepath location of additional .java source files * @param classpath location of additional .class files */ private map<string, byte[]> compile(string filename, string source, writer err, string sourcepath, string classpath) { // collect errors, warnings etc. diagnosticcollector<javafileobject> diagnostics = new diagnosticcollector<javafileobject>(); // create new memory javafilemanager memoryjavafilemanager filemanager = new memoryjavafilemanager(stdmanager); // prepare compilation unit list<javafileobject> compunits = new arraylist<javafileobject>(1); compunits.add(filemanager.makestringsource(filename, source)); return compile(compunits, filemanager, err, sourcepath, classpath); } private map<string, byte[]> compile(final list<javafileobject> compunits, final memoryjavafilemanager filemanager, writer err, string sourcepath, string classpath) { // collect errors, warnings etc. diagnosticcollector<javafileobject> diagnostics = new diagnosticcollector<javafileobject>(); // javac options list<string> options = new arraylist<string>(); options.add("-xlint:all"); // options.add("-g:none"); options.add("-deprecation"); if (sourcepath != null) { options.add("-sourcepath"); options.add(sourcepath); } if (classpath != null) { options.add("-classpath"); options.add(classpath); } // create compilation task javax.tools.javacompiler.compilationtask task = tool.gettask(err, filemanager, diagnostics, options, null, compunits); if (task.call() == false) { printwriter perr = new printwriter(err); (diagnostic diagnostic : diagnostics.getdiagnostics()) { perr.println(diagnostic); } perr.flush(); return null; } map<string, byte[]> classbytes = filemanager.getclassbytes(); try { filemanager.close(); } catch (ioexception exp) { } return classbytes; } }
memoryjavafilemanager.java
package me.soulmachine.compiler; import java.io.bytearrayoutputstream; import java.io.file; import java.io.filteroutputstream; import java.io.ioexception; import java.io.outputstream; import java.net.uri; import java.nio.charbuffer; import java.util.hashmap; import java.util.map; import javax.tools.fileobject; import javax.tools.forwardingjavafilemanager; import javax.tools.javafilemanager; import javax.tools.javafileobject; import javax.tools.javafileobject.kind; import javax.tools.simplejavafileobject; /** * javafilemanager keeps compiled .class bytes in memory. */ @suppresswarnings("unchecked") final class memoryjavafilemanager extends forwardingjavafilemanager { /** java source file extension. */ private final static string ext = ".java"; private map<string, byte[]> classbytes; public memoryjavafilemanager(javafilemanager filemanager) { super(filemanager); classbytes = new hashmap<>(); } public map<string, byte[]> getclassbytes() { return classbytes; } public void close() throws ioexception { classbytes = null; } public void flush() throws ioexception { } /** * file object used represent java source coming string. */ private static class stringinputbuffer extends simplejavafileobject { final string code; stringinputbuffer(string filename, string code) { super(touri(filename), kind.source); this.code = code; } public charbuffer getcharcontent(boolean ignoreencodingerrors) { return charbuffer.wrap(code); } } /** * file object stores java bytecode classbytes map. */ private class classoutputbuffer extends simplejavafileobject { private string name; classoutputbuffer(string name) { super(touri(name), kind.class); this.name = name; } public outputstream openoutputstream() { return new filteroutputstream(new bytearrayoutputstream()) { public void close() throws ioexception { out.close(); bytearrayoutputstream bos = (bytearrayoutputstream)out; classbytes.put(name, bos.tobytearray()); } }; } } public javafileobject getjavafileforoutput(javafilemanager.location location, string classname, kind kind, fileobject sibling) throws ioexception { if (kind == kind.class) { return new classoutputbuffer(classname); } else { return super.getjavafileforoutput(location, classname, kind, sibling); } } static javafileobject makestringsource(string filename, string code) { return new stringinputbuffer(filename, code); } static uri touri(string name) { file file = new file(name); if (file.exists()) { return file.touri(); } else { try { final stringbuilder newuri = new stringbuilder(); newuri.append("mfm:///"); newuri.append(name.replace('.', '/')); if(name.endswith(ext)) newuri.replace(newuri.length() - ext.length(), newuri.length(), ext); return uri.create(newuri.tostring()); } catch (exception exp) { return uri.create("mfm:///com/sun/script/java/java_source"); } } } }
memoryclassloader.java
package me.soulmachine.compiler; import java.io.file; import java.net.malformedurlexception; import java.net.url; import java.net.urlclassloader; import java.util.arraylist; import java.util.list; import java.util.map; import java.util.stringtokenizer; /** * classloader loads .class bytes memory. */ final class memoryclassloader extends urlclassloader { private map<string, byte[]> classbytes; public memoryclassloader(map<string, byte[]> classbytes, string classpath, classloader parent) { super(tourls(classpath), parent); this.classbytes = classbytes; } public memoryclassloader(map<string, byte[]> classbytes, string classpath) { this(classbytes, classpath, classloader.getsystemclassloader()); } public memoryclassloader(map<string, byte[]> classbytes) { this(classbytes, null, classloader.getsystemclassloader()); } public class load(string classname) throws classnotfoundexception { return loadclass(classname); } public iterable<class> loadall() throws classnotfoundexception { list<class> classes = new arraylist<class>(classbytes.size()); (string name : classbytes.keyset()) { classes.add(loadclass(name)); } return classes; } protected class findclass(string classname) throws classnotfoundexception { byte[] buf = classbytes.get(classname); if (buf != null) { // clear bytes in map -- don't need anymore classbytes.put(classname, null); return defineclass(classname, buf, 0, buf.length); } else { return super.findclass(classname); } } private static url[] tourls(string classpath) { if (classpath == null) { return new url[0]; } list<url> list = new arraylist<url>(); stringtokenizer st = new stringtokenizer(classpath, file.pathseparator); while (st.hasmoretokens()) { string token = st.nexttoken(); file file = new file(token); if (file.exists()) { try { list.add(file.touri().tourl()); } catch (malformedurlexception mue) {} } else { try { list.add(new url(token)); } catch (malformedurlexception mue) {} } } url[] res = new url[list.size()]; list.toarray(res); return res; } }
explanations:
- in order represent java source file in memory instead of disk, defined
stringinputbuffer
class inmemoryjavafilemanager.java
. - to save compiled
.class
files in memory, implemented classmemoryjavafilemanager
. main idea override functiongetjavafileforoutput()
store bytecodes map. - to load bytecodes in memory, have implement customized classloader
memoryclassloader
, reads bytecodes in map , turn them classes.
here unite test.
package me.soulmachine.compiler; import org.junit.test; import java.io.ioexception; import java.lang.reflect.invocationtargetexception; import java.lang.reflect.method; import java.util.arraylist; import static org.junit.assert.assertequals; public class memoryjavacompilertest { private final static memoryjavacompiler compiler = new memoryjavacompiler(); @test public void compilestaticmethodtest() throws classnotfoundexception, invocationtargetexception, illegalaccessexception { final string source = "public final class solution {\n" + "public static string greeting(string name) {\n" + "\treturn \"hello \" + name;\n" + "}\n}\n"; final method greeting = compiler.compilestaticmethod("greeting", "solution", source); final object result = greeting.invoke(null, "soulmachine"); assertequals("hello soulmachine", result.tostring()); } }
Comments
Post a Comment