Win a copy of TDD for a Shopping Website LiveProject this week in the Testing forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Jeanne Boyarsky
  • Tim Cooke
Sheriffs:
  • Liutauras Vilda
  • paul wheaton
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Tim Holloway
  • Stephan van Hulst
  • Carey Brown
  • Frits Walraven
Bartenders:
  • Piet Souris
  • Himai Minh

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

 
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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!!

 
author
Posts: 3281
8
Mac OS X Eclipse IDE Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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?
 
Rancher
Posts: 43027
76
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
Corey Brady
Greenhorn
Posts: 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Rancher
Posts: 43027
76
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Posts: 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Rancher
Posts: 43027
76
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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 didn't like the taste of tongue and it didn't like the taste of me. I will now try this tiny ad:
Free, earth friendly heat - from the CodeRanch trailboss
https://www.kickstarter.com/projects/paulwheaton/free-heat
reply
    Bookmark Topic Watch Topic
  • New Topic