Saturday, February 28, 2009

Locating The Classpath Definition Used in Java Code

In a previous blog posting, I listed some "everyday problems" Java developers will often face and included some ideas and links related to addressing these issues. One of the focal points of that post was Java classpath issues. One tool that I neglected to mention in that post (but will be adding belatedly when I add a link to this post) is use of Java security-related classes to identify where the class definition used in a Java application comes from on the classpath.

The ability to identify the source of a class definition in a Java application is an especially useful tactic when one knows or has reason to suspect that a particular class is defined twice on the classpath, possibly with different definitions. In general, the first definition of a class found on the classpath is the one used, but this technique can verify the class definition being used or can be useful when there is a very large classpath that makes it difficult to identify all instances of a particular classpath entry.

As discussed in How to Determine Where Your Java Class is Actually Located, Where's That Class Coming From?, How to Know from Which JAR Your Class is Loaded From, Where Did That Class Come From?, Where to Find JARs?, and J2SE Security, the Java SE Security model provides a convenient mechanism for identifying the source (JAR or .class file) of a class definition used in a Java application. This approach involves getting the ProtectionDomain from a class's Class and using that ProtectionDomain to obtain a CodeSource which provides a URL to the source of that particular class definition.

I will include the full code a little later, but here is a sample of code focused on using these Security APIs to get class definition information.


/**
* Provides the name of the classpath entry that provides the class definition
* of the provided object.
*
* @param objectInQuestion Object for which classpath source of instance is
* desired.
*/
public static final String getClassPathSource(final Object objectInQuestion)
{
final StringBuilder builder = new StringBuilder();
final Class clazz = objectInQuestion.getClass();
final ProtectionDomain protectedDomain = clazz.getProtectionDomain();
final CodeSource codeSource = protectedDomain.getCodeSource();
if (codeSource != null)
{
builder.append("CodeSource: ").append(codeSource.toString());
builder.append(Constants.NEW_LINE);
final URL url = codeSource.getLocation();
builder.append("URL: ").append(url.toString());
}
else // codeSource == null
{
builder.append("CodeSource for ")
.append(clazz.getCanonicalName())
.append(" is null.");
}
return builder.toString();
}


As the code listing above shows, it is a relatively straightforward process to use get a particular object's Class to obtain a ProtectionDomain from which a CodeSource can be obtained. The CodeSource provides the getLocation() and toString() methods that make it really easy to display the classpath locations from which the class definition came.

The code snippet above can be used in a class such as the ClassPathIdentifier class shown next.

ClassPathIdentifier.java


package classpathidentifier;

import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;

/**
* This class reports what entry in the classpath provides the definition of
* the provided Object.
*/
public final class ClassPathIdentifier
{
/** This class is not intended for public consumption. */
private ClassPathIdentifier() {}

/**
* Provides the name of the classpath entry that provides the class definition
* of the provided object.
*
* @param objectInQuestion Object for which classpath source of instance is
* desired.
*/
public static final String getClassPathSource(final Object objectInQuestion)
{
final StringBuilder builder = new StringBuilder();
final Class clazz = objectInQuestion.getClass();
final ProtectionDomain protectedDomain = clazz.getProtectionDomain();
final CodeSource codeSource = protectedDomain.getCodeSource();
if (codeSource != null)
{
builder.append("CodeSource: ").append(codeSource.toString());
builder.append(Constants.NEW_LINE);
final URL url = codeSource.getLocation();
builder.append("URL: ").append(url.toString());
}
else // codeSource == null
{
builder.append("CodeSource for ")
.append(clazz.getCanonicalName())
.append(" is null.");
}
return builder.toString();
}

/**
* Test method to demonstrate use of this functionality.
*/
public static void main(final String[] arguments)
{
if (arguments.length < 1)
{
System.err.println("You must provide a fully qualified Class name.");
System.exit(-1);
}
final String classForSource = arguments[0];
if (classForSource.isEmpty())
{
System.err.println("You must provide a valid string for class path/name.");
}
System.out.println("You provided class of " + classForSource + " ...");

try
{
final Class clazz = Class.forName(classForSource);
final Object object = clazz.newInstance();
System.out.println("Source: " + getClassPathSource(object));
}
catch (ClassNotFoundException classNotFoundEx)
{
System.err.println("Could not find class " + arguments[0]);
}
catch (InstantiationException instanceEx)
{
System.err.println("Unable to instantiate instanceof " + classForSource);
}
catch (IllegalAccessException illegalAccessEx)
{
System.err.println("Illegal Access trying to instantiatite " + classForSource);
}
}
}


It is not exciting, but I include the code listing for the Constants class next for completeness.

Constants.java


package classpathidentifier;

/**
* Constants used in simple example demonstrating one way to determine the
* classpath entry from which a particular instance was specified.
*/
public class Constants
{
/** This constructor is not intended for public consumption. */
private Constants() {}

public final static String NEW_LINE = System.getProperty("line.separator");
}


For this example, I am also using a basic class called SomeClass, which is defined next.

SomeClass.java


package classpathidentifier;

public class SomeClass
{
/** An integer. */
private final int someInt;

/** Some String. */
private final String someString;

/**
* Constructor accepting arguments to populate an instance of me.
*/
public SomeClass(final int newInt, final String newString)
{
this.someInt = newInt;
this.someString = newString;
}

/**
* Provide my integer value.
*
* @return My integer value.
*/
public int getSomeInt()
{
return this.someInt;
}

/**
* Provide my String value.
*
* @return My String value.
*/
public String getSomeString()
{
return this.someString;
}

/**
* Provide String representation of me.
*
* @return My String representation.
*/
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
builder.append("Some Int: ").append(this.someInt).append(Constants.NEW_LINE);
builder.append("Some String: ").append(this.someString);
return builder.toString();
}
}


With all of the classes defined above, it is time to turn to an executable class that can drive out use of these APIs. The relatively simple Main class demonstrates the approach of using Class->ProtectionDomain->SourceCode->getLocation() to determine the original source on the classpath of that class's definition.

Main.java


package classpathidentifier;

/**
* Main class that is separate from ClasspathIdentifier class, but uses that
* class.
*/
public class Main
{
/** My instance of SomeClass. */
private SomeClass someClass = new SomeClass(5, "Five");

/** No-arguments constructor doesn't do anything exciting. */
public Main() {}

/**
* Provide access to my instance of SomeClass.
*
* @return My instance of SomeClass.
*/
public SomeClass getSomeClass()
{
return this.someClass;
}

/**
* Main function for running me.
*
* @param arguments Command-line arguments; none expected.
*/
public static void main(final String[] arguments)
{
final Main me = new Main();
printString(ClassPathIdentifier.getClassPathSource(me));
printString(ClassPathIdentifier.getClassPathSource(me.getSomeClass()));
printString(ClassPathIdentifier.getClassPathSource(me.getSomeClass().getSomeInt()));
printString(ClassPathIdentifier.getClassPathSource(me.getSomeClass().getSomeString()));
}

/**
* Print provided String.
*
* @param stringToPrint String to be printed.
*/
private static void printString(final String stringToPrint)
{
System.out.println(Constants.NEW_LINE);
System.out.println(stringToPrint);
}
}


This class demonstrates how this approach works on different classes. When I run this Main class as part of a JAR, the output appears as shown below.



As the above screen snapshot demonstrates, the getLocation() method (and the toString()) provide a URL to the JAR file from which the class was found. The command at the top of the output window shows that this class was indeed in the JAR because that JAR is the only item on the classpath.

The next screen snapshot shows execution of the Main class using the classes directory in the classpath rather than using a JAR.



In this case, we can see that the source of the classpath entries was the directory rather than a JAR file. What happens when both (the JAR and classes directory) are on the classpath? The next screen snapshot demonstrates this with the JAR first and then with the directory first.



The last screen capture indicates that whatever class definition appears first on the classpath (when reading from left to right) is the one used by the class loader for that application.

Finally, you may have noticed that the classes Integer and String from the core Java API have null values for CodeSource.

Conclusion

There are situations when a mismatch of class definitions or multiply defined class definitions cause runtime headaches. In such cases, an approach like that demonstrated in this blog posting can be useful and provide some relief.

5 comments:

theholyjava said...

A very good post, thank you. I use this technique in my (excuse this little promotion :)) ClassLoaderViewer.jsp along with examination of the classloader chain to find out where a class was loaded from and which classloader has loaded it in an application server environment.

I've added a link to your blog to the project's page so that I don't need to explain how/why it works :)

Unknown said...

How well does this work if you use something like Java Web Start, or even the Java Plugin (Applets, JavaFX) their caches seem to go out of their way to obfuscate where and what the originating jars are.

@DustinMarx said...

Jakub,

Thanks for the post. I don't mind a little promotion as long as it is related and useful, both of which applies to your project you reference. Thanks for taking the time to provide that.

Dustin

@DustinMarx said...

osbald,

I have not tried this mechanism with either Java Web Start or with the Java Plugin, but I'll add a comment here if I get the opportunity to try that.

Dustin

@DustinMarx said...

Pankaj Batra demonstrates another way to identify the JAR file from which a class definition came in the post How to know from which jar file a class has been loaded?:

/**
* Identify JAR in which provided class definition is contained.
*
* @param clazz Class definition for which the JAR is desired.
* @return Name of the JAR which contains the provided class.
*/
public String whichJar(final Class clazz)
{
final String className = clazz.getName();
final String name = className.substring(className.lastIndexOf('.') + 1);
final String jar = clazz.getResource(name + ".class").toString();
return jar.substring(0, jar.indexOf('!'));
}