Compile o código totalmente na memory com javax.tools.JavaCompiler

Eu estou usando o JavaCompiler do pacote javax.tools (JDK 1.7) para compilar algumas coisas rapidamente, assim:

compiler.run(null, null, "-cp", paths, "path/to/my/file.java"); 

Ele funciona, mas eu gostaria de fazer tudo na memory (por exemplo, passar uma string com o código, não o arquivo de origem, e obter o código de byte de volta, não um arquivo .class). Descobri que não é útil estender os parâmetros InputStream e OutputStream pois é provavelmente o mesmo que no console. Você conhece uma maneira de fazer o método de execução funcionar assim? Ou você conhece uma maneira confirmada de fazer isso com o método getTask() ? (estender o FileManager parece fácil, mas não é tão fácil 🙂

    Eu corri o código acima no Mac OS Java 7. Nenhum deles funciona. Então eu escrevi um https://github.com/trung/InMemoryJavaCompiler

     StringBuffer sourceCode = new StringBuffer(); sourceCode.append("package org.mdkt;\n"); sourceCode.append("public class HelloClass {\n"); sourceCode.append(" public String hello() { return \"hello\"; }"); sourceCode.append("}"); Class< ?> helloClass = InMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString()); 

    Eu acho que isso aqui pode ser de ajuda, basicamente, mostra como compilar a fonte Java da memory (a string está localizada na class).

    Ele usa o PrinterWriter e o StringWriter para gravar o código-fonte em uma memory String / in e, em seguida, usa a class JavaCompiler (desde o JDK 6) para compilar e executar o programa:

     import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.ToolProvider; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; public class CompileSourceInMemory { public static void main(String args[]) throws IOException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnostics = new DiagnosticCollector(); StringWriter writer = new StringWriter(); PrintWriter out = new PrintWriter(writer); out.println("public class HelloWorld {"); out.println(" public static void main(String args[]) {"); out.println(" System.out.println(\"This is in another java file\");"); out.println(" }"); out.println("}"); out.close(); JavaFileObject file = new JavaSourceFromString("HelloWorld", writer.toString()); Iterable< ? extends JavaFileObject> compilationUnits = Arrays.asList(file); CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits); boolean success = task.call(); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { System.out.println(diagnostic.getCode()); System.out.println(diagnostic.getKind()); System.out.println(diagnostic.getPosition()); System.out.println(diagnostic.getStartPosition()); System.out.println(diagnostic.getEndPosition()); System.out.println(diagnostic.getSource()); System.out.println(diagnostic.getMessage(null)); } System.out.println("Success: " + success); if (success) { try { URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("").toURI().toURL() }); Class.forName("HelloWorld", true, classLoader).getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null }); } catch (ClassNotFoundException e) { System.err.println("Class not found: " + e); } catch (NoSuchMethodException e) { System.err.println("No such method: " + e); } catch (IllegalAccessException e) { System.err.println("Illegal access: " + e); } catch (InvocationTargetException e) { System.err.println("Invocation target: " + e); } } } } class JavaSourceFromString extends SimpleJavaFileObject { final String code; 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; } } 

    Se você der uma olhada no link de referência, você encontrará mais alguns exemplos também

    Referência:

    • Compilando da memory

    Esta é uma class que compila inteiramente na memory.

    Eu tomei (quase) a totalidade disso em http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html por Rekha Kumari (junho de 2011). Embora esta versão seja mais de 100 linhas mais curta e tenha mais resources (mas não docs: P).

    EDIT: Desde que eu comecei apenas 2 votos, eu quero dar de volta e editar minha nova versão em: Ele compila várias classs de uma vez, a única maneira de compilar as classs que dependem uns dos outros. Se você quer saber sobre a class “CompilerFeedback”: Estou fazendo atualmente um pequeno IDE Java para codificação de jogos onde eu precisava. Eu estou incluindo aqui porque eu suponho que você quer fazer algo com isso, e a predigestão pode te ajudar com isso. EDIT: E percebo que parte do código na class CompilerFeedback é uma porcaria completa. Eu comecei com o projeto muito recentemente, mas essa class eu reciclava de uma tentativa de anos.

    EDIT: Olá, outro upvote! Adicionando um método que deriva o nome completo da class a partir do código-fonte de uma class (incl. Nome do pacote, se for fornecido). Muito útil para chamar o compilador, que precisa dessa informação.

    EDIT: adicionado um método MISSING ao método que determina o nome completo da class. (desculpa)

    CLASSE DE DEMONSTRAÇÃO:

     import java.util.ArrayList; import java.util.List; public class Demo { public static void main(final String[] args) { final InMemoryCompiler.IMCSourceCode cls1source; final InMemoryCompiler.IMCSourceCode cls2source; final StringBuilder sb = new StringBuilder(); sb.append("package toast;\n"); sb.append("public class DynaClass {\n"); sb.append(" public static void main(final String[] args) {"); sb.append(" System.out.println(\"Based massively on the work of Rekha Kumari, http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html\");\n"); sb.append(" System.out.println(\"This is the main method speaking.\");\n"); sb.append(" System.out.println(\"Args: \" + java.util.Arrays.toString(args));\n"); sb.append(" final Test test = new Test();\n"); sb.append(" }\n"); sb.append(" public String toString() {\n"); sb.append(" return \"Hello, I am \" + "); sb.append("this.getClass().getSimpleName();\n"); sb.append(" }\n"); sb.append("}\n"); cls1source = new InMemoryCompiler.IMCSourceCode("toast.DynaClass", sb.toString()); sb.setLength(0); sb.append("package toast;\n"); sb.append("public class Test {\n"); sb.append(" public Test() {\n"); sb.append(" System.out.println(\"class Test constructor reporting in.\");\n"); sb.append(" System.out.println(new DynaClass());\n"); sb.append(" }\n"); sb.append("}\n"); cls2source = new InMemoryCompiler.IMCSourceCode("toast.Test", sb.toString()); final List classSources = new ArrayList<>(); classSources.add(cls1source); classSources.add(cls2source); final InMemoryCompiler uCompiler = new InMemoryCompiler(classSources); final CompilerFeedback compilerFeedback = uCompiler.compile(); System.out.println("\n\nCOMPILER FEEDBACK: " + compilerFeedback); if (compilerFeedback != null && compilerFeedback.success) { try { System.out.println("\nTOSTRING DEMO:"); uCompiler.runToString(cls1source.fullClassName); } catch (Exception e) { e.printStackTrace(); } try { System.out.println("\nMAIN DEMO:"); uCompiler.runMain(cls1source.fullClassName, new String[] { "test1", "test2" }); } catch (Exception e) { e.printStackTrace(); } } System.exit(0); } } 

    CLASSE DE COMPILAÇÃO:

     import javax.tools.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.security.SecureClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * MASSIVELY based on http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html by Rekha Kumari * (June 2011) */ final public class InMemoryCompiler { final public static class IMCSourceCode { final public String fullClassName; final public String sourceCode; /** * @param fullClassName Full name of the class that will be compiled. If the class should be in some package, * fullName should contain it too, for example: "testpackage.DynaClass" * @param sourceCode the source code */ public IMCSourceCode(final String fullClassName, final String sourceCode) { this.fullClassName = fullClassName; this.sourceCode = sourceCode; } } final public boolean valid; final private List classSourceCodes; final private JavaFileManager fileManager; public InMemoryCompiler(final List classSourceCodes) { this.classSourceCodes = classSourceCodes; final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); if (compiler == null) { fileManager = null; valid = false; System.err.println("ToolProvider.getSystemJavaCompiler() returned null! This program needs to be run on a system with an installed JDK."); return; } valid = true; fileManager = new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) { final private Map byteStreams = new HashMap<>(); @Override public ClassLoader getClassLoader(final Location location) { return new SecureClassLoader() { @Override protected Class< ?> findClass(final String className) throws ClassNotFoundException { final ByteArrayOutputStream bos = byteStreams.get(className); if (bos == null) { return null; } final byte[] b = bos.toByteArray(); return super.defineClass(className, b, 0, b.length); } }; } @Override public JavaFileObject getJavaFileForOutput(final Location location, final String className, final JavaFileObject.Kind kind, final FileObject sibling) throws IOException { return new SimpleJavaFileObject(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind) { @Override public OutputStream openOutputStream() throws IOException { ByteArrayOutputStream bos = byteStreams.get(className); if (bos == null) { bos = new ByteArrayOutputStream(); byteStreams.put(className, bos); } return bos; } }; } }; } public CompilerFeedback compile() { if (!valid) { return null; } final List files = new ArrayList<>(); for (IMCSourceCode classSourceCode : classSourceCodes) { URI uri = null; try { uri = URI.create("string:///" + classSourceCode.fullClassName.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension); } catch (Exception e) { // e.printStackTrace(); } if (uri != null) { final SimpleJavaFileObject sjfo = new SimpleJavaFileObject(uri, JavaFileObject.Kind.SOURCE) { @Override public CharSequence getCharContent(final boolean ignoreEncodingErrors) { return classSourceCode.sourceCode; } }; files.add(sjfo); } } final DiagnosticCollector diagnostics = new DiagnosticCollector<>(); final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); if (files.size() > 0) { final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, files); return new CompilerFeedback(task.call(), diagnostics); } else { return null; } } public void runToString(final String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException { if (!valid) { return; } final Class< ?> theClass = getCompiledClass(className); final Object instance = theClass.newInstance(); System.out.println(instance); } public void runMain(final String className, final String[] args) throws IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException { if (!valid) { return; } final Class< ?> theClass = getCompiledClass(className); final Method mainMethod = theClass.getDeclaredMethod("main", String[].class); mainMethod.invoke(null, new Object[] { args }); } public Class< ?> getCompiledClass(final String className) throws ClassNotFoundException { if (!valid) { throw new IllegalStateException("InMemoryCompiler instance not usable because ToolProvider.getSystemJavaCompiler() returned null: No JDK installed."); } final ClassLoader classLoader = fileManager.getClassLoader(null); final Class< ?> ret = classLoader.loadClass(className); if (ret == null) { throw new ClassNotFoundException("Class returned by ClassLoader was null!"); } return ret; } } 

    CLASSE COMPILERFEEDBACK:

     import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; final public class CompilerFeedback { final public boolean success; final public List messages = new ArrayList<>(); public CompilerFeedback(final Boolean success, final DiagnosticCollector diagnostics) { this.success = success != null && success; for (Diagnostic< ? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { messages.add(new CompilerMessage(diagnostic)); } } public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("SUCCESS: ").append(success).append('\n'); final int iTop = messages.size(); for (int i = 0; i < iTop; i++) { sb.append("\n[MESSAGE ").append(i + 1).append(" OF ").append(iTop).append("]\n\n"); // sb.append(messages.get(i).toString()).append("\n"); // sb.append(messages.get(i).toStringForList()).append("\n"); sb.append(messages.get(i).toStringForDebugging()).append("\n"); } return sb.toString(); } final public static class CompilerMessage { final public Diagnostic compilerInfo; final public String typeOfProblem; final public String typeOfProblem_forDebugging; final public String multiLineMessage; final public int lineNumber; final public int columnNumber; final public int textHighlightPos_lineStart; final public int textHighlightPos_problemStart; final public int textHighlightPos_problemEnd; final public String sourceCode; final public String codeOfConcern; final public String codeOfConcernLong; CompilerMessage(final Diagnostic< ? extends JavaFileObject> diagnostic) { final JavaFileObject sourceFileObject = diagnostic.getSource(); String sourceCodePreliminary = null; if (sourceFileObject instanceof SimpleJavaFileObject) { final SimpleJavaFileObject simpleSourceFileObject = (SimpleJavaFileObject) sourceFileObject; try { final CharSequence charSequence = simpleSourceFileObject.getCharContent(false); sourceCodePreliminary = charSequence.toString(); } catch (IOException e) { e.printStackTrace(); } } if (sourceCodePreliminary == null) { sourceCode = "[SOURCE CODE UNAVAILABLE]"; } else { sourceCode = sourceCodePreliminary; } compilerInfo = diagnostic; typeOfProblem = diagnostic.getKind().name(); typeOfProblem_forDebugging = "toString() = " + diagnostic.getKind().toString() + "; name() = " + typeOfProblem; lineNumber = (int) compilerInfo.getLineNumber(); columnNumber = (int) compilerInfo.getColumnNumber(); final int sourceLen = sourceCode.length(); textHighlightPos_lineStart = (int) Math.min(Math.max(0, diagnostic.getStartPosition()), sourceLen); textHighlightPos_problemStart = (int) Math.min(Math.max(0, diagnostic.getPosition()), sourceLen); textHighlightPos_problemEnd = (int) Math.min(Math.max(0, diagnostic.getEndPosition()), sourceLen); final StringBuilder reformattedMessage = new StringBuilder(); final String message = diagnostic.getMessage(Locale.US); final int messageCutOffPosition = message.indexOf("location:"); final String[] messageParts; if (messageCutOffPosition >= 0) { messageParts = message.substring(0, messageCutOffPosition).split("\n"); } else { messageParts = message.split("\n"); } for (String s : messageParts) { String s2 = s.trim(); if (s2.length() > 0) { boolean lengthChanged; do { final int lBeforeReplace = s2.length(); s2 = s2.replace(" ", " "); lengthChanged = (s2.length() != lBeforeReplace); } while (lengthChanged); reformattedMessage.append(s2).append("\n"); } } codeOfConcern = sourceCode.substring(textHighlightPos_problemStart, textHighlightPos_problemEnd); codeOfConcernLong = sourceCode.substring(textHighlightPos_lineStart, textHighlightPos_problemEnd); if (!codeOfConcern.isEmpty()) { reformattedMessage.append("Code of concern: \"").append(codeOfConcern).append('\"'); } multiLineMessage = reformattedMessage.toString(); } public String toStringForList() { if (compilerInfo == null) { return "No compiler!"; } else { return compilerInfo.getCode(); } } public String toStringForDebugging() { final StringBuilder ret = new StringBuilder(); ret.append("Type of problem: ").append(typeOfProblem_forDebugging).append("\n\n"); ret.append("Message:\n").append(multiLineMessage).append("\n\n"); ret.append(compilerInfo.getCode()).append("\n\n"); ret.append("line number: ").append(lineNumber).append("\n"); ret.append("column number: ").append(columnNumber).append("\n"); ret.append("textHighlightPos_lineStart: ").append(textHighlightPos_lineStart).append("\n"); ret.append("textHighlightPos_problemStart: ").append(textHighlightPos_problemStart).append("\n"); ret.append("textHighlightPos_problemEnd: ").append(textHighlightPos_problemEnd).append("\n"); return ret.toString(); } @Override public String toString() { // return compilerInfo.getMessage(Locale.US); return typeOfProblem + ": " + multiLineMessage + "\n"; } } } 

    MÉTODO DE UTILIDADE (Não é necessário para as três classs mais acima):

     final public static String PREFIX_CLASSNAME = "class "; final public static String PREFIX_PACKAGENAME = "package "; final public static String CHARSET_JAVAKEYWORDENDERS = " \n[](){}<>;,\"\\/*+-=%!&?@:"; /** * @return eg "com.dreamspacepresident.TestClass" if the source's first root level "class" (I'm talking about {} * hierarchy.) is named "TestClass", and if the "package" name is "com.dreamspacepresident". Null is returned if * sourceCode is null or does not provide a class name. (Mind that the parsing is done in a quite crappy way.) */ public static String deriveFullClassNameFromSource(final String sourceCode) { if (sourceCode == null) { return null; } final int firstBr = sourceCode.indexOf('{'); if (firstBr >= 0) { // DETERMINE CLASS NAME final int firstClass = sourceCode.indexOf(PREFIX_CLASSNAME); if (firstClass < firstBr) { String className = sourceCode.substring(firstClass + PREFIX_CLASSNAME.length(), firstBr).trim(); final int classNameEnd = indexOfAnyOfThese(className, CHARSET_JAVAKEYWORDENDERS); if (classNameEnd >= 0) { className = className.substring(0, classNameEnd); } if (!className.isEmpty()) { // DETERMINE PACKAGE NAME String packageName = null; final int firstPackage = sourceCode.indexOf(PREFIX_PACKAGENAME); if (firstPackage >= 0 && firstPackage < firstBr && firstPackage < firstClass) { packageName = sourceCode.substring(firstPackage + PREFIX_PACKAGENAME.length(), firstBr).trim(); final int packageNameEnd = indexOfAnyOfThese(packageName, CHARSET_JAVAKEYWORDENDERS); if (packageNameEnd >= 0) { packageName = packageName.substring(0, packageNameEnd); } } return (packageName != null && !packageName.isEmpty() ? packageName + "." : "") + className; } } } return null; } /** * Looks for the first occurrence of ANY of the given characters, which is easier than using a bunch of * String.indexOf() calls. * * @return -1 if not found, otherwise the String index of the first hit */ public static int indexOfAnyOfThese(final String text, final String characters) { if (text != null && !text.isEmpty() && characters != null && !characters.isEmpty()) { final int lenT = text.length(); final int lenC = characters.length(); for (int i = 0; i < lenT; i++) { final char c = text.charAt(i); for (int ii = 0; ii < lenC; ii++) { if (c == characters.charAt(ii)) { return i; } } } } return -1; } 

    Eu escrevi uma biblioteca para fazer isso há alguns anos atrás. É preciso uma String que possa conter classs aninhadas, compila-as e, opcionalmente, carrega-as no carregador de classs atual (para que você não precise de um carregador de classs adicional) Se a JVM estiver em execução no modo de debugging, gravará o código gerado em um arquivo para que você possa percorrer seu código gerado.

    http://vanillajava.blogspot.co.uk/2010_11_01_archive.html


    Parafraseando o exemplo de erolagnab você pode fazer

     StringBuilder sourceCode = new StringBuilder(); sourceCode.append("package org.mdkt;\n") .append("public class HelloClass {\n") .append(" public String hello() { return \"hello\"; }") .append("}"); Class< ?> helloClass = CACHED_COMPILER.compile("org.mdkt.HelloClass", sourceCode.toString()); 

    Atualização, a fonte está disponível aqui https://github.com/OpenHFT/Java-Runtime-Compiler

    E você pode obter a última compilation via maven http://search.maven.org/#browse%7C842970587


    Um exemplo mais longo.

     // this writes the file to disk only when debugging is enabled. CachedCompiler cc = CompilerUtils.DEBUGGING ? new CachedCompiler(new File(parent, "src/test/java"), new File(parent, "target/compiled")) : CompilerUtils.CACHED_COMPILER; String text = "generated test " + new Date(); Class fooBarTeeClass = cc.loadFromJava("eg.FooBarTee", "package eg;\n" + '\n' + "import eg.components.BarImpl;\n" + "import eg.components.TeeImpl;\n" + "import eg.components.Foo;\n" + '\n' + "public class FooBarTee{\n" + " public final String name;\n" + " public final TeeImpl tee;\n" + " public final BarImpl bar;\n" + " public final BarImpl copy;\n" + " public final Foo foo;\n" + '\n' + " public FooBarTee(String name) {\n" + " // when viewing this file, ensure it is synchronised with the copy on disk.\n" + " System.out.println(\"" + text + "\");\n" + " this.name = name;\n" + '\n' + " tee = new TeeImpl(\"test\");\n" + '\n' + " bar = new BarImpl(tee, 55);\n" + '\n' + " copy = new BarImpl(tee, 555);\n" + '\n' + " // you should see the current date here after synchronisation.\n" + " foo = new Foo(bar, copy, \"" + text + "\", 5);\n" + " }\n" + '\n' + " public void start() {\n" + " }\n" + '\n' + " public void stop() {\n" + " }\n" + '\n' + " public void close() {\n" + " stop();\n" + '\n' + " }\n" + "}\n"); // add a debug break point here and step into this method. FooBarTee fooBarTee = new FooBarTee("test foo bar tee"); Foo foo = fooBarTee.foo; assertNotNull(foo); assertEquals(text, foo.s); 

    Eu queria:

    • compilation em memory de um arquivo java (útil para Java como uma linguagem de script)
    • Nenhuma dependência adicional (fácil de configurar)
    • Implementação em um número tão baixo de arquivos quanto possível (fácil de incorporar em um projeto)

    Você pode tentar primeiro aqui: http://ideone.com/cu1GhE#view_edit_box

    O código a seguir é baseado no código Rekha Kumari :

    Main.java

     package com.mycompany.java; //import org.slf4j.Logger; //import org.slf4j.LoggerFactory; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Main { //private static final Logger logger = LoggerFactory.getLogger(Main.class); public static void main(String[] args) { try { StringWriter writer = new StringWriter(); PrintWriter out = new PrintWriter(writer); out.println("package com.mycompany.script;"); out.println(""); out.println("public class HelloWorld {"); out.println(" public static void main(String args[]) {"); out.println(" System.out.println(\"This is in another java file\");"); out.println(" }"); out.println("}"); out.close(); String fullName = "com.mycompany.script.HelloWorld"; String src = writer.toString(); DynamicCompiler uCompiler = new DynamicCompiler(fullName, src); uCompiler.compile(); uCompiler.run(); } catch (Exception e) { //logger.error("Exception:", e); System.out.print("Exception"); } } } 

    DynamicCompiler.java

     package com.mycompany.java; //import org.slf4j.Logger; //import org.slf4j.LoggerFactory; import javax.tools.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.security.SecureClassLoader; import java.util.ArrayList; import java.util.List; // Based on: http://javapracs.blogspot.cz/2011/06/dynamic-in-memory-compilation-using.html public class DynamicCompiler { //private static final Logger logger = LoggerFactory.getLogger(DynamicCompiler.class); private JavaFileManager fileManager; private String fullName; private String sourceCode; public DynamicCompiler(String fullName, String srcCode) { this.fullName = fullName; this.sourceCode = srcCode; this.fileManager = initFileManager(); } public JavaFileManager initFileManager() { if (fileManager != null) return fileManager; else { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); fileManager = new ClassFileManager(compiler .getStandardFileManager(null, null, null)); return fileManager; } } public void compile() { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); List files = new ArrayList<>(); files.add(new CharSequenceJavaFileObject(fullName, sourceCode)); compiler.getTask( null, fileManager, null, null, null, files ).call(); } public void run() throws InstantiationException, IllegalAccessException, ClassNotFoundException { try { fileManager .getClassLoader(null) .loadClass(fullName) .getDeclaredMethod("main", new Class[]{String[].class}) .invoke(null, new Object[]{null}); } catch (InvocationTargetException e) { System.out.print("InvocationTargetException"); //logger.error("InvocationTargetException:", e); } catch (NoSuchMethodException e) { System.out.print("NoSuchMethodException "); //logger.error("NoSuchMethodException:", e); } } public class CharSequenceJavaFileObject extends SimpleJavaFileObject { /** * CharSequence representing the source code to be compiled */ private CharSequence content; public CharSequenceJavaFileObject(String className, CharSequence content) { super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.content = content; } public CharSequence getCharContent(boolean ignoreEncodingErrors) { return content; } } public class ClassFileManager extends ForwardingJavaFileManager { private JavaClassObject javaClassObject; public ClassFileManager(StandardJavaFileManager standardManager) { super(standardManager); } @Override public ClassLoader getClassLoader(Location location) { return new SecureClassLoader() { @Override protected Class< ?> findClass(String name) throws ClassNotFoundException { byte[] b = javaClassObject.getBytes(); return super.defineClass(name, javaClassObject.getBytes(), 0, b.length); } }; } public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { this.javaClassObject = new JavaClassObject(className, kind); return this.javaClassObject; } } public class JavaClassObject extends SimpleJavaFileObject { protected final ByteArrayOutputStream bos = new ByteArrayOutputStream(); public JavaClassObject(String name, Kind kind) { super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind); } public byte[] getBytes() { return bos.toByteArray(); } @Override public OutputStream openOutputStream() throws IOException { return bos; } } } 

    Eu gostaria de apresentar minha solução que funciona bem em produção.

    Aqui estão os três arquivos de código fonte.

    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 to 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 get Java compiler. Please, ensure that JDK is used instead of JRE."); } stdManager = tool.getStandardFileManager(null, null, null); } /** * Compile a single static method. */ public Method compileStaticMethod(final String methodName, final String className, final String source) throws ClassNotFoundException { final Map classBytes = compile(className + ".java", source); final MemoryClassLoader classLoader = new MemoryClassLoader(classBytes); final Class clazz = classLoader.loadClass(className); final Method[] methods = clazz.getDeclaredMethods(); for (final Method method : methods) { if (method.getName().equals(methodName)) { if (!method.isAccessible()) method.setAccessible(true); return method; } } throw new NoSuchMethodError(methodName); } public Map compile(String fileName, String source) { return compile(fileName, source, new PrintWriter(System.err), null, null); } /** * compile given String source and return bytecodes as a Map. * * @param fileName source fileName to be used for error messages etc. * @param source Java source as String * @param err error writer where diagnostic messages are written * @param sourcePath location of additional .java source files * @param classPath location of additional .class files */ private Map compile(String fileName, String source, Writer err, String sourcePath, String classPath) { // to collect errors, warnings etc. DiagnosticCollector diagnostics = new DiagnosticCollector(); // create a new memory JavaFileManager MemoryJavaFileManager fileManager = new MemoryJavaFileManager(stdManager); // prepare the compilation unit List compUnits = new ArrayList(1); compUnits.add(fileManager.makeStringSource(fileName, source)); return compile(compUnits, fileManager, err, sourcePath, classPath); } private Map compile(final List compUnits, final MemoryJavaFileManager fileManager, Writer err, String sourcePath, String classPath) { // to collect errors, warnings etc. DiagnosticCollector diagnostics = new DiagnosticCollector(); // javac options List options = new ArrayList(); 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 a compilation task javax.tools.JavaCompiler.CompilationTask task = tool.getTask(err, fileManager, diagnostics, options, null, compUnits); if (task.call() == false) { PrintWriter perr = new PrintWriter(err); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { perr.println(diagnostic); } perr.flush(); return null; } Map 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 that 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 classBytes; public MemoryJavaFileManager(JavaFileManager fileManager) { super(fileManager); classBytes = new HashMap<>(); } public Map getClassBytes() { return classBytes; } public void close() throws IOException { classBytes = null; } public void flush() throws IOException { } /** * A file object used to represent Java source coming from a 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); } } /** * A file object that stores Java bytecode into the 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 that loads .class bytes from memory. */ final class MemoryClassLoader extends URLClassLoader { private Map classBytes; public MemoryClassLoader(Map classBytes, String classPath, ClassLoader parent) { super(toURLs(classPath), parent); this.classBytes = classBytes; } public MemoryClassLoader(Map classBytes, String classPath) { this(classBytes, classPath, ClassLoader.getSystemClassLoader()); } public MemoryClassLoader(Map classBytes) { this(classBytes, null, ClassLoader.getSystemClassLoader()); } public Class load(String className) throws ClassNotFoundException { return loadClass(className); } public Iterable loadAll() throws ClassNotFoundException { List classs = new ArrayList(classBytes.size()); for (String name : classBytes.keySet()) { classs.add(loadClass(name)); } return classs; } protected Class findClass(String className) throws ClassNotFoundException { byte[] buf = classBytes.get(className); if (buf != null) { // clear the bytes in map -- we don't need it 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 list = new ArrayList(); 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 :

    1. In order to represent a Java source file in memory instead of disk, I defined a StringInputBuffer class in the MemoryJavaFileManager.java .
    2. To save the compiled .class files in memory, I implemented a class MemoryJavaFileManager . The main idea is to override the function getJavaFileForOutput() to store bytecodes into a map.
    3. To load the bytecodes in memory, I have to implement a customized classloader MemoryClassLoader , which reads bytecodes in the map and turn them into classs.

    Here is a 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()); } } 

    Referência

    1. JavaCompiler.java from Cloudera Morphlines
    2. How to create an object from a string in Java (how to eval a string)?
    3. InMemoryJavaCompiler
    4. Java-Runtime-Compiler
    5. [动态的Java – 无废话JavaCompilerAPI中文指南]
    6. Compile and Run Java Source Code in Memory .
     String fileToCompile = ; JavaCompile compiler = ToolProvider.getSystemJavaCompiler(); if( compiler.run(null, null, null, "PACKAGE_NAME" + java.io.File.separator +"CLASS_NAME.java") == 0 ) System.out.println("Compilation is successful"); else System.out.println("Compilation Failed");