0

Adapting this the following code takes a class and function name, a string of Java code, compiles the code and runs the function.

public class Compile {

    static void compileAndRun(final String className, final String methodName, final String code) {

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

        JavaFileObject file = new JavaSourceFromString(className, code);

        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
        JavaCompiler.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(methodName, 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);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
    }
}

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

You might call this using:

    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(\"Hello World !\");");
    out.println("  }");
    out.println("}");
    out.close();

    Compile.compileAndRun("HelloWorld", "main", writer.toString());

Is it possible from inside the compiled program to call external functions and variables? e.g.

class SomeClass {

    int x = 123;

    void myFunc() {

        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);
        out.println("public class HelloWorld {");
        out.println("  public static void main(String args[]) {");
        out.println("    y = foo(x);");
        out.println("  }");
        out.println("}");
        out.close();

        Compile.compileAndRun("HelloWorld", "main", writer.toString());
    }

    void foo(int x) {
    } 
}

or maybe with the foo and x in another class? Obviously I tried this, but it fails to compile. Is there a way to achieve this?

1 Answer 1

0

In the end I was able to use Groovy, which is a superset of the Java language, to achieve my goal, so you can write a script in Java or Groovy. Passing in variables and classes, which can of course contain variables and functions, was straight-forward, e.g.

class MyClass {

    int x = 100;

    void myFunc(int r) {

        System.out.println("value from inside script = " + r + " !");
    }
}

void groovy() {

    MyClass q = new MyClass();

    StringWriter writer = new StringWriter();
    PrintWriter out = new PrintWriter(writer);

    out.println("    System.out.println(\"Hello world\" + q.x);");
    out.println("    q.x += 100;");
    out.println("    q.myFunc(123)");

    out.close();

    // call groovy expressions from Java code
    Binding binding = new Binding();
    binding.setVariable("q", q);
    GroovyShell shell = new GroovyShell(binding);

    Object value = shell.evaluate(writer.toString());

    System.out.println("new value = " + q.x);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.