A repository of class loaders that form a flat namespace of classes
and resources. This version uses UnifiedClassLoader3 instances. Class
and resource loading is synchronized by the acquiring the monitor to the
associated repository structure monitor. See the variable javadoc comments
for what monitor is used to access a given structure.
| Method from org.jboss.mx.loading.UnifiedLoaderRepository3 Detail: |
public void addClassLoader(ClassLoader loader) {
// if you come to us as UCL we send you straight to the orbit
if (loader instanceof RepositoryClassLoader)
addRepositoryClassLoader((RepositoryClassLoader) loader);
else if (loader instanceof MLet)
{
addMLetClassLoader((MLet) loader);
}
else if (loader instanceof URLClassLoader)
{
addURLClassLoader((URLClassLoader) loader);
}
else
{
log.warn("Tried to add non-URLClassLoader. Ignored");
} // end of else
}
Add a class loader to the repository. |
public boolean addClassLoaderURL(ClassLoader cl,
URL url) {
RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
boolean added = false;
synchronized (classLoaders)
{
// Strip any query parameter
String query = url.getQuery();
if (query != null)
{
String ext = url.toExternalForm();
String ext2 = ext.substring(0, ext.length() - query.length() - 1);
try
{
url = new URL(ext2);
}
catch (MalformedURLException e)
{
log.warn("Failed to strip query from: " + url, e);
}
}
// See if the URL is associated with a class loader
if (classLoaderURLs.contains(url) == false)
{
updatePackageMap(ucl, url);
classLoaderURLs.add(url);
added = true;
// Check for a dynamic URL
if (query != null && query.indexOf("dynamic=true") >= 0)
dynamicClassLoaders.add(ucl);
}
}
return added;
}
|
public void addNotificationListener(NotificationListener listener,
NotificationFilter filter,
Object handback) throws IllegalArgumentException {
broadcaster.addNotificationListener(listener, filter, handback);
}
addNotificationListener delegates to the broadcaster object we hold. |
public void cacheLoadedClass(String name,
Class cls,
ClassLoader cl) {
synchronized (classes)
{
// Update the global cache
Object prevClass = classes.put(name, cls);
if (log.isTraceEnabled())
{
log.trace("cacheLoadedClass, classname: " + name + ", class: " + cls
+ ", ucl: " + cl + ", prevClass: " + prevClass);
}
// Update the cache for this classloader
// This is used to cycling classloaders
HashSet loadedClasses = (HashSet) loaderToClassesMap.get(cl);
if (loadedClasses == null)
{
loadedClasses = new HashSet();
loaderToClassesMap.put(cl, loadedClasses);
}
loadedClasses.add(name);
}
}
Add a Class to the repository cache. |
public String displayClassInfo(String className) {
/* We have to find the class as a resource as we don't want to invoke
loadClass(name) and cause the side-effect of loading new classes.
*/
String classRsrcName = className.replace('.", '/") + ".class";
int count = 0;
Class loadedClass = this.loadClassFromCache(className);
StringBuffer results = new StringBuffer(className + " Information\n");
if (loadedClass != null)
{
results.append("Repository cache version:");
Classes.displayClassInfo(loadedClass, results);
}
else
{
results.append("Not loaded in repository cache\n");
}
Set tmp = classLoaders;
for (Iterator iter = tmp.iterator(); iter.hasNext();)
{
URLClassLoader cl = (URLClassLoader) iter.next();
URL classURL = cl.findResource(classRsrcName);
if (classURL != null)
{
results.append("\n\n### Instance" + count + " found in UCL: " + cl + "\n");
count++;
}
}
// Also look to the parent class loaders of the TCL
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
URLClassLoader[] stack = ClassLoaderUtils.getClassLoaderStack(tcl);
for (int s = 0; s < stack.length; s++)
{
URLClassLoader cl = stack[s];
URL classURL = cl.findResource(classRsrcName);
if (classURL != null)
{
results.append("\n\n### Instance" + count + " via UCL: " + cl + "\n");
count++;
}
}
return results.toString();
}
A utility method that iterates over all repository class loaders and
display the class information for every UCL that contains the given
className |
public void flush() {
synchronized (classes)
{
classes.clear();
}
}
|
public int getCacheSize() {
return classes.size();
}
|
public Class getCachedClass(String classname) {
return (Class) classes.get(classname);
}
|
public int getClassLoadersSize() {
return classLoaders.size();
}
|
public LoaderRepository getInstance() {
return this;
}
|
public MBeanNotificationInfo[] getNotificationInfo() {
if (info == null)
{
if (NOTIFICATION_MODE != NO_NOTIFICATION_MODE)
{
info = new MBeanNotificationInfo[]{
new MBeanNotificationInfo(new String[]{"CLASSLOADER_ADDED"},
"javax.management.Notification",
"Notification that a classloader has been added to the extensible classloader"),
new MBeanNotificationInfo(new String[]{"CLASS_REMOVED"},
"javax.management.Notification",
"Notification that a class has been removed from the extensible classloader")
};
}
else
{
info = new MBeanNotificationInfo[0];
}
}
return info;
}
|
public Set getPackageClassLoaders(String className) {
String pkgName = ClassLoaderUtils.getPackageName(className);
// Don't try to load java.* classes, it is impossible
if (pkgName.startsWith("java."))
return null;
Set pkgSet = (Set) packagesMap.get(pkgName);
if (dynamicClassLoaders.size() > 0)
{
Set< RepositoryClassLoader > newSet = ClassLoaderUtils.newPackageSet();
if(pkgSet != null)
newSet.addAll(pkgSet);
pkgSet = newSet;
pkgSet.addAll(dynamicClassLoaders);
}
return pkgSet;
}
Called by LoadMgr to obtain all class loaders for the given className |
public URL getResource(String name,
ClassLoader cl) {
// getResource() calls are not synchronized on the classloader from JDK code.
// First ask the cache (of the calling classloader)
URL resource = getResourceFromCache(name, cl);
// The resource was already loaded by the calling classloader, we're done
if (resource != null)
return resource;
// Not found in cache, ask the calling classloader
resource = getResourceFromClassLoader(name, cl);
// The calling classloader sees the resource, we're done
if (resource != null)
return resource;
// Not found in classloader, ask the global cache
resource = getResourceFromGlobalCache(name);
// The cache has it, we are done
if (resource != null)
return resource;
// Not visible in global cache, iterate on all classloaders
resource = getResourceFromRepository(name, cl);
// Some other classloader sees the resource, we're done
if (resource != null)
return resource;
// This resource is not visible
return null;
}
Loads a resource following the Unified ClassLoader architecture |
protected URL getResourceFromGlobalCache(String name) {
ResourceInfo ri = null;
synchronized (loaderToResourcesMap)
{
ri = (ResourceInfo) globalResources.get(name);
}
URL resource = null;
if (ri != null)
resource = ri.url;
return resource;
}
Check for a resource in the global cache
Synchronizes access to globalResources using the loaderToResourcesMap monitor |
protected URL getResourceFromRepository(String name,
ClassLoader cl) {
// Get the set of class loaders from the packages map
String pkgName = getResourcePackageName(name);
Iterator i = null;
Set pkgSet = (Set) this.packagesMap.get(pkgName);
if (pkgSet != null)
{
i = pkgSet.iterator();
}
if (i == null)
{
// If no pkg match was found just go through all class loaders
i = classLoaders.iterator();
}
URL url = null;
while (i.hasNext() == true)
{
ClassLoader classloader = (ClassLoader) i.next();
if (classloader.equals(cl))
{
continue;
}
if (classloader instanceof RepositoryClassLoader)
{
url = ((RepositoryClassLoader) classloader).getResourceLocally(name);
if (url != null)
{
cacheLoadedResource(name, url, classloader);
cacheGlobalResource(name, url, classloader);
break;
}
else
{
// Do nothing, go on with next classloader
}
}
}
return url;
}
|
public void getResources(String name,
ClassLoader cl,
List urls) {
// Go through all class loaders
Iterator iter = classLoaders.iterator();
while (iter.hasNext() == true)
{
ClassLoader nextCL = (ClassLoader) iter.next();
if (nextCL instanceof RepositoryClassLoader)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) nextCL;
try
{
Enumeration resURLs = ucl.findResourcesLocally(name);
while (resURLs.hasMoreElements())
{
Object res = resURLs.nextElement();
urls.add(res);
}
}
catch (IOException ignore)
{
}
}
}
}
Find all resource URLs for the given name. This is entails an
exhuastive search of the repository and is an expensive operation. |
public URL[] getURLs() {
HashSet classpath = new HashSet();
Set tmp = classLoaders;
for (Iterator iter = tmp.iterator(); iter.hasNext();)
{
Object obj = iter.next();
if (obj instanceof RepositoryClassLoader)
{
RepositoryClassLoader cl = (RepositoryClassLoader) obj;
URL[] urls = cl.getClasspath();
int length = urls != null ? urls.length : 0;
for (int u = 0; u < length; u++)
{
URL path = urls[u];
classpath.add(path);
}
}
} // for all ClassLoaders
URL[] cp = new URL[classpath.size()];
classpath.toArray(cp);
return cp;
}
This is a utility method a listing of the URL for all UnifiedClassLoaders
associated with the repository. It is never called in response to
class or resource loading. |
public RepositoryClassLoader getWrappingClassLoader(ClassLoader cl) {
synchronized (classLoaders)
{
return (RepositoryClassLoader) nonUCLClassLoader.get(cl);
}
}
Get any wrapping classloader for the passed classloader |
public Class loadClass(String className) throws ClassNotFoundException {
// Try to load from a UCL in the ULR first
ClassLoader scl = Thread.currentThread().getContextClassLoader();
ClassLoader ucl = null;
if (classLoaders.size() > 0)
ucl = (ClassLoader) this.classLoaders.iterator().next();
try
{
if (ucl != null)
return loadClass(className, false, ucl);
}
catch (ClassNotFoundException ignore)
{
// go on and try the next loader
}
try
{
// If there is no class try the TCL
return scl.loadClass(className);
}
catch (ClassNotFoundException e)
{
// go on and try the next loader
}
// at last try a native
Class clazz = getNativeClassForName(className);
if (clazz != null) return clazz;
throw new ClassNotFoundException(className);
}
First tries to load from any UCL in the ULR, and if the
class is not found, next tries the current thread context
class loader. |
public Class loadClass(String name,
boolean resolve,
ClassLoader cl) throws ClassNotFoundException {
RepositoryClassLoader rcl = getRepositoryClassLoader(cl, name);
return rcl.loadClass(name, resolve);
}
Unlike other implementations of LoaderRepository, this method does
nothing but ask the UnifiedClassLoader3 to load the class as UCL3s
do not use this method. |
public Class loadClassBefore(ClassLoader stop,
String className) throws ClassNotFoundException {
RepositoryClassLoader stopAt = getRepositoryClassLoader(stop, className);
return stopAt.loadClassBefore(className);
}
Loads a class from the repository, using the classloaders that were
registered before the given classloader. |
public Class loadClassFromCache(String name) {
Class cls = null;
synchronized (classes)
{
cls = (Class) classes.get(name);
}
return cls;
}
Lookup a Class from the repository cache. |
Class loadClassFromClassLoader(String name,
boolean resolve,
RepositoryClassLoader cl) {
try
{
Class cls = cl.loadClassLocally(name, resolve);
cacheLoadedClass(name, cls, cl);
return cls;
}
catch (ClassNotFoundException x)
{
// The class is not visible by the calling classloader
if(log.isTraceEnabled())
log.trace("Failed to load class: "+name, x);
}
return null;
}
|
public Class loadClassWithout(ClassLoader loader,
String className) throws ClassNotFoundException {
throw new ClassNotFoundException("NYI");
}
Loads a class from the repository, excluding the given
classloader. |
public RepositoryClassLoader newClassLoader(URL url,
boolean addToRepository) throws Exception {
// JBAS-4593 notification behavior
String value = ClassToStringAction.getProperty("org.jboss.mx.loading.UnifiedLoaderRepository.notifyMode", "0");
NOTIFICATION_MODE = Integer.valueOf(value).intValue();
switch(NOTIFICATION_MODE)
{
case LEGACY_MODE:
case WEAK_REFERENCE_MODE:
case NO_NOTIFICATION_MODE:
break;
default:
log.warn("Invalid org.jboss.mx.loading.UnifiedLoaderRepository.notifyMode("
+value+"), defaulting to LEGACY_MODE");
NOTIFICATION_MODE = LEGACY_MODE;
break;
}
UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null, this);
if (addToRepository)
this.registerClassLoader(ucl);
return ucl;
}
|
public RepositoryClassLoader newClassLoader(URL url,
URL origURL,
boolean addToRepository) throws Exception {
UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL, this);
if (addToRepository)
this.registerClassLoader(ucl);
return ucl;
}
|
public void postDeregister() {
log.debug("postDeregister, clearing all references");
classLoaders.clear();
dynamicClassLoaders.clear();
nonUCLClassLoader.clear();
classLoaderURLs.clear();
classes.clear();
loaderToClassesMap.clear();
loaderToResourcesMap.clear();
globalResources.clear();
packagesMap.clear();
loaderToPackagesMap.clear();
}
|
public void postRegister(Boolean registrationDone) {
}
|
public void preDeregister() throws Exception {
}
|
public ObjectName preRegister(MBeanServer server,
ObjectName name) throws Exception {
return name;
}
|
public LoaderRepository registerClassLoader(RepositoryClassLoader ucl) {
addClassLoader(ucl);
if (NOTIFICATION_MODE == LEGACY_MODE || NOTIFICATION_MODE == WEAK_REFERENCE_MODE)
{
Notification msg = new Notification(CLASSLOADER_ADDED, this, getNextSequenceNumber());
if (NOTIFICATION_MODE == WEAK_REFERENCE_MODE)
msg.setUserData(new WeakReference(ucl));
else
msg.setUserData(ucl);
broadcaster.sendNotification(msg);
}
return this;
}
This method provides an mbean-accessible way to add a
UnifiedClassloader, and sends a notification when it is added. |
public void removeClassLoader(ClassLoader loader) {
ArrayList removeNotifications = new ArrayList();
ClassLoader cl = loader;
synchronized (classLoaders)
{
if ((loader instanceof RepositoryClassLoader) == false)
{
cl = (ClassLoader) nonUCLClassLoader.remove(loader);
}
if (cl instanceof RepositoryClassLoader)
{
RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
if (getTranslator() != null)
getTranslator().unregisterClassLoader(ucl);
URL[] urls = ucl.getClasspath();
for (int u = 0; u < urls.length; u++)
classLoaderURLs.remove(urls[u]);
}
boolean dynamic = dynamicClassLoaders.remove(cl);
boolean removed = classLoaders.remove(cl);
log.debug("UnifiedLoaderRepository removed(" + removed + ") " + cl);
// Take care also of the cycling mapping for classes
HashSet loadedClasses = null;
boolean hasLoadedClasses = false;
synchronized (classes)
{
hasLoadedClasses = loaderToClassesMap.containsKey(cl);
if (hasLoadedClasses)
loadedClasses = (HashSet) loaderToClassesMap.remove(cl);
// This classloader has loaded at least one class
if (loadedClasses != null)
{
// Notify that classes are about to be removed
for (Iterator iter = loadedClasses.iterator(); iter.hasNext();)
{
String className = (String) iter.next();
Notification n = new Notification(CLASS_REMOVED, this,
getNextSequenceNumber(), className);
removeNotifications.add(n);
}
// Remove the classes from the global cache
for (Iterator i = loadedClasses.iterator(); i.hasNext();)
{
String cls = (String) i.next();
this.classes.remove(cls);
}
}
}
// Take care also of the cycling mapping for resources
synchronized (loaderToResourcesMap)
{
if (loaderToResourcesMap.containsKey(cl))
{
HashMap resources = (HashMap) loaderToResourcesMap.remove(cl);
// Remove the resources from the global cache that are from this classloader
if (resources != null)
{
for (Iterator i = resources.keySet().iterator(); i.hasNext();)
{
String name = (String) i.next();
ResourceInfo ri = (ResourceInfo) globalResources.get(name);
if (ri != null && ri.cl == cl)
globalResources.remove(name);
}
}
}
}
// Clean up the package name to class loader mapping
if (dynamic == false)
{
List< String > pkgNames = loaderToPackagesMap.remove(cl);
if( pkgNames != null )
{
for(String pkgName : pkgNames)
{
Set pkgSet = (Set) packagesMap.get(pkgName);
if (pkgSet != null)
{
Set< RepositoryClassLoader > newSet = ClassLoaderUtils.newPackageSet();
newSet.addAll(pkgSet);
pkgSet = newSet;
pkgSet.remove(cl);
packagesMap.put(pkgName, newSet);
if (pkgSet.isEmpty())
packagesMap.remove(pkgName);
}
}
}
}
else
{
// A dynamic classloader could end up in any package set
loaderToPackagesMap.remove(cl);
for (Iterator i = packagesMap.entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = (Map.Entry) i.next();
Set pkgSet = (Set) entry.getValue();
if(pkgSet.contains(cl))
{
if(pkgSet.size() > 1)
{
Set< RepositoryClassLoader > newSet = ClassLoaderUtils.newPackageSet();
newSet.addAll(pkgSet);
newSet.remove(cl);
packagesMap.put(entry.getKey(), newSet);
}
else
{
pkgSet = Collections.emptySet();
}
}
if (pkgSet.isEmpty())
i.remove();
}
}
}
// Send the class removal notfications outside of the synchronized block
for (int n = 0; n < removeNotifications.size(); n++)
{
Notification msg = (Notification) removeNotifications.get(n);
broadcaster.sendNotification(msg);
}
if (NOTIFICATION_MODE == LEGACY_MODE || NOTIFICATION_MODE == WEAK_REFERENCE_MODE)
{
Notification msg = new Notification(CLASSLOADER_REMOVED, this, getNextSequenceNumber());
if (NOTIFICATION_MODE == WEAK_REFERENCE_MODE)
msg.setUserData(new WeakReference(cl));
else
msg.setUserData(cl);
broadcaster.sendNotification(msg);
}
}
Remove the class loader from the repository. This synchronizes on the
this.classLoaders |
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener);
}
removeNotificationListener delegates to our broadcaster object |