Friday, 24 June 2011

Discover from which JAR File your class is being loaded

A trick often used by certain organisations is to bundle up the Open Source project code on which their proprietary code depends into their own set of randomly named jar files and then to ship them with their products. The guys at BEA / Oracle are guilty of this practise and the upside is that it ensures that the classes used by their product are available and that they don’t get any ClassNotFoundExceptions. The downside to this is that it can lead to mix ups in your code with completing JAR files on your classpath, and possibly competing versions of the same class leading weird bugs.

If you’re ever in the situation where you’re unsure of which JAR file a class is being loaded from, then you can use the debug code below to investigate.

This class is based upon the CodeSource and URL classes in the JDK and takes the name of the class that may be causing problems as an input argument to its constructor.

  public class ClassLocator {

   
private CodeSource source;
   
private URL location;

   
public ClassLocator(String qualifiedClassName) throws ClassNotFoundException {
     
Class<?> clazz = Class.forName(qualifiedClassName);
      init
(clazz);
   
}

   
public ClassLocator(Class<?> clazz) {
     
init(clazz);
   
}

   
private void init(Class<?> clazz) {
     
source = clazz.getProtectionDomain().getCodeSource();
     
if (isNotNull(source)) {
       
location = source.getLocation();
     
} else {
       
throw new RuntimeException("Unknown code source for class: "
           
+ clazz.getSimpleName() + " -- try rt.jar");
     
}
    }

   
private boolean isNotNull(Object obj) {
     
return obj != null;
   
}

   
public String getFile() {
     
return location.getFile();
   
}

   
public String getPath() {
     
return location.getPath();
   
}

   
public String getHost() {
     
return location.getHost();
   
}

   
public String getUserInfo() {
     
return location.getUserInfo();
   
}

   
@Override
   
public String toString() {
     
return source.toString();
   
}
  }

Note that this code will throw a ClassNotFoundException exception if it can’t find the class you’re trying to load, and a simple RuntimeException if the required CodeSource is null. If the RuntimeException occurs then it usually means that your trying to locate the JAR file of a class in the JDK’s rt.jar file.

The following code is used to demonstrate how the ClassLocator works, showing the locations of the org.apache.log4j.helpers.LogLog and ClassLoaderMain classes:

  public static void main(String[] args) throws ClassNotFoundException {

   
examineClass("org.apache.log4j.helpers.LogLog");
    examineClass
(ClassLoaderMain.class.getName());
 
}

 
private static void examineClass(String className) throws ClassNotFoundException {

   
ClassLocator classLocator = new ClassLocator(className);

    System.out.println
("Examining class: " + className);
    System.out.println
("File: " + classLocator.getFile());
 
}

As you can see, the ClassLocator has several ‘getter’ methods that allow you to determine from where your class has been loaded.

Running the code above will give you the following sample output:

Examining class: org.apache.log4j.helpers.LogLog
File: /Users/Roger/.m2/repository/log4j/log4j/1.2.9/log4j-1.2.9.jar
Examining class: classloader.ClassLoaderMain
File: /Java_Projects2/Tips/marin-tips-java/target/classes/

This is not meant to be a complete class, just something useful that can be built upon...

No comments: