Reputation: 161
I have to use a 3rd party platform, but the platform has an older version of the jar libjar-1.0.0.jar
that cannot be replaced. The platform let me run my own (flat file) packages on top of it. I put the new version of libjar-2.0.0.jar
under my package /packages/package-name/external-jar. When I used URLClassLoader
to load libjar-2.0.0.jar
and then printing out all declaredmethods, I was able to see the method that is in 2.0.0 jar. However, when I invoke, I always get NoSuchMethodException
. When I print out newobj.class.getProtectionDomain().getCodeSource().getLocation().toString()
, it always shows libjar-1.0.0.jar
. Could anyone help explaining what I did wrong and what I need to do to force using the classes in a particular jar during runtime?
Here is a snapshot of my code
File f = new File(path);
URL[] urls = new URL[1];
urls[0] = f.toURI().toURL();
ClassLoader cl = new URLClassLoader(urls);
Class<?> utilsClass = cl.loadClass("com.myclass");
Constructor<?> cons = utilsClass.getConstructor(First.class, Second.class);
Object utils = cons.newInstance(firstObj, new Second());
if (utilsClass.getProtectionDomain() != null) {
LOGGER.info(utilsClass.getProtectionDomain().getCodeSource().getLocation().toString());
}
// this print out --- 1.0.0.jar instead of 2.0.0.jar
for (Method m : utilsClass.getDeclaredMethods()) {
LOGGER.info("methods: " + m.getName());
}
// method shows the "methodILookFor"
Method m = utilsClass.getDeclaredMethod("methodILookFor", Target.class, String[].class, Object.class);
// always throws NoSuchMethodException
m.invoke(utils, target, string, obj);
Upvotes: 1
Views: 3642
Reputation: 7394
URLClassLoader
is used for loading classes that are not already specified in the application classpath.
URLClassLoader
delegates the class loading to its parent, i.e., the Application Class Loader
. Application Class Loader
finds the class in libjar-1.0.0.jar
. So, the URLClassLoader
ends up not loading the class from libjar-2.0.0.jar
.Here is a simple example of a custom class loader which extends URLClassLoader
. This class loader tries to load classes from its URLs before it delegates to its parent class loader. It should be able to load different versions of JARs that you need in your example. You will find a complete example here with a unit test.
P.S. Class loading has changed in Java 9. It is not tested with Java 9 and may not work.
public class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected synchronized Class<?> loadClass(String name,
boolean resolve) throws ClassNotFoundException {
// 1. Check if the class has already been loaded
Class<?> clazz = findLoadedClass(name);
ClassLoader parentCL = getParent();
// 2. If the class is not loaded and the class name starts
// with 'java.' or 'javax.', delegate loading to parent
if (clazz == null && parentCL != null && (name.startsWith(
"java.") || name.startsWith(
"javax."))) {
clazz = parentCL.loadClass(name);
}
// 3. If the class is still null, try to load the class from the URL
// (since we have already taken care of 'java.' and 'javax.'
if (clazz == null) {
try {
clazz = super.findClass(name);
} catch (ClassNotFoundException e) {
//don't do anything
}
}
// 4. If the class is still null, let the parent class loader load it.
// Previously, we allowed 'java.' and 'javax.' classes to be loaded
// from parent
if (clazz == null && parentCL != null) {
clazz = parentCL.loadClass(name);
}
// 5. If the class is still null, throw a class not found exception
if (clazz == null) {
throw new ClassNotFoundException(name);
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
}
Upvotes: 3
Reputation: 583
Instead of using URL class loader you can try using custom class loader to load class using its fully qualified name. Using this approach you should be able to bypass the class loading delegation to the parent class loader which is causing problem in your case. So your class loader should be able to load the class from libjar-2.0.0.jar.
Upvotes: 1