This week's book giveaway is in the Agile and other Processes forum.
We're giving away four copies of The Mikado Method and have Ola Ellnestam and Daniel Brolund on-line!
See this thread for details.
The moose likes Java in General and the fly likes Robustness to External Library versioning (solution through dynamic classloading, URLClassLoader). Big Moose Saloon
  Search | Java FAQ | Recent Topics
Register / Login


Win a copy of The Mikado Method this week in the Agile and other Processes forum!
JavaRanch » Java Forums » Java » Java in General
Reply Bookmark "Robustness to External Library versioning (solution through dynamic classloading, URLClassLoader)." Watch "Robustness to External Library versioning (solution through dynamic classloading, URLClassLoader)." New topic
Author

Robustness to External Library versioning (solution through dynamic classloading, URLClassLoader).

Corey Brady
Greenhorn

Joined: Apr 04, 2009
Posts: 3
Hello -
The problem I am trying to solve is:

My application needs to use classes found in a jar that is installed separately, and that exists in two versions. Classes and methods in the two versions of the jar have the same signatures but differ in implementations.

In an attempt to address this, I am...

1) compiling against one of the jar versions
2) at runtime, using a technique to discover the installed location of the actual version of the jar
3) using a URLClassLoader to load the jar

Everything seems to work well, until i try to CAST the newly created class instance. At that point, I get a NoClassDefFoundException

For reference, here is a snippet of code. In this example, the "NavstackPacket" class is the one that i'm loading, and attempting to pass back to the caller of this method. "ourClassLoader" is a URLClassLoader. The exception comes in the line, NavstackPacket nspInstance = (NavstackPacket)o


public NavstackPacket getNewNavstackPacket( byte[] payld )
{
Class<?> NSP;
System.err.println("Attempting to create navstack PACKET");
try {
NSP = ourClassLoader.loadClass("com.learningsoft.navstack.NavstackPacket");
Constructor<?> c = NSP.getConstructor(new Class[] { byte[].class } );
Object o = c.newInstance(new Object[] {payld} );
NavstackPacket nspInstance = (NavstackPacket)o;
System.err.println("Loaded Packet Class from Classloader");
return nspInstance;
}
catch (NoSuchMethodException e) { System.err.println( "Couldn't get to necessary constructor "); }
catch (InstantiationException e) {System.err.println("Abstract class");}
catch (InvocationTargetException e) {System.err.println("Invocation Target Exception-"); e.printStackTrace(); }
catch (IllegalAccessException e){ System.err.println("Access Exc. in creating NS Socket"); e.printStackTrace();}
catch (ClassNotFoundException e) { System.err.println("Could not locate navstack socket class"); e.printStackTrace(); }
return null;
}


My questions are:

1) is this APPROACH the recommended one in this instance? It seems a common enough problem, but i haven't found any forum discussions on it.
2) if this approach is valid, why is the Type-Casting step causing me issues?
3) is it perhaps related to the way the classloader is reporting issues? (i.e., is it a red herring that the exception is appearing in the type-casting line?

Thanks in advance!!

Martijn Verburg
author
Bartender

Joined: Jun 24, 2003
Posts: 3268

Hi Corey and welcome to Javaranch!

OK, a simpler way to deal with this problem is simply to compile against the same version JAR that is running on your target environment. e.g. commons-lang-2.2.jar.

If the JAR file is in the CLASSPATH on the target environment then your code will just 'simply' work (no looking it up, casting etc).

Now it sounds like the situation is a little bit complicated by the fact that you have 2 versions of the JAR on your target environment CLASSPATH, e.g. commons-lang-2.2.jar and commons-lang-2.4.jar. Is that the case?


Cheers, Martijn - Blog,
Twitter, PCGen, Ikasan, My The Well-Grounded Java Developer book!,
My start-up.
Ulf Dittmer
Marshal

Joined: Mar 22, 2005
Posts: 35241
    
    7
This exception would be occurring if "NavstackPacket" is loaded by more than a single classloader. I didn't fully understand the details, but it sounds as if this might well be the case here.

A class -if loaded by different classloaders- will not be the same in the sense that you could cast (or assign) one to the other. So your code would need to be structured in a way that all references to the dynamically loaded class are confined to a single classloader.


Android appsImageJ pluginsJava web charts
Corey Brady
Greenhorn

Joined: Apr 04, 2009
Posts: 3
Hi! Thanks for the responses!

Reply to Martijn: Yes: it DOES work when i have the right location in the CLASSPATH (which is great), but what i'm attempting to do is handle both versions in my code (and future cases that use the same "lookup" mechanism, as well). I'm not sure if that was clear: I have application A, which uses jar Library Z from application B, which I don't maintain or control. Application B has changed over time, as application (B1), with jar Library (Z1) which has the same signatures and different implementations. I want my application A to work with Z and Z1. I have a mechanism for FINDING the right jar in the installation (properties files in known locations), and I have a guarantee that the signatures have not and will not change.

Reply to Ulf: I think this may be my issue (loading by more than one classloader). The follow-on question i have is -- do i need to explicitly use the classloader to which i've added the URLs of the jar whenever i even MENTION a class in this jar --
For example, in that method i posted, another class has a member variable that is a NavstackPacket. It gets "set up" by this method, so that there's a line like

NavstackPacket outbound = getNewNavstackPacket( thepayload );

so, maybe i should pass the thing back as an OBJECT and then use the classloader to load the class and then CAST the result of getNewNavstackPacket() to the class?

Something like that?

If you have advice about where I can read up on the "standard approaches" to doing this kind of thing, that would also be GREAT -- I bet that there are lots of places in my code where I need to do a little trick like this one, and it'd be better to be doing the "recommended" little trick, if possible

Thanks again!!!
--Corey

Ulf Dittmer
Marshal

Joined: Mar 22, 2005
Posts: 35241
    
    7
The follow-on question i have is -- do i need to explicitly use the classloader to which i've added the URLs of the jar whenever i even MENTION a class in this jar --
For example, in that method i posted, another class has a member variable that is a NavstackPacket. It gets "set up" by this method, so that there's a line like

NavstackPacket outbound = getNewNavstackPacket( thepayload );

so, maybe i should pass the thing back as an OBJECT and then use the classloader to load the class and then CAST the result of getNewNavstackPacket() to the class?

No, that would not do. Or, to answer the first question - yes, it needs to be the same classloader. Every class that has a field called "NavstackPacket", or a cast to it, will ensure that class NavstackPacket is loaded when it is loaded itself. And "loaded" in this context does not mean "loaded by any classloader" but "loaded by the class's own classloader, or a classloader higher up in the hierarchy" - which will exclude any classloader explicitly set up in your code.

One possible solution would be to separate all "interaction points" -for lack of a better phrase- to interfaces instead of concrete classes, and then to keep the interfaces in a jar file that's loaded by the application classloader (which is higher up in the hierarchy than any classloader that your code creates explicitly).
Corey Brady
Greenhorn

Joined: Apr 04, 2009
Posts: 3
Ah: ok -- i think i've got it (i'll know for sure when i get into the implementation of this idea !
Thanks again,
--Corey

Ulf Dittmer
Marshal

Joined: Mar 22, 2005
Posts: 35241
    
    7
By sheer coincidence I heard a talk yesterday about the OSGi service platform and its Java implementation Apache Felix. Problems like this one are *exactly* what OSGi addresses.
 
I agree. Here's the link: http://zeroturnaround.com/jrebel - it saves me about five hours per week
 
subject: Robustness to External Library versioning (solution through dynamic classloading, URLClassLoader).
 
Similar Threads
Insights on Class loading needed
problem with login to the server
UDP without IP
Loading POJO object without loading library first
Where is my class? ClassNotFound Exception?