Closed lexika979 closed 6 months ago
Hi!
j4rs
prepares the System Classloader, and initializes the JVM as needed. The system classpath is defined during the Java VM initialization and additionally, j4rs
provides the ability to add more jars/classes during runtime, but that's it. Classes are loaded only once in the process lifecycle and this stands for any classloader.
Handling cases as you describe is pretty delicate and difficult to achieve in a generic way. Classloader isolation is something that OSGi tries to resolve for example. This is for sure something cumbersome with many corner cases.
Depending on your use case, this could be something that may be resolved with some Java code tricks. If you have control on creating the said instances and there is a defined logic when to load a Class from one jar and when from another, you could implement your own Classloaders (one per version you want to use) and then have a factory class that does instantiations via reflection, selecting one of the Classloaders per case. Your Rust code can just call the factory, asking it to instantiate Objects of classes loaded by a specific Classloader.
Here is an example with two custom classloaders, defined as children of the System Classloader. Using reflection, we invoke two different versions of a method:
public class Main {
public static void main(String[] args) throws Exception {
MyClassLoader c1 = new MyClassLoader(ClassLoader.getSystemClassLoader());
MyClassLoader c2 = new MyClassLoader(ClassLoader.getSystemClassLoader());
File jarV1 = new File("/my/v1/tmp_classpath/my-lib-0.1.0.jar");
c1.add(jarV1.toURI().toURL());
File jarV2 = new File("/my/v2/tmp_classpath/my-lib-0.2.0.jar");
c2.add(jarV2.toURI().toURL());
var myTestV1 = Class.forName("my.lib.MyTest", true, c1);
var myTestV2 = Class.forName("my.lib.MyTest", true, c2);
// Invoke MyTest.staticMethodToInvoke for version 0.1.0
Method method1 = myTestV1.getMethod("staticMethodToInvoke");
method1.invoke(null);
// Invoke MyTest.staticMethodToInvoke for version 0.2.0
Method method2 = myTestV2.getMethod("staticMethodToInvoke");
method2.invoke(null);
}
public static class MyClassLoader extends URLClassLoader {
public MyClassLoader(ClassLoader offeredClassLoader) {
super("myClassLoader", new URL[0], offeredClassLoader);
}
void add(URL url) {
addURL(url);
}
}
}
Of course many things can go wrong, depending on what you want to achieve.
So from what I understand, j4rs does not support this on its own, and I would have to do some tricks on the Java side (by implementing a custom classloader) to get this working? Is there no other way to do this?
Yes, this is something application specific that actually concerns the Java part and cannot be handled in an easy or even generic way.
Regarding the solution, the only idea I have is the one I commented and this is outside j4rs
. It just seemed interesting and gave it some thought instead of just saying smth like "not supported" :smile:
I understand, thank you for your help!
Hi Aston, quick question: As you may remember from my past issue(s), I'm currently working on a software product that implements a REST API ontop of our legacy JAR's for the company I work at. A couple of days ago, the scope for the API was re-evaluated, and now I need to be able to load multiple JAR's at once (With conflicting class paths!). I figured that implimenting this would be fairly trivial, just create another Jvm, let it load the other JAR's, then use it.
Unfortunately, no matter on which Jvm I perform calls on, It seems like all calls get re-routed to the first JAR I loaded. Is this intentional or a bug? If this is intentional/a technical limitation, how could I work around this without major performance hits?
Thank you for providing this awesome library! Regards