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.
tmpfs) and that would be nearly as fast as working on memory. Maybe you could write your ownClassLoaderSecurityManager.