Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 4
Class.getResource() and Class.forName() use the current classloader
java.util.ResourceBundle uses the caller's current classloader
java.protocol.handler.pkgs system property are looked up in the bootstrap and system classloaders only
Those class and resource loading strategies must be the most poorly documented and least specified area of J2SE.
If your implementation is confined to a certain framework with articulated resource loading rules, stick to them. Hopefully,
the burden of making them work will be on whoever has to implement the framework (such as an application server vendor, although
they don't always get it right either). For example, always use Class.getResource() in a Web application or an Enterprise JavaBean.
In other situations, you might consider using a solution I have found useful in personal work. The following class serves as a global decision point for acquiring the best classloader to use at any given time in the application (all classes shown in this article are available with the download):
public abstract class ClassLoaderResolver
{
/**
* This method selects the best classloader instance to be used for
* class/resource loading by whoever calls this method. The decision
* typically involves choosing between the caller's current, thread context,
* system, and other classloaders in the JVM and is made by the {@link IClassLoadStrategy}
* instance established by the last call to {@link #setStrategy}.
*
* @return classloader to be used by the caller ['null' indicates the
* primordial loader]
*/
public static synchronized ClassLoader getClassLoader ()
{
final Class caller = getCallerClass (0);
final ClassLoadContext ctx = new ClassLoadContext (caller);
return s_strategy.getClassLoader (ctx);
}
public static synchronized IClassLoadStrategy getStrategy ()
{
return s_strategy;
}
public static synchronized IClassLoadStrategy setStrategy (final IClassLoadStrategy strategy)
{
final IClassLoadStrategy old = s_strategy;
s_strategy = strategy;
return old;
}
/**
* A helper class to get the call context. It subclasses SecurityManager
* to make getClassContext() accessible. An instance of CallerResolver
* only needs to be created, not installed as an actual security
* manager.
*/
private static final class CallerResolver extends SecurityManager
{
protected Class [] getClassContext ()
{
return super.getClassContext ();
}
} // End of nested class
/*
* Indexes into the current method call context with a given
* offset.
*/
private static Class getCallerClass (final int callerOffset)
{
return CALLER_RESOLVER.getClassContext () [CALL_CONTEXT_OFFSET +
callerOffset];
}
private static IClassLoadStrategy s_strategy; // initialized in <clinit>
private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
private static final CallerResolver CALLER_RESOLVER; // set in <clinit>
static
{
try
{
// This can fail if the current SecurityManager does not allow
// RuntimePermission ("createSecurityManager"):
CALLER_RESOLVER = new CallerResolver ();
}
catch (SecurityException se)
{
throw new RuntimeException ("ClassLoaderResolver: could not create CallerResolver: " + se);
}
s_strategy = new DefaultClassLoadStrategy ();
}
} // End of class.
You acquire a classloader reference by calling the ClassLoaderResolver.getClassLoader() static method and use the result to load classes and resources via the normal java.lang.ClassLoader API. Alternatively, you can use this ResourceLoader API as a drop-in replacement for java.lang.ClassLoader:
public abstract class ResourceLoader
{
/**
* @see java.lang.ClassLoader#loadClass(java.lang.String)
*/
public static Class loadClass (final String name)
throws ClassNotFoundException
{
final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
return Class.forName (name, false, loader);
}
/**
* @see java.lang.ClassLoader#getResource(java.lang.String)
*/
public static URL getResource (final String name)
{
final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
if (loader != null)
return loader.getResource (name);
else
return ClassLoader.getSystemResource (name);
}
... more methods ...
} // End of class
The decision of what constitutes the best classloader to use is factored out into a pluggable component implementing the IClassLoadStrategy interface:
public interface IClassLoadStrategy
{
ClassLoader getClassLoader (ClassLoadContext ctx);
} // End of interface
To help IClassLoadStrategy make its decision, it is given a ClassLoadContext object:
public class ClassLoadContext
{
public final Class getCallerClass ()
{
return m_caller;
}
ClassLoadContext (final Class caller)
{
m_caller = caller;
}
private final Class m_caller;
} // End of class
ClassLoadContext.getCallerClass() returns the class whose code calls into ClassLoaderResolver or ResourceLoader. This is so that the strategy implementation can figure out the caller's classloader (the context loader is always available
as Thread.currentThread().getContextClassLoader()). Note that the caller is determined statically; thus, my API does not require existing business methods to be augmented
with extra Class parameters and is suitable for static methods and initializers as well. You can augment this context object with other attributes
that make sense in your deployment situation.
All of this should look like a familiar Strategy design pattern to you. The idea is that decisions like "always context loader" or "always current loader" get separated from the rest of your implementation logic. It is hard to know ahead of time which strategy will be the right one, and with this design, you can always change the decision later.
I have a default strategy implementation that should work correctly in 95 percent of real-life situations:
public class DefaultClassLoadStrategy implements IClassLoadStrategy
{
public ClassLoader getClassLoader (final ClassLoadContext ctx)
{
final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();
final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();
ClassLoader result;
// If 'callerLoader' and 'contextLoader' are in a parent-child
// relationship, always choose the child:
if (isChild (contextLoader, callerLoader))
result = callerLoader;
else if (isChild (callerLoader, contextLoader))
result = contextLoader;
else
{
// This else branch could be merged into the previous one,
// but I show it here to emphasize the ambiguous case:
result = contextLoader;
}
final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();
// Precaution for when deployed as a bootstrap or extension class:
if (isChild (result, systemLoader))
result = systemLoader;
return result;
}
... more methods ...
} // End of class
The logic above should be easy to follow. If the caller's current and context classloaders are in a parent-child relationship, I always choose the child. The set of resources visible to a child loader is normally a superset of classes visible to its parent, so this feels like the right decision as long as everybody plays by J2SE delegation rules.