Skompiluj kod w pełni w pamięci za pomocą javax.narzędzia.JavaCompiler [duplikat]

To pytanie ma już odpowiedź tutaj:

Używam Javacompilera z javaxa.pakiet narzędzi (JDK 1.7) do kompilacji niektórych rzeczy w locie, jak to:

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

Działa, ale chciałbym zrobić to wszystko w pamięci (np. przekazać ciąg z kod, Nie plik źródłowy, i uzyskać kod bajtowy z powrotem nie a .plik klasowy). Odkryłem, że rozszerzanie parametrów InputStream i OutputStream jest bezużyteczne, ponieważ prawdopodobnie jest tak samo jak w konsoli. Wiesz, jak sprawić, by metoda run działała w ten sposób? A może wiesz jak to zrobić metodą getTask()? (rozszerzenie menedżera plików wygląda na łatwe, ale nie jest takie proste:)

Author: Sandman, 2012-08-29

7 answers

Uruchomiłem powyższy kod w Mac OS Java 7. Żaden z nich nie działa. Więc napisałem jedną 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());
 29
Author: erolagnab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-06 00:22:54

Myślę, że to tutaj może być pomocne, to w zasadzie pokazuje, jak skompilować źródło Javy z pamięci (ciąg znaków znajduje się w klasie).

Używa PrinterWriter i StringWriter do zapisu źródła do String / w pamięci, a następnie używa klasy JavaCompiler (od JDK 6) do kompilacji i uruchomienia programu:

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<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

    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;
  }
}

Jeśli spojrzysz na odnośnik, znajdziesz też kilka innych przykładów

Numer referencyjny:

 30
Author: David Kroukamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-27 15:32:09

Jest to klasa, która kompiluje w całości w pamięci.

Wziąłem (prawie) całość z http://javapracs.blogspot.de/2011/06/dynamic-in-memory-compilation-using.html [[10]} by Rekha Kumari (czerwiec 2011). Chociaż ta wersja jest o ponad 100 linii krótsza i ma znacznie więcej funkcji (ale bez dokumentów:P).

Może kompilować wiele klas naraz, co jest jedynym sposobem kompilowania klas, które zależą od siebie nawzajem. Jeśli zastanawiasz się nad klasą "CompilerFeedback": pracowałem nad małym Java IDE do kodowania-gier, w których tego potrzebowałem. Włączam to tutaj, ponieważ zakładam, że chcesz zrobić coś z tym kompilatorem, a predigmentacja może w tym pomóc. (Zdaję sobie sprawę, że część kodu w klasie CompilerFeedback jest kompletną bzdurą. Został poddany recyklingowi z próby sprzed lat.

Istnieje również metoda użytkowa, nie potrzebna do kompilacji, która wywodzi pełną nazwę klasy z kodu źródłowego klasy (incl. Nazwa pakietu, jeśli jest podana). Bardzo przydatne do wywołania kompilatora, który potrzebuje tych informacji.

KLASA DEMO:

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<InMemoryCompiler.IMCSourceCode> 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);
    }
}

KLASA KOMPILATORA:

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<IMCSourceCode> classSourceCodes;
    final private JavaFileManager fileManager;

    public InMemoryCompiler(final List<IMCSourceCode> 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<JavaFileManager>(compiler.getStandardFileManager(null, null, null)) {

            final private Map<String, ByteArrayOutputStream> 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<JavaFileObject> 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<JavaFileObject> 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;
    }
}

KLASA 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<CompilerMessage> messages = new ArrayList<>();

    public CompilerFeedback(final Boolean success, final DiagnosticCollector<JavaFileObject> 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<? extends JavaFileObject> 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";
        }
    }
}

Metoda użytkowa (nie jest potrzebna dla trzech klas dalej.):

final public static String PREFIX_CLASSNAME = "class ";
final public static String PREFIX_PACKAGENAME = "package ";
final public static String CHARSET_JAVAKEYWORDENDERS = " \n[](){}<>;,\"\\/*+-=%!&?@:";

/**
 * @return e.g. "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;
}
 10
Author: Dreamspace President,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-22 06:43:16

Napisałem do tego bibliotekę kilka lat temu. Pobiera ciąg znaków, który może zawierać zagnieżdżone klasy, kompiluje je i opcjonalnie ładuje do bieżącego loadera klas (więc nie potrzebujesz dodatkowego loadera klas) jeśli JVM działa w trybie debugowania, zapisze wygenerowany kod do pliku, abyś mógł przejść przez wygenerowany kod.

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


Parafrazując przykład z erolagnab możesz do

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());

Aktualizacja, źródło jest dostępne tutaj https://github.com/OpenHFT/Java-Runtime-Compiler

I możesz uzyskać najnowszą kompilację przez maven http://search.maven.org/#browse%7C842970587


Dłuższy przykład.

// 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);
 7
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-02 16:37:30

Chciałem:

  • W przypadku języka Java nie jest to możliwe, ponieważ jest on używany jako język skryptowy.]}
  • Brak dodatkowych zależności (łatwa konfiguracja)
  • implementacja w jak najmniejszej liczbie plików (łatwa do włączenia w projekt)

Możesz spróbować najpierw tutaj: http://ideone.com/cu1GhE#view_edit_box

Poniższy kod jest oparty na Rekha Kumari Kod:

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<JavaFileObject> 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;
        }
    }
}
 2
Author: Martin Vseticka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-05 11:55:54

[10]}chciałbym przedstawić moje rozwiązanie, które dobrze sprawdza się w produkcji.

Oto trzy pliki kodu źródłowego.

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<String, byte[]> 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<String, byte[]> 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<String, byte[]> compile(String fileName, String source,
        Writer err, String sourcePath, String classPath) {
        // to collect errors, warnings etc.
        DiagnosticCollector<JavaFileObject> diagnostics =
            new DiagnosticCollector<JavaFileObject>();

        // create a new memory JavaFileManager
        MemoryJavaFileManager fileManager = new MemoryJavaFileManager(stdManager);

        // prepare the 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) {
        // to 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 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<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 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<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 {
    }

    /**
     * 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<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());
        for (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 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<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;
    }
}

Objaśnienia :

  1. aby reprezentować plik źródłowy Javy w pamięci zamiast na dysku, zdefiniowałem klasę StringInputBuffer w MemoryJavaFileManager.java.
  2. aby zapisać skompilowane pliki .class w pamięci, zaimplementowałem klasa MemoryJavaFileManager. Główną ideą jest nadpisanie funkcji getJavaFileForOutput() do przechowywania bajtowych kodów na mapie.
  3. aby załadować bajtowe kody do pamięci, muszę zaimplementować Niestandardowy classloader MemoryClassLoader, który odczytuje bajtowe kody na mapie i zamienia je w klasy.

Oto Test unite.

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

Odniesienie

  1. JavaCompiler.java z Cloudera Morphlines
  2. Jak utworzyć obiekt z ciągu znaków w Javie (jak evalować string)?
  3. InMemoryJavaCompiler
  4. Java-Runtime-Compiler
  5. [动态的Java - 无废话javacompilerapi中文指南]
  6. Skompiluj i uruchom kod źródłowy Javy w pamięci .
 0
Author: soulmachine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:07
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");
 -2
Author: Confuse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-22 15:36:14