I am compiling and loading classes in memory with JavaCompiler
.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaCompiler.CompilationTask compile = compiler.getTask(null, inMemoryFileManager, diagnostics, null, annotationClasses, compilationUnits);
if (!compile.call()) {
diagnostics.getDiagnostics().forEach(System.err::println);
}
I am using a similar Filemanager
and SimpleJavaObject
as in this example. I modified it slightly to be able to handle multiple files. If you need my source code please let me know.
The code above runs fine as long as annotationClasses
is null.
When I try to add classes to annotation processing however I get the following error:
Class names, '<classNames>', are only accepted if annotation processing is explicitly requested
For classNames I tried Main, Main.java, package.Main and package.Main.java. They all result in the same error.
I know this is a very specific scenario. Did anybody try to do something similar and can help me?
Edit:
/* Container for a Java compilation unit (ie Java source) in memory. */
private static class CompilationUnit extends SimpleJavaFileObject {
private final String source;
public CompilationUnit(String className, String source) {
super(URI.create("file:///" + className.replaceAll("\\.", "/") + ".java"), Kind.SOURCE);
this.source = source;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}
@Override
public OutputStream openOutputStream() {
throw new IllegalStateException();
}
@Override
public InputStream openInputStream() {
return new ByteArrayInputStream(source.getBytes());
}
}
/* Container for Java byte code in memory. */
private static class ByteJavaFileObject extends SimpleJavaFileObject {
private ByteArrayOutputStream byteArrayOutputStream;
public ByteJavaFileObject(String className) {
super(URI.create("byte:///" + className.replaceAll("\\.", "/") + ".class"), Kind.CLASS);
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return null;
}
@Override
public OutputStream openOutputStream() {
byteArrayOutputStream = new ByteArrayOutputStream();
return byteArrayOutputStream;
}
@Override
public InputStream openInputStream() {
return null;
}
public byte[] getByteCode() {
return byteArrayOutputStream.toByteArray();
}
}
private static class InMemoryFileManager extends ForwardingJavaFileManager {
private final InMemoryClassLoader inMemoryClassLoader;
public InMemoryFileManager(JavaCompiler compiler, Map<String, ByteJavaFileObject> fileObjects) {
super(compiler.getStandardFileManager(null, null, null));
inMemoryClassLoader = new InMemoryClassLoader(fileObjects);
}
@Override
public JavaFileObject getJavaFileForOutput(Location notUsed, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return inMemoryClassLoader.getFileObject(className);
}
@Override
public ClassLoader getClassLoader(Location location) {
return inMemoryClassLoader;
}
public InMemoryClassLoader getClassLoader() {
return inMemoryClassLoader;
}
}
private static class InMemoryClassLoader extends ClassLoader {
private final Map<String, ByteJavaFileObject> fileObjects;
public InMemoryClassLoader(Map<String, ByteJavaFileObject> fileObjects) {
this.fileObjects = fileObjects;
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
ByteJavaFileObject fileObject = fileObjects.get(className);
return defineClass(className, fileObject.getByteCode(), 0, fileObject.getByteCode().length);
}
JavaFileObject getFileObject(String className) {
return fileObjects.get(className);
}
}