/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.mx.loading; import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Enumeration; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.io.IOException; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.NotificationBroadcaster; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.MBeanRegistration; import javax.management.ObjectName; import javax.management.MBeanServer; import javax.management.loading.MLet; import org.jboss.logging.Logger; import org.jboss.mx.util.JBossNotificationBroadcasterSupport; import org.jboss.util.Classes; import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet; /** 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. * * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>. * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>. * @version $Revision: 68584 $ * just a hint... xdoclet not really used * @jmx.name="JMImplementation:service=UnifiedLoaderRepository,name=Default" */ public class UnifiedLoaderRepository3 extends LoaderRepository implements MBeanRegistration, NotificationBroadcaster, UnifiedLoaderRepository3MBean { // Static -------------------------------------------------------- private static final Logger log = Logger.getLogger(UnifiedLoaderRepository3.class); /** Used to provide a relative ordering of UCLs based on the order in * which they are added to the repository */ private static int addedCount; /** The jmx notification behavior mode. This is set by the * org.jboss.mx.loading.UnifiedLoaderRepository.notifyMode system * property. */ // Send notification with the ClassLoader as the user data private static final int LEGACY_MODE = 0; // Send notification with the ClassLoader as the user data wrapped in a WeakReference private static final int WEAK_REFERENCE_MODE = 1; // Don't send any notifications private static final int NO_NOTIFICATION_MODE = 2; private static int NOTIFICATION_MODE; // Attributes ---------------------------------------------------- /** HashSet<UCL> of classloaders in the repository. * Access synchronized via this.classLoaders monitor. */ private CopyOnWriteArraySet classLoaders = new CopyOnWriteArraySet(); /** HashSet<UCL> of class loaders in the repository that have a dynamic * URL associated with them. Such a class loader is added to every package * class loader set in #getPackageClassLoaders(String). */ private HashSet dynamicClassLoaders = new HashSet(); /** A HashMap<ClassLoader, UCL> of foreign (non-UCL) classloaders that * have been added to the repository as the key and the value the UCL * actually used by the ULR. * Access synchronized via this.classLoaders monitor. */ private HashMap nonUCLClassLoader = new HashMap(); /** A HashSet<URL> used to check for duplicate URLs. Previously this was handled by the UCL.equals, but this caused problems with Class.forName(String, boolean, ClassLoader) caching. Access synchronized via this.classLoaders monitor. */ private HashSet classLoaderURLs = new HashSet(); /** The loaded classes cache, HashMap<String, Class>. * Access synchronized via this.classes monitor. */ private ConcurrentReaderHashMap classes = new ConcurrentReaderHashMap(); /** HashMap<UCL, HashSet<String>> class loaders to the set of class names * loaded via the UCL. * Access synchronized via this.classes monitor. */ private HashMap loaderToClassesMap = new HashMap(); /** HashMap<UCL, HashMap<String, URL>> class loaders to the set of * resource names they looked up. * Access synchronized via this.loaderToResourcesMap monitor. */ private HashMap loaderToResourcesMap = new HashMap(); /** HashMap<String, ResourceInfo(URL, UCL)> of global resources not unique * to a UCL * Access synchronized via this.loaderToResourcesMap monitor. */ private HashMap globalResources = new HashMap(); /** A HashMap<String, Set<UCL>> of package names to the set of * ClassLoaders which have classes in the package. * Access synchronized via this.packagesMap monitor. */ private ConcurrentReaderHashMap packagesMap = new ConcurrentReaderHashMap(); /** A HashMap<UCL, String[]> of class loaders to the array of pckages names * they serve * Access synchronized via this.packagesMap monitor. */ private HashMap<RepositoryClassLoader, List<String>> loaderToPackagesMap = new HashMap<RepositoryClassLoader, List<String>>(); /** * The sequenceNumber used to number notifications. */ private long sequenceNumber = 0; /** * We delegate our notification sending to a support object. */ private final JBossNotificationBroadcasterSupport broadcaster = new JBossNotificationBroadcasterSupport(); /** * The NotificationInfo we emit. */ private MBeanNotificationInfo[] info; static { // 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; } } // Public -------------------------------------------------------- public RepositoryClassLoader newClassLoader(final URL url, boolean addToRepository) throws Exception { UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null, this); if (addToRepository) this.registerClassLoader(ucl); return ucl; } public RepositoryClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository) throws Exception { UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL, this); if (addToRepository) this.registerClassLoader(ucl); return ucl; } public int getCacheSize() { return classes.size(); } public int getClassLoadersSize() { return classLoaders.size(); } public void flush() { synchronized (classes) { classes.clear(); } } public Class getCachedClass(String classname) { return (Class) classes.get(classname); } /** 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 loadClass(String name, boolean resolve, ClassLoader cl) throws ClassNotFoundException { RepositoryClassLoader rcl = getRepositoryClassLoader(cl, name); return rcl.loadClass(name, resolve); } /** Called by LoadMgr to obtain all class loaders for the given className * @return Set<UnifiedClassLoader3>, may be null */ 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; } private String getResourcePackageName(String rsrcName) { int index = rsrcName.lastIndexOf('/'); String pkgName = rsrcName; if (index > 0) pkgName = rsrcName.substring(0, index); return pkgName.replace('/', '.'); } /** Lookup a Class from the repository cache. * @param name the fully qualified class name * @return the cached Class if found, null otherwise */ public Class loadClassFromCache(String name) { Class cls = null; synchronized (classes) { cls = (Class) classes.get(name); } return cls; } /** Add a Class to the repository cache. * @param name the fully qualified class name * @param cls the Class instance * @param cl the repository UCL */ 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); } } 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; } /** * Loads a resource following the Unified ClassLoader architecture */ 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; } /** Find all resource URLs for the given name. This is entails an * exhuastive search of the repository and is an expensive operation. * * @param name the resource name * @param cl the requesting class loader * @param urls a list into which the located resource URLs will be placed */ 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) { } } } } /** As opposed to classes, resource are not looked up in a global cache, * since it is possible that 2 classloaders have the same resource name * (ejb-jar.xml), a global cache will overwrite. Instead we look in the * classloader's cache that we mantain to cycle the classloaders * @param name the resource name * @param cl the repository classloader * @return the resource URL if found, null otherwise */ private URL getResourceFromCache(String name, ClassLoader cl) { URL resource = null; synchronized (loaderToResourcesMap) { if (loaderToResourcesMap.containsKey(cl)) { HashMap resources = (HashMap) loaderToResourcesMap.get(cl); resource = (URL) resources.get(name); } } return resource; } private URL getResourceFromClassLoader(String name, ClassLoader cl) { URL resource = null; if (cl instanceof RepositoryClassLoader) { RepositoryClassLoader ucl = (RepositoryClassLoader) cl; resource = ucl.getResourceLocally(name); cacheLoadedResource(name, resource, cl); } return resource; } /** Check for a resource in the global cache * Synchronizes access to globalResources using the loaderToResourcesMap monitor * @param name * @return */ 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; } 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; } /** Update the loaderToResourcesMap * @param name the resource name * @param url the resource URL * @param cl the UCL */ private void cacheLoadedResource(String name, URL url, ClassLoader cl) { // Update the cache for this classloader only // This is used for cycling classloaders synchronized (loaderToResourcesMap) { HashMap resources = (HashMap) loaderToResourcesMap.get(cl); if (resources == null) { resources = new HashMap(); loaderToResourcesMap.put(cl, resources); } resources.put(name, url); } } /** Update cache of resources looked up via one UCL, buf found in another UCL * @param name the resource name * @param url the resource URL * @param cl the UCL */ private void cacheGlobalResource(String name, URL url, ClassLoader cl) { synchronized (loaderToResourcesMap) { globalResources.put(name, new ResourceInfo(url, cl)); } } /** 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 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; } /** A utility method that iterates over all repository class loaders and * display the class information for every UCL that contains the given * className */ 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(); } // LoaderRepository overrides ------------------------------------ /** 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. * @param className - the class to load */ 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); } /** * Loads a class from the repository, excluding the given * classloader. * * @param loader the classloader to exclude * @param className the class to load * @return the found class * @exception ClassNotFoundException when there is no such class */ public Class loadClassWithout(ClassLoader loader, String className) throws ClassNotFoundException { throw new ClassNotFoundException("NYI"); } /** * Loads a class from the repository, using the classloaders that were * registered before the given classloader. * * @param stop consult all the classloaders registered before this one * in an attempt to load a class * @param className name of the class to load * @return loaded class instance * @throws ClassNotFoundException if none of the consulted classloaders were * able to load the requested class */ public Class loadClassBefore(ClassLoader stop, String className) throws ClassNotFoundException { RepositoryClassLoader stopAt = getRepositoryClassLoader(stop, className); return stopAt.loadClassBefore(className); } /** * Get any wrapping classloader for the passed classloader * * @param cl the wrapped classloader * @return the wrapping classloader or null if not wrapped */ public RepositoryClassLoader getWrappingClassLoader(ClassLoader cl) { synchronized (classLoaders) { return (RepositoryClassLoader) nonUCLClassLoader.get(cl); } } /** Add a class loader to the repository. */ 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 } 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; } /** Add a UCL to the repository. * This sychronizes on classLoaders. * @param cl */ private void addRepositoryClassLoader(RepositoryClassLoader cl) { cl.setRepository(this); // See if this URL already exists URL url = cl.getURL(); boolean added = false; synchronized (classLoaders) { boolean exists = false; if (cl instanceof UnifiedClassLoader) exists = classLoaderURLs.contains(url); // If already present will not be added if (!exists) { if (url != null) classLoaderURLs.add(url); added = classLoaders.add(cl); } if (added) { log.debug("Adding " + cl); addedCount++; cl.setAddedOrder(addedCount); updatePackageMap(cl); } else { log.debug("Skipping duplicate " + cl); } } } private void addMLetClassLoader(MLet loader) { MLetRepositoryClassLoader rcl = new MLetRepositoryClassLoader(loader); synchronized (classLoaders) { nonUCLClassLoader.put(loader, rcl); } addRepositoryClassLoader(rcl); } private void addURLClassLoader(URLClassLoader loader) { URL[] urls = loader.getURLs(); int count = urls != null && urls.length > 0 ? urls.length : 0; URL origURL = count > 0 ? urls[0] : null; UnifiedClassLoader3 ucl3 = new UnifiedClassLoader3(origURL, origURL, this); addRepositoryClassLoader(ucl3); synchronized (classLoaders) { nonUCLClassLoader.put(loader, ucl3); } for (int i = 1; i < count; i++) { this.addClassLoaderURL(ucl3, urls[i]); } } /** Walk through the class loader URL to see what packages it is capable of handling */ private void updatePackageMap(RepositoryClassLoader cl) { try { URL url = cl.getURL(); PackageMapper listener = new PackageMapper(cl); ClassLoaderUtils.updatePackageMap(url, listener); } catch (Exception e) { if (log.isTraceEnabled()) log.trace("Failed to update pkgs for cl=" + cl, e); else log.debug("Failed to update pkgs for cl=" + cl+", "+e.getMessage()); } } /** Walk through the new URL to update the packages the ClassLoader is * capable of handling */ private void updatePackageMap(RepositoryClassLoader cl, URL url) { try { PackageMapper listener = new PackageMapper(cl); ClassLoaderUtils.updatePackageMap(url, listener); } catch (Exception e) { if (log.isTraceEnabled()) log.trace("Failed to update pkgs for cl=" + cl, e); else log.debug("Failed to update pkgs for cl=" + cl, e); } } /** Remove the class loader from the repository. This synchronizes on the * this.classLoaders */ 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); } } /** * This method provides an mbean-accessible way to add a * UnifiedClassloader, and sends a notification when it is added. * * @param ucl an <code>UnifiedClassLoader</code> value * @return a <code>LoaderRepository</code> value * * @jmx.managed-operation */ 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; } /** * @jmx.managed-operation */ public LoaderRepository getInstance() { return this; } // implementation of javax.management.NotificationBroadcaster interface /** * addNotificationListener delegates to the broadcaster object we hold. * * @param listener a <code>NotificationListener</code> value * @param filter a <code>NotificationFilter</code> value * @param handback an <code>Object</code> value * @exception IllegalArgumentException if an error occurs */ public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws IllegalArgumentException { broadcaster.addNotificationListener(listener, filter, handback); } /** * * @return <description> */ 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; } /** * removeNotificationListener delegates to our broadcaster object * * @param listener a <code>NotificationListener</code> value * @exception ListenerNotFoundException if an error occurs */ public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { broadcaster.removeNotificationListener(listener); } // MBeanRegistration public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { return name; } public void postRegister(Boolean registrationDone) { } public void preDeregister() throws Exception { } 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(); } private synchronized long getNextSequenceNumber() { return sequenceNumber++; } private RepositoryClassLoader getRepositoryClassLoader(ClassLoader cl, String name) throws ClassNotFoundException { if (cl instanceof RepositoryClassLoader) return (RepositoryClassLoader) cl; else { RepositoryClassLoader rcl = getWrappingClassLoader(cl); if (rcl == null) throw new ClassNotFoundException("Class not found " + name + " (Unknown classloader " + cl + ")"); return rcl; } } private class PackageMapper implements ClassLoaderUtils.PkgNameListener { private RepositoryClassLoader loader; PackageMapper(RepositoryClassLoader loader) { this.loader = loader; } public void addPackage(String pkgName) { // Skip the standard J2EE archive directories if( pkgName.startsWith("META-INF") || pkgName.startsWith("WEB-INF") ) return; Set<RepositoryClassLoader> pkgSet = (Set<RepositoryClassLoader>) packagesMap.get(pkgName); if( pkgSet == null ) { pkgSet = ClassLoaderUtils.newPackageSet(); packagesMap.put(pkgName, pkgSet); } if( pkgSet.contains(loader) == false ) { // Make a copy of the pkgSet to avoid concurrent mods Set<RepositoryClassLoader> newSet = ClassLoaderUtils.newPackageSet(); newSet.addAll(pkgSet); pkgSet = newSet; // Add the class loader pkgSet.add((RepositoryClassLoader)loader); packagesMap.put(pkgName, newSet); List<String> loaderPkgNames = loaderToPackagesMap.get(loader); if( loaderPkgNames == null ) { loaderPkgNames = new ArrayList<String>(); loaderToPackagesMap.put((RepositoryClassLoader)loader, loaderPkgNames); } loaderPkgNames.add(pkgName); // Anytime more than one class loader exists this may indicate a problem if( pkgSet.size() > 1 ) { log.debug("Multiple class loaders found for pkg: "+pkgName); } if( log.isTraceEnabled() ) log.trace("Indexed pkg: "+pkgName+", UCL: "+loader); } } } }