jOOQ/jOOR

Dynamic compilation with provided ClassLoader

tioricardo opened this issue · 14 comments

Hi, is it possible to create overloaded Compile.compile(String, String, ClassLoader) and Reflect.compile(String, String, ClassLoader)?

I use Apache CXF to create dynamic SOAP web service client, but it loads the classes on a new ClassLoader instance, so I can't reference them on my class compiled by Reflect.compile(String, String).

I may try submit a PR later.

Thank you very much for your suggestion. That definitely makes sense. Will implement right away.

Hang on, I misunderstood. Would you mind showing an example of what doesn't work with the current implementation, and how your suggestion would improve that?

To create a dynamic SOAP client, Apache CXF reads a WSDL, generates Java source files and then creates a new ClassLoader to load the classes.
When I call the service, I need to use an instance of one of those classes.

I intend to use jOOR to compile a java.util.function.Function to populate the request object, but it needs the ClassLoader created by CXF to load the class.
Currently I can't compile my class, the error I get is:

Exception in thread "main" org.joor.ReflectException: Compilation error: /ricardo/sandbox/Input2Request.java:5: error: package com.dataaccess.webservicesserver does not exist
        com.dataaccess.webservicesserver.NumberToWords req = new com.dataaccess.webservicesserver.NumberToWords();
                                        ^
/ricardo/sandbox/Input2Request.java:5: error: package com.dataaccess.webservicesserver does not exist
        com.dataaccess.webservicesserver.NumberToWords req = new com.dataaccess.webservicesserver.NumberToWords();
                                                                                                 ^
2 errors

	at org.joor.Compile.compile(Compile.java:65)
	at org.joor.Reflect.compile(Reflect.java:77)
	at ricardo.sandbox.Sample.joor(Sample.java:64)
	at ricardo.sandbox.Sample.main(Sample.java:21)

Oh, I see, thanks for the explanation. But are you sure that passing a class loader is really needed? It appears that it might be sufficient to simply pass the desired parent class loader to the ClassLoader constructor.

I have created a branch:
https://github.com/jOOQ/jOOR/tree/issue-64

With a fix:
aac2e98

Would you mind testing that on your side to see if it fixes your problem?

I just reviewed your fix and I realized I didn't mention I'm working with Java 8. My bad m(._.)m

Anyway, CXF uses org.apache.cxf.common.classloader.ClassLoaderUtils.getURLClassLoader() to get a ClassLoader:

public static ClassLoader getURLClassLoader(
    final URL[] urls, final ClassLoader parent
) {
    return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
        public ClassLoader run() {
            return new URLClassLoader(urls, parent);
        }
    });
}

with parent as Thread.currentThread().getContextClassLoader(), so it's not related to the MethodHandles.lookup().lookupClass().getClassLoader().

Huh, but you also said:

but it loads the classes on a new ClassLoader instance

And that's being done in the Java 9+ distribution only. Maybe I really do need an MCVE first before I can help here: https://stackoverflow.com/help/mcve

Or you could send a PR that you know will fix your issue?

but it loads the classes on a new ClassLoader instance

By "it" I meant CXF.

Right now I'm at work, later I'll provide an MCVE.
Thanks for all the replies.

By "it" I meant CXF.

Oh, I see, thanks for clarifying! :-) That does make more sense indeed.

I prepared an MCVE, but when I tested with a possible fix a new problem arose.
The class that I tried to compile was referencing a dynamic class that exists only in memory, so I couldn't set a classpath with it.
So instead of new com.dataaccess.webservicesserver.NumberToWords() I used org.joor.Reflect.on("com.dataaccess.webservicesserver.NumberToWords", cl) and it worked as intended.

Thanks for your help!

Cool, thanks for the feedback. Glad you've found a solution

Related: #72

Hi,
I have a spring boot application with some custom code and I've tried to compile that sample with a custom class:

Supplier<String> supplier = Reflect.compile(
            "com.sample.soc.RuntimeCompilerTest",
            "package com.sample.soc; " +
                    "import com.sample.package.WebsitesBO; " +
                    "class RuntimeCompilerTest implements java.util.function.Supplier<String> { " +
                    "public String get() { " +
                    "return \"Hello World!\"; } " +
                    "}"
    ).create().get();

But I get the next error (classloader):

Compilation error: /com/sample/soc/RuntimeCompilerTest.java:1: error: package com.sample.package does not exist\r\npackage com.sample.soc; import com.sample.package.WebsitesBO; class RuntimeCompilerTest implements java.util.function.Supplier { public String get() { return "Hello World!"; } }\r\n ^\r\n1 error\r\n

Can I compile dynamic classes with my custom code?

Thank you.

Java 8
Library Version: 0.9.8 and 0.9.9

@joanbonilla May I invite you to create a new issue in the future? Yours isn't really related to this one. In your case, you named a package package, which is not possible in Java, given that package is a reserved word.

You are right, I replaced the original package (I didn't want to show it :) ) #73