2

Say I have a String containing the content of a .java file. Are any APIs out there that would allow me to compile this source file into a virtual .class file (i.e. generate and store the content in memory, NOT creating an actual physical .class file on disk)? And this "virtual" .class would then be loaded and executed in the JVM?

Edit 1: The only reason I want to do this is because sometimes, my application might not have the write permission.

5
  • Probably not, but you could (at least on Linux) create that file on a disk-less filesystem (e.g. tmpfs) and that would be nearly as fast as working on memory. Maybe you could write your own ClassLoader Commented Apr 18, 2013 at 5:25
  • "my application might not have the write permission." Is that because it is sand-boxed by Java, or DYM OS security settings? Commented Apr 18, 2013 at 5:29
  • @AndrewThompson Both. It might be sand-boxed by Java because my app will be downloaded from the internet. By the OS because the user might decide to put the jar file (my app) in some directory like C:\ in Windows. Commented Apr 18, 2013 at 5:34
  • "downloaded from the internet." So by that you mean an applet or JWS app.? AFAIU, they are the only ones where Java supplies a SecurityManager. Commented Apr 18, 2013 at 6:29
  • Sorry I misread you question. No, it's a desktop app, so the write permission is caused only by OS settings. Commented Apr 18, 2013 at 7:34

3 Answers 3

1

Use the JavaCompiler for this. I think the trick will be to define a custom JavaFileManager.

Sign up to request clarification or add additional context in comments.

Comments

1

Java does have a compilation API to compile files dynamically, but I'm not aware of an option that would not persist the class files to disk. You can always use a ClassLoader and load those classes dynamically and then use them. You might be able to load the classes in memory by overriding the getFileForOutput method.

Optionally, this file manager might consider the sibling as a hint for where to place the output. The exact semantics of this hint is unspecified. The JDK compiler, javac, for example, will place class files in the same directories as originating source files unless a class file output directory is provided. To facilitate this behavior, javac might provide the originating source file as sibling when calling this method.

Another option is to use an Interpreter like BeanShell that will run the java code for you. It executes script like code and can work in repl mode.

Comments

0

javax.tools has everything you need, but needs a bit of coaxing to stop storing class files on the file system. Fortunately, it can be done with a single class of about 100 lines of code:

package simple.tools;

import java.io.*;
import java.net.URI;
import java.util.*;

import javax.tools.*;
import javax.tools.JavaCompiler.CompilationTask;

public class SimpleCompiler {

    public static JavaFileObject sourceFile(String name, String source) {
        return inputFile(name, JavaFileObject.Kind.SOURCE, source);
    }

    private static URI uri(String name, JavaFileObject.Kind kind) {
        return URI.create(name.replace('.', '/') + kind.extension);
    }

    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    private final JavaFileManager manager =
    new ForwardingJavaFileManager<JavaFileManager>(compiler.getStandardFileManager(null, null, null)) {
        @Override
        public JavaFileObject getJavaFileForOutput(Location l, String name, JavaFileObject.Kind kind, FileObject f) {
            return outputFile(name, kind);
        }
    };

    private static JavaFileObject inputFile(String name, JavaFileObject.Kind kind, String content) {
        return new SimpleJavaFileObject(uri(name, kind), kind) {
            @Override
            public CharSequence getCharContent(boolean b) {
                return content;
            }
        };
    }

    private JavaFileObject outputFile(String name, JavaFileObject.Kind kind) {
        return new SimpleJavaFileObject(uri(name, kind), kind) {
            @Override
            public OutputStream openOutputStream() {
                return outputStream(name);
            }
        };
    }

    private final Map<String, byte[]> classes = new HashMap<>();

    private OutputStream outputStream(String name) {
        return new ByteArrayOutputStream() {
            @Override
            public void close() {
                classes.put(name, toByteArray());
            }
        };
    }

    private final ClassLoader loader = new ClassLoader() {
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] bytes = classes.get(name);
            if (bytes == null) throw new ClassNotFoundException(name);
            return super.defineClass(name, bytes, 0, bytes.length);
        }
    };

    public Class<?> compile(String name, String source) {
        compile(sourceFile(name, source));
        try { return loadClass(name); }
        catch (ClassNotFoundException e) { throw new IllegalStateException(e.toString(), e); }
    }

    public void compile(JavaFileObject... files) {
        compile(Arrays.asList(files));
    }

    public void compile(List<JavaFileObject> files) {
        if (files.isEmpty()) throw new RuntimeException("No input files");
        DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
        CompilationTask task = compiler.getTask(null, manager, collector, null, null, files);
        boolean success = task.call();
        check(success, collector);
    }

    private void check(boolean success, DiagnosticCollector<?> collector) {
        for (Diagnostic<?> diagnostic : collector.getDiagnostics()) {
            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
                String message = diagnostic.getMessage(Locale.US);
                throw new RuntimeException(message.split("[\r\n]")[0]);
            }
        }
        if (! success) throw new RuntimeException("Unknown error");
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loader.loadClass(name);
    }
}

See https://gitlab.com/jcsahnwaldt/simple-java-tools.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.