2

I am dynamically compiling Java sources using the Java compiler API. My generated source files inherit from com.example.BaseClass, which is just a normal class, not dynamically generated. The generated Java sources look like this:

public class Foo implements com.example.BaseClass
{
    @Override
    public Integer getAnswer(com.example.Context context) throws Exception
    {
        return ...;
    }
}

All works fine when running in IDE, but after packaging into a Springboot jar, my com.example.BaseClass is moved to BOOT-INF/classes/com.example.BaseClass. When dynamically compiling I now get:

/Foo.java:1: error: package com.example does not exist 
public class Foo implements com.example.BaseClass
                                       ^

I try to change the classloader of the compiler so that the compiler will search in BOOT-INF/classes.

    ClassLoader before = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(new CustomClassloader(before));
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    Thread.currentThread().setContextClassLoader(before);

However, debugging shows that my CustomClassloader.loadClass(String name) method is never called. More debugging showed that compiler.getClass().getClassloader() returns

java.net.FactoryURLClassLoader@39a5ae48

So, the CustomClassloader is not used by the Compiler instance. How can I get the Compiler to use my CustomClassloader? Better solutions for solving the compiling issue are also welcome ofcourse :-).

2
  • What happens when, instead of swapping classloaders, you getClass().forName("com.example.BaseClass") before you get an instance of JavaCompiler Commented Sep 21, 2018 at 9:02
  • No change; compiler still complains that package does not exist. Commented Sep 21, 2018 at 15:49

1 Answer 1

0

There are some oddities about how the java standard compiler does lookups and it doesn't always resolve out of the running class path correctly. Anyway, it does that resolution using the JavaFileManager.list call.

It will call it at least 4 times in the process of trying to look up your base class. Override a ForwardingJavaFileManager and pass that into getTask and have it lookup the resource and return it.

Alternately, you could use the Janino in-momeory compiler library which sets up a fake in memory file system ( no compiling to disk ) and still uses the plaform compiler and sorts out all this classpath nonsense for you.

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

3 Comments

@Iscoughlin Thanks for your input. I have created a JavaFileManager, the list() method does find BaseClass. However, the list method has to return Iterable<JavaFileObject>. I have implemented JavaFileObject using an anonymous class. When the code is about to compile, I get this message: java.lang.RuntimeException: java.lang.IllegalArgumentException: com.example.MyCompilingClass$1$1 l suppose the IllegalArgument is caused by the Compiler class not being able to find the anonymous inner class. Same message when using subclass of SimpleJavaFileObject instead of anonymous class.
You only need simple file object. The kind should be Kind.CLASS and the URI should resolve to your ...com/example/BaseClass.class resource.
Also, you can see a complete implementation of a byte array file manager here: janino-compiler.github.io/janino/apidocs/org/codehaus/commons/…

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.