Dude, where’s my class?

Have you ever been stuck in a situation where you needed to find out where a particular Java class was loaded from? While this may not be the kind of problem the average CFer faces, I’m sure there are many gurus who can attest that they’ve been burned with issues of this sort. Robi Sen recently posted a bit of Java code to help solve problems like this, and reminded me of a time just a few months ago when I had to chase down a bizarre bug that turned out to be rooted in exactly this kind of JAR file hell.

On the ColdFusion team, we have a fairly rigorous checkin/build/test process. Every time anyone checks something in to the source control system, the build machine gets it, attempts to do a full build and runs a series of automated regression tests against it, just to make sure that nothing obvious has been broken. Those who break the build have the dubious honour of being named beer boy/girl, until someone else breaks the build, and claims the crown!

This has happened to me several times and, on most occasions, the problem was obvious, embarrassing, and easily fixed. However, in this particular case, I just could not figure out what was going on - I’d made changes to a couple of classes, with class A relying on a new method that I’d introduced in class B, but the build machine would constantly fail, with an error message which made it appear as though the new method on class B just did not exist. The build worked on my machine, and on other engineers’ machines, but constantly failed on the build machine.

It was Rupesh who eventually suggested that, perhaps, class B was getting loaded from somewhere other than it was supposed to be. As matters turned out, he was right. There was a dead JAR file that someone had placed on the build machine with a very old version of class B; the build machine was trying to build against that, rather than the newer version which I had checked in. Once the dead JAR file was removed, everything went through OK, and my beer boy crown passed on to the next hapless build-breaking engineer.

This bit of detective work was made possible by some interesting, albeit not very well known, Java APIs. All Java classes are defined, self-referentially, as instances of the class java.lang.Class. The java.lang.Class instance for a specific Java class can be loaded using java.lang.Class’s forName method. Every class maintains an object called ProtectionDomain, which defines the security access rules for the domain which that class belongs to. ProtectionDomain, in turn, maintains a CodeSource object that points to the location from which the code for that domain was loaded. The one gotcha to keep in mind is that the CodeSource object will be null for code which is loaded by Java’s bootstrap* classloader.

So here’s a bit of CF code which uses these APIs:

<cfset codeSource = createObject("java", "java.lang.Class").forName(url.classname).getProtectionDomain().getCodeSource()>
<cfif not isDefined("codeSource")>
<cfoutput>Code source is not defined; <b>#url.classname#</b> is probably being loaded by Java's bootstrap classloader.</cfoutput>
<cfelse>
<cfoutput>The class <b>#url.classname#</b> was loaded from <b>#codeSource.getLocation().toString()#</b></cfoutput>
</cfif>

Just pass your classname in (be sure to get the uppercase/lowercase characters right! Java’s not as forgiving as CF) with the classname URL parameter, and this little code snippet will tell you exactly where the application server loaded the class from.

There is one caveat to keep in mind, which can be good or bad, depending on where you’re coming from. The example that I’ve provided will show where a class is loaded from, from the perspective of the calling code’s classloader. 99% of the time, this is exactly the behaviour you’ll want, but for those with more advanced requirements, you may want to drop this code into a more appropriate location. This is an important consideration, since Java does allow parallel classloaders, which may load the same class from different locations. All application servers adopt this approach, since it is mandated by the JEE specifications. Sounds abstruse? Ignore this little detail then, it probably doesn’t matter to you anyway.

For those strange times when you have to debug a problem on a production system, or want to make sure that you’re using exactly the same classpath as your application server, you may want to try the approach that I’ve outlined. I hope that it will help you as much as it helped me.

Update: As Dan points out, Robi’s intention was quite different from mine - his code was written to list all classes in a given package, not to find where a class was loaded from.

* The Java bootstrap classloader is the mother of all classloaders, which loads up the Java core libraries. All application servers establish their own byzantine classloader hierarchies under the bootstrap classloader; I’ll navigate that labyrinth another time. For the curious, this theserverside.com article, although a bit dated, will give you an idea of what’s involved.