jOOQ / jOOR

jOOR - Fluent Reflection in Java jOOR is a very simple fluent API that gives access to your Java Class structures in a more intuitive way. The JDK's reflection APIs are hard and verbose to use. Other languages have much simpler constructs to access type meta information at runtime. Let us make Java reflection better.
http://www.jooq.org/products
Apache License 2.0
2.81k stars 376 forks source link

Dynamic compilation with provided ClassLoader #64

Closed tioricardo closed 6 years ago

tioricardo commented 6 years ago

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.

lukaseder commented 6 years ago

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

lukaseder commented 6 years ago

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?

tioricardo commented 6 years ago

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)
lukaseder commented 6 years ago

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: https://github.com/jOOQ/jOOR/commit/aac2e981139d665c5fb4b56f596c8272f1c1ff83

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

tioricardo commented 6 years ago

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().

lukaseder commented 6 years ago

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?

tioricardo commented 6 years ago

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.

lukaseder commented 6 years ago

By "it" I meant CXF.

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

tioricardo commented 6 years ago

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!

lukaseder commented 6 years ago

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

lukaseder commented 5 years ago

Related: #72

joanbonilla commented 5 years ago

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

lukaseder commented 5 years ago

@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.

joanbonilla commented 5 years ago

You are right, I replaced the original package (I didn't want to show it :) ) https://github.com/jOOQ/jOOR/issues/73