views:

2978

answers:

6

Hi there,

I've just solved another I-though-I-was-using-this-version-of-a-library-but-apparently-my-app-server-has-already-loaded-an-older-version-of-this-library-issue (sigh).

Does anybody know a good way to verify (or monitor) whether your application has access to all the appropriate jar-files, or loaded class-versions?

Thanks in advance!

[P.S. A very good reason to start using the OSGi module architecture in my view!]

Update: This article helped as well! It gave me insight which classes JBoss' classloader loaded by writing it to a log file.

A: 

If the manifest file of the jar files you're interested in has a version field then you can access it via:

ZipFile zipJar=zipJar=new ZipFile("jarToMonitor.jar");
ZipEntry zipEn=zipJar.getEntry("META-INF/MANIFEST.MF");
BufferedInputStream buf=new BufferedInputStream(zipJar.getInputStream(zipEn));
Properties p=new Properties();
p.load(buf);
String build=p.get("version").toString();
zipJar.close();

To find the JARs loaded you can look in the classpath property:

String classpath[]=System.getProperty("java.class.path").split(System.getProperty("path.separator"));`

Then iterate around those (checking if they're directories then search for jar files).

GavinCattell
If your application uses the above code to verify the loaded jars against the version is 'knows' it needs and shouts about it at runtime. you should know of problems at start-up.
Ron Tuffin
also, tha man says his application runs in an appserver. your code wouldn't work as libraries are not referenced through the classpath mechanism.
entzik
+1  A: 

I don't think there is a good way to check that. And I'm not sure you want to do that. What you need to do is get familiar with your app-server's class loading architecture, and understand how that works.

A simplified explanation of how it works is: an EJB or a web-app will first look for a class or a resource in libraries declared in its own module (ejb-jar or war). if the class is not found there, the class loader forwards the request to the its parent class loader which is either a declared dependency (usualy an ejb) or the the application class loader which is responsible to load libraries and resources declared in the ear package. If the class or the resource is still not found the request is forwarded to the app server which will look in its own classpath.

This being said, you should remember that a JEE module (web-app, ejb) will always load classes from a jar that is in the nearest scope. For example if you package log4j v1 in the war file, log4j v2 at ear level and you put log4j v3 in your app-server's class path, the module will use the jar in its own module. take that away and it'll use the one at ear level. take that out and it will use the one in the app-server's classpath. Things get more tricky when you have complex dependencies between modules.

Best is to put application global libraries at ear level.

entzik
For JBoss at least, it's not as simple as that. In some cases JBoss will load a class from *outside* your WAR file (e.g from server/default/lib) even though the class exists *inside* your WAR file. It depends on various factors such as whether scoped loading is enabled (the default value for which varies between JBoss versions) and whether delegation to the parent class repository is enabled.
Andrew Swan
A: 

There must be a better way than the way I do it, but I tend to do this in a very manual way.

  1. Every Jar must have it's version number in the file name (if it doesn't change it's name).
  2. each application has it's own classpath.
  3. There must be a reason to start using an updated Jar (new version). Don't just change because it is available, change because it gives you functionality that you need.
  4. Each release must include all Jars that are needed.
  5. I keep a Version class that knows the list of Jars it needs (this is coded into the source file) and can be checked at runtime against the list of Jars in the classpath.

As I said, it is manual, but it works.

Ron Tuffin
+2  A: 

In the current version of Java, library versioning is a rather wooly term that relies on the JAR being packaged correctly with a useful manifest. Even then, it's a lot of work for the running application to gather this information together in a useful way. The JVm runtime gives you no help whatsoever.

I think your best bet is to enforce this at build time, using dependency management tools like Ivy or Maven to fetch the correct versions of everything.

Interestingly, Java 7 will likely include a proper module versioning framework for precisely this sort of thing. Not that that helps you right at this moment,

skaffman
+7  A: 

If you happen to be using JBoss, there is an MBean (the class loader repository iirc) where you can ask for all classloaders that have loaded a certain class.

If all else fails, there's always 'java -verbose:class' which will print the location of the jar for every class file that is being loaded.

Tom
Sounds like something I could use Tom! I'll dig into it tomorrow. Thanks in advance....Johan
Johan Pelgrim
+2  A: 

If you've got appropriate versions info in a jar manifest, there are methods to retrieve and test the version. No need to manually read the manifest.

java.lang.Package.getImplementationVersion() and getSpecificationVersion() and isCompatibleWith() sound like they'd do what you're looking for.

You can get the Package with this.getClass().getPackage() among other ways.

The javadoc for java.lang.Package doesn't give the specific manifest attribute names for these attributes. A quick google search turned it up at http://java.sun.com/docs/books/tutorial/deployment/jar/packageman.html

John M