Back to your Class roots
Find a class’s runtime origin
July 11, 2003
Q: If I have an instance of class X, how can I figure out its classfile location at runtime?
A: Before I give my answer, I want to point out that your life with Java will be much easier if you get into the habit of writing strictly disk location-independent code. Load resources such as property and configuration files via Class.getResource()
and ResourceBundle.getBundle()
as much as possible and avoid java.util.File
until absolutely necessary. Not only is this very Java 2 Platform, Enterprise Edition (J2EE)-friendly, you will be surprised how much you can load from the classpath and how convenient it is.
Having said that, it is occasionally helpful during application testing and debugging to track down classes to their archives. With this kind of usage in mind, I think the following helper method provides the best possible answer within the current Java 2 Platform, Standard Edition (J2SE) APIs:
/**
* Given a Class object, attempts to find its .class location [returns null
* if no such definition can be found]. Use for testing/debugging only.
*
* @return URL that points to the class definition [null if not found].
*/
public static URL getClassLocation (final Class cls)
{
if (cls == null) throw new IllegalArgumentException ("null input: cls");
URL result = null;
final String clsAsResource = cls.getName ().replace ('.', '/').concat (".class");
final ProtectionDomain pd = cls.getProtectionDomain ();
// java.lang.Class contract does not specify if 'pd' can ever be null;
// it is not the case for Sun's implementations, but guard against null
// just in case:
if (pd != null)
{
final CodeSource cs = pd.getCodeSource ();
// 'cs' can be null depending on the classloader behavior:
if (cs != null) result = cs.getLocation ();
if (result != null)
{
// Convert a code source location into a full class file location
// for some common cases:
if ("file".equals (result.getProtocol ()))
{
try
{
if (result.toExternalForm ().endsWith (".jar") ||
result.toExternalForm ().endsWith (".zip"))
result = new URL ("jar:".concat (result.toExternalForm ())
.concat("!/").concat (clsAsResource));
else if (new File (result.getFile ()).isDirectory ())
result = new URL (result, clsAsResource);
}
catch (MalformedURLException ignore) {}
}
}
}
if (result == null)
{
// Try to find 'cls' definition as a resource; this is not
// documented to be legal, but Sun's implementations seem to allow this:
final ClassLoader clsLoader = cls.getClassLoader ();
result = clsLoader != null ?
clsLoader.getResource (clsAsResource) :
ClassLoader.getSystemResource (clsAsResource);
}
return result;
}
Your best initial shot is to get the class’s CodeSource
and its location URL from the class’s ProtectionDomain
. However, note that even though Class.getProtectionDomain()
is likely to return something non-null, there is no guarantee that the result maps to a valid non-null code source location URL (hence the various guards against null in the code above).
The details depend on the classloader’s behavior that loads and defines the class in question. The necessary class and protection domain association is established by using the five-parameter version of java.lang.ClassLoader.defineClass()
with a non-null value for the ProtectionDomain
parameter. Normally, this happens for any java.net.URLClassLoader
extension, but is not automatically guaranteed for any custom classloader implementation.
Should the first step fail, try locating the class definition as a classloader resource with a .class
extension. Java specifications do not detail whether this is allowed: arbitrary code with the ability to read entire class definitions via URLs is a potential security hole, and some JVMs forbid getResource()
for *.class
resources. However, Sun Microsystems’ JDK implementation uses this approach for normal classloading, and that seems to convey some measure of legality on it.
Finally, don’t forget that you can’t find something that doesn’t exist: a java.lang.Class
object in a JVM does not need to be backed by a real .class
file anywhere. An obvious example here is a dynamic proxy class: its byte-code definition is synthesized at runtime, and getClassLocation()
returns null for such a class. Most likely, future J2SE versions will rely even more on runtime class construction. For that and classloader-related reasons, you should use the method I’ve shown only for testing and debugging.