0

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);
        }
    }
eclipse
  • 2,831
  • 3
  • 26
  • 34
  • 1
    Have you tried package/Main.java? – jbunting Jun 29 '15 at 10:47
  • Yes it gives not a valid class name. – eclipse Jun 29 '15 at 11:00
  • 1
    Alright. What are you trying to accomplish? If you can share some source code, that would be helpful. – jbunting Jun 29 '15 at 11:07
  • Well I am trying to compile and load jpa classes with annotation processing. It would be nice if I can accomplish this in memory. I will edit my question to show the source code. – eclipse Jun 29 '15 at 11:10
  • 1
    Makes sense. What are you trying to accomplish by setting annotationClasses to something other than null? I ask only because in the several times that I've used this api I don't believe that I've ever passed anything inn for that parameter. – jbunting Jun 29 '15 at 11:35
  • I just read the javadoc again and I think you are right I should set it to null. Ah then the problem lies with JPA. I got the jpa error **bean class not found, does it have @Entity annotation**. – eclipse Jun 29 '15 at 11:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81867/discussion-between-eclipse-and-jbunting). – eclipse Jun 29 '15 at 11:50

0 Answers0