[Jboss-cvs] JBossAS SVN: r56381 - branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Mon Aug 28 22:02:36 EDT 2006
Author: scott.stark at jboss.org
Date: 2006-08-28 22:02:32 -0400 (Mon, 28 Aug 2006)
New Revision: 56381
Added:
branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/ClassLoadingTaskDCL.java
branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/DomainClassLoaderUCLImpl.java
branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/LoadMgrDCL.java
branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCL.java
branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCLMBean.java
Log:
Initial port of ucl/ulr to DomainClassLoader/ClassLoadingDomain
Copied: branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/ClassLoadingTaskDCL.java (from rev 56376, trunk/jmx/src/main/org/jboss/mx/loading/ClassLoadingTask.java)
===================================================================
--- trunk/jmx/src/main/org/jboss/mx/loading/ClassLoadingTask.java 2006-08-29 00:48:36 UTC (rev 56376)
+++ branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/ClassLoadingTaskDCL.java 2006-08-29 02:02:32 UTC (rev 56381)
@@ -0,0 +1,254 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.security.CodeSource;
+import java.security.ProtectionDomain;
+import java.util.Comparator;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
+import org.jboss.logging.Logger;
+
+/** An encapsulation of a UCL3.loadClass task.
+ * @author Scott.Stark at jboss.org
+ * @version $Revision$
+*/
+public class ClassLoadingTaskDCL
+{
+ protected static Logger log = Logger.getLogger(ClassLoadingTaskDCL.class);
+ protected static Comparator taskComparator = new ThreadTaskComparator();
+
+ public static final int FOUND_CLASS_LOADER = 1;
+ public static final int NEXT_EVENT = 2;
+ public static final int WAIT_ON_EVENT = 3;
+ public static final int FINISHED = 4;
+
+ protected String classname;
+ protected Thread requestingThread;
+ protected LegacyDomainClassLoader requestingClassLoader;
+ protected Class loadedClass;
+ protected int loadOrder = Integer.MAX_VALUE;
+ protected int stopOrder = Integer.MAX_VALUE;
+ protected Throwable loadException;
+ /** The number of ThreadTasks remaining */
+ protected int threadTaskCount;
+ /** The state of the requestingThread */
+ protected int state;
+ /** The Logger trace level flag */
+ protected boolean trace;
+
+ protected int numCCE;
+
+ /** Compare ThreadTask first based on their order ivar, and then the
+ * relative ordering with which their UCLs were added to the ULR.
+ */
+ static class ThreadTaskComparator implements Comparator
+ {
+ public int compare(Object o1, Object o2)
+ {
+ ThreadTask t1 = (ThreadTask) o1;
+ ThreadTask t2 = (ThreadTask) o2;
+ int compare = t1.order - t2.order;
+ /*
+ if( compare == 0 )
+ {
+ compare = t1.ucl.getAddedOrder() - t2.ucl.getAddedOrder();
+ }
+ */
+ return compare;
+ }
+ }
+
+ /** An ecapsulation of a <Thread, UCL3> task used when requestingClassLoader
+ * needs to ask another UCL3 to perform the class loading.
+ */
+ class ThreadTask
+ {
+ /** The class loader for the classname package */
+ LegacyDomainClassLoader ucl;
+ /** The thread that owns the ucl monitor */
+ Thread t;
+ /** The relative order of the task. If o0 < o1 then the class loaded
+ by task o0 is preferred to o1.
+ */
+ int order;
+ boolean releaseInNextTask;
+
+ ThreadTask(LegacyDomainClassLoader ucl, Thread t, int order,
+ boolean releaseInNextTask)
+ {
+ this.ucl = ucl;
+ this.t = t;
+ this.order = order;
+ this.releaseInNextTask = releaseInNextTask;
+ }
+
+ public String toString()
+ {
+ return "{t="+t+", ucl="+ucl+", name="+classname
+ +", requestingThread="+requestingThread
+ +", order="+order+", releaseInNextTask="+releaseInNextTask
+ +"}";
+ }
+
+ String getClassname()
+ {
+ return classname;
+ }
+ Class getLoadedClass()
+ {
+ return loadedClass;
+ }
+ ClassLoadingTaskDCL getLoadTask()
+ {
+ return ClassLoadingTaskDCL.this;
+ }
+
+ void run() throws ClassNotFoundException
+ {
+ Class theClass = null;
+ try
+ {
+ if( loadedClass == null )
+ {
+ theClass = ucl.loadClassLocally(classname, false);
+ setLoadedClass(theClass, order);
+ }
+ else if( trace )
+ {
+ log.trace("Already found class("+loadedClass+"), skipping loadClassLocally");
+ }
+ }
+ finally
+ {
+ ;//setLoadedClass(theClass, order);
+ }
+ }
+ }
+
+ protected ClassLoadingTaskDCL(String classname, LegacyDomainClassLoader requestingClassLoader,
+ Thread requestingThread)
+ {
+ this(classname, requestingClassLoader, requestingThread, Integer.MAX_VALUE);
+ }
+
+ protected ClassLoadingTaskDCL(String classname, LegacyDomainClassLoader requestingClassLoader,
+ Thread requestingThread, int stopAt)
+ {
+ this.requestingThread = requestingThread;
+ this.requestingClassLoader = requestingClassLoader;
+ this.classname = classname;
+ this.stopOrder = stopAt;
+ this.trace = log.isTraceEnabled();
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer(super.toString());
+ buffer.append('{');
+ buffer.append("classname: "+classname);
+ buffer.append(", requestingThread: "+requestingThread);
+ buffer.append(", requestingClassLoader: "+requestingClassLoader);
+ buffer.append(", loadedClass: "+loadedClass);
+ ClassToStringAction.toString(loadedClass, buffer);
+ buffer.append(", loadOrder: "+loadOrder);
+ buffer.append(", loadException: "+loadException);
+ buffer.append(", threadTaskCount: "+threadTaskCount);
+ buffer.append(", state: "+state);
+ buffer.append(", #CCE: "+numCCE);
+ buffer.append('}');
+ if( trace && loadException != null )
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ loadException.printStackTrace(pw);
+ buffer.append("loadException details:\n");
+ buffer.append(sw.toString());
+ }
+ return buffer.toString();
+ }
+
+ ThreadTask newThreadTask(LegacyDomainClassLoader ucl, Thread t, int order,
+ boolean reschedule, boolean releaseInNextTask)
+ {
+ // Only update the threadTaskCount if this is not a reschedule
+ if( reschedule == false )
+ threadTaskCount ++;
+ return new ThreadTask(ucl, t, order, releaseInNextTask);
+ }
+
+ synchronized void setLoadError(Throwable t)
+ {
+ this.threadTaskCount--;
+ if( trace )
+ log.trace("setLoadedError, error="+t);
+ loadException = t;
+ }
+
+
+ /** This is called from run on success or failure to mark the end
+ * of the load attempt. This must decrement the threadTaskCount or
+ * the ClassLoadingTaskDCL will never complete.
+ */
+ private synchronized void setLoadedClass(Class theClass, int order)
+ {
+ this.threadTaskCount --;
+ if( trace )
+ log.trace("setLoadedClass, theClass="+theClass+", order="+order);
+
+ // Warn about duplicate classes
+ if( this.loadedClass != null && order == loadOrder && theClass != null )
+ {
+ StringBuffer tmp = new StringBuffer("Duplicate class found: "+classname);
+ tmp.append('\n');
+ ProtectionDomain pd = this.loadedClass.getProtectionDomain();
+ CodeSource cs = pd != null ? pd.getCodeSource() : null;
+ tmp.append("Current CS: "+cs);
+ tmp.append('\n');
+ pd = theClass.getProtectionDomain();
+ cs = pd != null ? pd.getCodeSource() : null;
+ tmp.append("Duplicate CS: "+cs);
+ log.warn(tmp.toString());
+ }
+
+ // Accept the lowest order source of the class
+ if( theClass != null )
+ {
+ if( loadedClass == null || order <= loadOrder )
+ {
+ this.loadedClass = theClass;
+ this.loadOrder = order;
+ }
+ else
+ {
+ ProtectionDomain pd = this.loadedClass.getProtectionDomain();
+ CodeSource cs = pd != null ? pd.getCodeSource() : null;
+ ProtectionDomain pd2 = theClass.getProtectionDomain();
+ CodeSource cs2 = pd != null ? pd2.getCodeSource() : null;
+ log.debug("Ignoring source of: "+classname+" from CodeSource: "+cs2
+ +", due to order("+order+">="+loadOrder+"), "
+ +"accepted CodeSource: "+cs);
+ }
+ }
+ }
+}
Added: branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/DomainClassLoaderUCLImpl.java
===================================================================
--- branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/DomainClassLoaderUCLImpl.java 2006-08-29 02:01:28 UTC (rev 56380)
+++ branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/DomainClassLoaderUCLImpl.java 2006-08-29 02:02:32 UTC (rev 56381)
@@ -0,0 +1,352 @@
+package org.jboss.mx.loading;
+
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Enumeration;
+
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.jboss.logging.Logger;
+import org.jboss.util.loading.Translatable;
+
+/**
+* A ClassLoader which loads classes from a single URL in conjunction with
+* the {@link LoaderRepositoryDomain}. Notice that this classloader does
+* not work independently of the repository. A repository reference
+* must be provided via the constructor or the classloader must be explicitly
+* registered to the repository before any attempt to load a class.
+*
+* At this point this is little more than an abstract class maintained as the
+* interface for class loaders as the algorithm of the UnifiedLoaderRepositoryDomain
+* fails with deadlocks, and several other class loading exceptions in multi-
+* threaded environments.
+*
+* @author <a href="marc.fleury at jboss.org">Marc Fleury</a>
+* @author <a href="christoph.jung at jboss.org">Christoph G. Jung</a>
+* @author <a href="scott.stark at jboss.org">Scott Stark</a>
+* @author <a href="juha at jboss.org">Juha Lindfors</a>
+* @author <a href="bill at jboss.org">Bill Burke</a>
+* @version <tt>$Revision: 44243 $</tt>
+*/
+public class DomainClassLoaderUCLImpl extends LegacyDomainClassLoader
+ implements UnifiedClassLoaderMBean, Translatable
+{
+ // Static --------------------------------------------------------
+
+ private static final Logger log = Logger.getLogger(UnifiedClassLoader.class);
+
+ // Attributes ----------------------------------------------------
+
+ /** One URL per ClassLoader in our case */
+ protected URL url = null;
+ /** An optional URL from which url may have been copied. It is used to
+ allow the security permissions to be based on a static url namespace. */
+ protected URL origURL = null;
+
+ // Constructors --------------------------------------------------
+ /**
+ * Construct a <tt>UnifiedClassLoader</tt> without registering it to the
+ * classloader repository.
+ *
+ * @param url the single URL to load classes from.
+ */
+ public DomainClassLoaderUCLImpl(URL url)
+ {
+ this(url, (URL) null);
+ }
+ /**
+ * Construct a <tt>UnifiedClassLoader</tt> without registering it to the
+ * classloader repository.
+ *
+ * @param url the single URL to load classes from.
+ * @param origURL the possibly null original URL from which url may
+ * be a local copy or nested jar.
+ */
+ public DomainClassLoaderUCLImpl(URL url, URL origURL)
+ {
+ this(url, origURL, UnifiedClassLoader.class.getClassLoader());
+ }
+
+ /** Construct a UnifiedClassLoader without registering with the
+ * classloader repository.
+ *
+ * @param url the single URL to load classes from.
+ * @param origURL the possibly null original URL from which url may
+ * be a local copy or nested jar.
+ * @param parent the parent class loader to use
+ */
+ public DomainClassLoaderUCLImpl(URL url, URL origURL, ClassLoader parent)
+ {
+ super(url == null ? new URL[]{} : new URL[] {url}, parent);
+
+ if (log.isTraceEnabled())
+ log.trace("New jmx UCL with url " + url);
+ this.url = url;
+ this.origURL = origURL;
+ }
+
+ /**
+ * Construct a <tt>UnifiedClassLoader</tt> and registers it to the given
+ * repository.
+ *
+ * @param url The single URL to load classes from.
+ * @param repository the repository this classloader delegates to
+ */
+ public DomainClassLoaderUCLImpl(URL url, LoaderRepositoryDomain repository)
+ {
+ this(url, null, repository);
+ }
+ /**
+ * Construct a <tt>UnifiedClassLoader</tt> and registers it to the given
+ * repository.
+ * @param url The single URL to load classes from.
+ * @param origURL the possibly null original URL from which url may
+ * be a local copy or nested jar.
+ * @param repository the repository this classloader delegates to
+ * be a local copy or nested jar.
+ */
+ public DomainClassLoaderUCLImpl(URL url, URL origURL, LoaderRepositoryDomain repository)
+ {
+ this(url, origURL);
+
+ // set the repository reference
+ this.setDomain(repository);
+
+ // register this loader to the given repository
+ repository.addClassLoader(this);
+ }
+
+ /**
+ * UnifiedClassLoader constructor that can be used to
+ * register with a particular Loader Repository identified by ObjectName.
+ *
+ * @param url an <code>URL</code> value
+ * @param server a <code>MBeanServer</code> value
+ * @param repositoryName an <code>ObjectName</code> value
+ * @exception Exception if an error occurs
+ */
+ public DomainClassLoaderUCLImpl(final URL url, final MBeanServer server,
+ final ObjectName repositoryName) throws Exception
+ {
+ this(url, null, server, repositoryName);
+ }
+ /**
+ * UnifiedClassLoader constructor that can be used to
+ * register with a particular Loader Repository identified by ObjectName.
+ *
+ * @param url an <code>URL</code> value
+ * @param origURL the possibly null original URL from which url may
+ * be a local copy or nested jar.
+ * @param server a <code>MBeanServer</code> value
+ * @param repositoryName an <code>ObjectName</code> value
+ * @exception Exception if an error occurs
+ */
+ public DomainClassLoaderUCLImpl(final URL url, final URL origURL,
+ final MBeanServer server, final ObjectName repositoryName) throws Exception
+ {
+ this(url, origURL);
+ LoaderRepositoryDomain rep = (LoaderRepositoryDomain)server.invoke(repositoryName,
+ "registerClassLoader",
+ new Object[] {this},
+ new String[] {getClass().getName()});
+ this.setDomain(rep);
+ }
+
+ // Public --------------------------------------------------------
+
+ /** Obtain the ObjectName under which the UCL can be registered with the
+ JMX server. This creates a name of the form "jmx.loading:UCL=hashCode"
+ since we don't currently care that UCL be easily queriable.
+ */
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ String name = "jmx.loading:UCL="+Integer.toHexString(super.hashCode());
+ return new ObjectName(name);
+ }
+
+ public void unregister()
+ {
+ super.unregister();
+ this.origURL = null;
+ this.url = null;
+ }
+
+ /** Get the URL associated with the UCL.
+ */
+ public URL getURL()
+ {
+ return url;
+ }
+
+ /** Get the original URL associated with the UCL. This may be null.
+ */
+ public URL getOrigURL()
+ {
+ return origURL;
+ }
+
+ public synchronized Class loadClassImpl(String name, boolean resolve, int stopAt)
+ throws ClassNotFoundException
+ {
+ loadClassDepth ++;
+ boolean trace = log.isTraceEnabled();
+
+ if( trace )
+ log.trace("loadClassImpl, name="+name+", resolve="+resolve);
+ if( domain == null )
+ {
+ // If we have been undeployed we can still try locally
+ try
+ {
+ return super.loadClass(name, resolve);
+ }
+ catch (ClassNotFoundException ignored)
+ {
+ }
+ String msg = "Invalid use of destroyed classloader, UCL destroyed at:";
+ throw new ClassNotFoundException(msg, this.unregisterTrace);
+ }
+
+ /* Since loadClass can be called from loadClassInternal with the monitor
+ already held, we need to determine if there is a ClassLoadingTask
+ which requires this UCL. If there is, we release the UCL monitor
+ so that the ClassLoadingTask can use the UCL.
+ */
+ boolean acquired = attempt(1);
+ while( acquired == false )
+ {
+ /* Another thread needs this UCL to load a class so release the
+ monitor acquired by the synchronized method. We loop until
+ we can acquire the class loading lock.
+ */
+ try
+ {
+ if( trace )
+ log.trace("Waiting for loadClass lock");
+ this.wait();
+ }
+ catch(InterruptedException ignore)
+ {
+ }
+ acquired = attempt(1);
+ }
+
+ ClassLoadingTaskDCL task = null;
+ try
+ {
+ Thread t = Thread.currentThread();
+ // Register this thread as owning this UCL
+ if( loadLock.holds() == 1 )
+ LoadMgrDCL.registerLoaderThread(this, t);
+
+ // Create a class loading task and submit it to the repository
+ task = new ClassLoadingTaskDCL(name, this, t, stopAt);
+ /* Process class loading tasks needing this UCL until our task has
+ been completed by the thread owning the required UCL(s).
+ */
+ if( LoadMgrDCL.beginLoadTask(task, domain) == false )
+ {
+ while( task.threadTaskCount != 0 )
+ {
+ try
+ {
+ LoadMgrDCL.nextTask(t, task, domain);
+ }
+ catch(InterruptedException e)
+ {
+ // Abort the load or retry?
+ break;
+ }
+ }
+ }
+ }
+ finally
+ {
+ // Unregister as the UCL owner to reschedule any remaining load tasks
+ if( loadLock.holds() == 1 )
+ LoadMgrDCL.endLoadTask(task);
+ // Notify any threads waiting to use this UCL
+ this.release();
+ this.notifyAll();
+ loadClassDepth --;
+ }
+
+ if( task.loadedClass == null )
+ {
+ if( task.loadException instanceof ClassNotFoundException )
+ throw (ClassNotFoundException) task.loadException;
+ else if( task.loadException instanceof NoClassDefFoundError )
+ throw (NoClassDefFoundError) task.loadException;
+ else if( task.loadException != null )
+ {
+ if( log.isTraceEnabled() )
+ log.trace("Unexpected error during load of:"+name, task.loadException);
+ String msg = "Unexpected error during load of: "+name
+ + ", msg="+task.loadException.getMessage();
+ ClassNotFoundException cnfe = new ClassNotFoundException(msg, task.loadException);
+ throw cnfe;
+ }
+ // Assert that loadedClass is not null
+ else
+ throw new IllegalStateException("ClassLoadingTask.loadedTask is null, name: "+name);
+ }
+
+ return task.loadedClass;
+ }
+
+ // URLClassLoader overrides --------------------------------------
+
+ /** Override the permissions accessor to use the CodeSource
+ based on the original URL if one exists. This allows the
+ security policy to be defined in terms of the static URL
+ namespace rather than the local copy or nested URL.
+ This builds a PermissionCollection from:
+ 1. The origURL CodeSource
+ 2. The argument CodeSource
+ 3. The Policy.getPermission(origURL CodeSource)
+
+ This is necessary because we cannot define the CodeSource the
+ SecureClassLoader uses to register the class under.
+
+ @param cs the location and signatures of the codebase.
+ */
+ protected PermissionCollection getPermissions(CodeSource cs)
+ {
+ CodeSource permCS = cs;
+ if( origURL != null )
+ {
+ permCS = new CodeSource(origURL, cs.getCertificates());
+ }
+ Policy policy = Policy.getPolicy();
+ PermissionCollection perms = super.getPermissions(permCS);
+ PermissionCollection perms2 = super.getPermissions(cs);
+ PermissionCollection perms3 = policy.getPermissions(permCS);
+ Enumeration iter = perms2.elements();
+ while( iter.hasMoreElements() )
+ perms.add((Permission) iter.nextElement());
+ iter = perms3.elements();
+ while( iter.hasMoreElements() )
+ perms.add((Permission) iter.nextElement());
+ if( log.isTraceEnabled() )
+ log.trace("getPermissions, url="+url+", origURL="+origURL+" -> "+perms);
+ return perms;
+ }
+
+ /**
+ * Determine the protection domain. If we are a copy of the original
+ * deployment, use the original url as the codebase.
+ * @return the protection domain
+ * @todo certificates and principles?
+ */
+ protected ProtectionDomain getProtectionDomain()
+ {
+ return getProtectionDomain(origURL != null ? origURL : url);
+ }
+}
+
Copied: branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/LoadMgrDCL.java (from rev 56376, trunk/jmx/src/main/org/jboss/mx/loading/LoadMgr3.java)
===================================================================
--- trunk/jmx/src/main/org/jboss/mx/loading/LoadMgr3.java 2006-08-29 00:48:36 UTC (rev 56376)
+++ branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/LoadMgrDCL.java 2006-08-29 02:02:32 UTC (rev 56381)
@@ -0,0 +1,628 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.security.PrivilegedAction;
+import java.security.AccessController;
+
+import org.jboss.classloading.spi.DomainClassLoader;
+import org.jboss.logging.Logger;
+import org.jboss.mx.loading.ClassLoadingTask.ThreadTask;
+
+
+/** A utility class used by the DomainClassLoaderUCLImpl to manage the thread
+ * based class loading tasks.
+ *
+ * @author Scott.Stark at jboss.org
+ * @version $Revision$
+ */
+public class LoadMgrDCL
+{
+ private static Logger log = Logger.getLogger(LoadMgrDCL.class);
+ /** Used as a synchronization monitor during the setup/teardown of the
+ thread owning a UCL.loadClass lock
+ */
+ private static Object registrationLock = new Object();
+
+ /** A Map<UnifiedClassLoader3, Thread> of the active loadClass UCL3/threads.
+ * This must be accessed under the registrationLock monitor.
+ */
+ private static HashMap loadClassThreads = new HashMap();
+ /** A Map<Thread, LinkedList<ThreadTask> > of the class loading tasks
+ * associated with a thread
+ */
+ private static Map loadTasksByThread = Collections.synchronizedMap(new WeakHashMap());
+
+ private static SecurityManager sm = System.getSecurityManager();
+
+ /** A UCL and its relative ordering with respect to the class loading.
+ * The UCL with the lowest order to load a class is the UCL that will
+ * populate the repository cache and be assigned as the UCL.loadClass
+ * return value.
+ */
+ public static class PkgClassLoader
+ {
+ public final RepositoryClassLoader ucl;
+ public final int order;
+
+ public PkgClassLoader(RepositoryClassLoader ucl)
+ {
+ this(ucl, Integer.MAX_VALUE);
+ }
+ public PkgClassLoader(RepositoryClassLoader ucl, int order)
+ {
+ this.ucl = ucl;
+ this.order = order;
+ }
+
+ public String toString()
+ {
+ StringBuffer buffer = new StringBuffer(100);
+ buffer.append(super.toString());
+ buffer.append("{ucl=").append(ucl);
+ buffer.append(" order=").append(order);
+ buffer.append('}');
+ return buffer.toString();
+ }
+ }
+ /** A PrivilegedAction for locating a class as a resource
+ *
+ */
+ private static class ResourceAction implements PrivilegedAction
+ {
+ RepositoryClassLoader ucl;
+ String classRsrcName;
+ ResourceAction(RepositoryClassLoader ucl, String classRsrcName)
+ {
+ this.ucl = ucl;
+ this.classRsrcName = classRsrcName;
+ }
+ public Object run()
+ {
+ URL url = ucl.getResourceLocally(classRsrcName);
+ ucl = null;
+ classRsrcName = null;
+ return url;
+ }
+ }
+
+ /** Register that a thread owns the UCL3.loadClass monitor. This is called
+ * from within UCL3.loadClass(String,boolean) and this method creates
+ * entries in the loadClassThreads and loadTasksByThread maps.
+ */
+ public static void registerLoaderThread(DomainClassLoader ucl, Thread t)
+ {
+ synchronized( registrationLock )
+ {
+ Object prevThread = loadClassThreads.put(ucl, t);
+ if( log.isTraceEnabled() )
+ log.trace("registerLoaderThread, ucl="+ucl+", t="+t+", prevT="+prevThread);
+
+ synchronized( loadTasksByThread )
+ {
+ List taskList = (List) loadTasksByThread.get(t);
+ if( taskList == null )
+ {
+ taskList = Collections.synchronizedList(new LinkedList());
+ loadTasksByThread.put(t, taskList);
+ if( log.isTraceEnabled() )
+ log.trace("created new task list");
+ }
+ }
+ registrationLock.notifyAll();
+ }
+ }
+
+ /** Initiate the class loading task. This is called by UCL3.loadClass to
+ * initiate the process of loading the requested class. This first attempts
+ * to load the class from the repository cache, and then the class loaders
+ * in the repsository. If the package of the class is found in the repository
+ * then one or more ThreadTask are created to complete the ClassLoadingTask.
+ * The ThreadTask are assigned to the threads that own the associated UCL3
+ * monitor. If no class loader serves the class package, then the requesting
+ * class loader is asked if it can load the class.
+ *
+ * @return true if the class could be loaded from the cache or requesting
+ * UCL3, false to indicate the calling thread must process the
+ * tasks assigned to it until the ClassLoadingTask state is FINISHED
+ * @exception ClassNotFoundException if there is no chance the class can
+ * be loaded from the current repository class loaders.
+ */
+ public static boolean beginLoadTask(ClassLoadingTask task,
+ UnifiedLoaderRepository3 repository)
+ throws ClassNotFoundException
+ {
+ boolean trace = log.isTraceEnabled();
+ if( trace )
+ log.trace("Begin beginLoadTask, task="+task);
+
+ // Try the cache before anything else.
+ Class cls = repository.loadClassFromCache(task.classname);
+ if( cls != null )
+ {
+ task.loadedClass = cls;
+ task.state = ClassLoadingTask.FINISHED;
+ if( trace )
+ log.trace("End beginLoadTask, loadClassFromCache, classname: "+task.classname);
+ return true;
+ }
+
+ // Next get the set of class loaders from the packages map
+ Set pkgSet = repository.getPackageClassLoaders(task.classname);
+ if( pkgSet == null || pkgSet.size() == 0 )
+ {
+ if (task.stopOrder == Integer.MAX_VALUE)
+ {
+ /* If there are no class loaders in the repository capable of handling
+ the request ask the class loader itself in the event that its parent(s)
+ can load the class.
+ */
+ try
+ {
+ cls = repository.loadClassFromClassLoader(task.classname, false,
+ task.requestingClassLoader);
+ }
+ catch(LinkageError e)
+ {
+ if( trace )
+ log.trace("End beginLoadTask, LinkageError for task: "+task, e);
+ throw e;
+ }
+ if( cls != null )
+ {
+ task.loadedClass = cls;
+ task.state = ClassLoadingTask.FINISHED;
+ if( trace )
+ log.trace("End beginLoadTask, loadClassFromClassLoader");
+ return true;
+ }
+ }
+
+ // Else, fail the load
+ if( trace )
+ log.trace("End beginLoadTask, ClassNotFoundException");
+ String msg = "No ClassLoaders found for: "+task.classname;
+ throw new ClassNotFoundException(msg);
+ }
+
+ /* A class loading task for each ClassLoader is needed. There can be
+ multiple class loaders for a pkg due to the pkg being spread out over
+ multiple jars, or duplicate classes due to versioning/patches, or
+ just bad packaging.
+
+ In the case of a non-scoped deployment of multiple classes which
+ will provide a PkgClassLoader to define the ordering, we simply
+ choose an ordering based on the order the UCL3s were added to the
+ repository. At most one of the candidate UCL3s will load the class
+ in order to avoid ClassCastExceptions or LinkageErrors due to the
+ strong Java type system/security model.
+
+ TODO: A simple ordering mechanism exists, but this probably needs
+ to be augmented.
+ */
+ Iterator iter = pkgSet.iterator();
+ RepositoryClassLoader theUCL = null;
+ int order = Integer.MAX_VALUE;
+ while( iter.hasNext() )
+ {
+ Object next = iter.next();
+ int uclOrder;
+ RepositoryClassLoader ucl;
+ // This may be either a PkgClassLoader or a UCL3
+ if( next instanceof RepositoryClassLoader )
+ {
+ ucl = (RepositoryClassLoader) next;
+ uclOrder = ucl.getAddedOrder();
+ }
+ else
+ {
+ PkgClassLoader pkgUcl = (PkgClassLoader) next;
+ ucl = pkgUcl.ucl;
+ uclOrder = pkgUcl.order;
+ }
+
+ // If we have a stop order check it
+ if (task.stopOrder != Integer.MAX_VALUE && task.stopOrder <= uclOrder)
+ break;
+
+ // Validate that the ucl has the class as a resource
+ String classRsrcName = task.classname.replace('.', '/') + ".class";
+ URL url = null;
+ if( sm != null )
+ {
+ ResourceAction action = new ResourceAction(ucl, classRsrcName);
+ url = (URL) AccessController.doPrivileged(action);
+ }
+ else
+ {
+ url = ucl.getResourceLocally(classRsrcName);
+ }
+
+ if( url != null && uclOrder < order )
+ {
+ if( trace && theUCL != null )
+ log.trace("Replacing UCL: "+theUCL+" with UCL:"+ucl);
+ theUCL = ucl;
+ order = uclOrder;
+ }
+ }
+ if( theUCL == null && task.stopOrder == Integer.MAX_VALUE)
+ {
+ /* If there are no class loaders in the repository capable of handling
+ the request ask the class loader itself in the event that its parent(s)
+ can load the class. But not if we have a stopOrder.
+ */
+ try
+ {
+ cls = repository.loadClassFromClassLoader(task.classname, false,
+ task.requestingClassLoader);
+ }
+ catch(LinkageError e)
+ {
+ if( trace )
+ log.trace("End beginLoadTask, LinkageError for task: "+task, e);
+ throw e;
+ }
+ if( cls != null )
+ {
+ task.loadedClass = cls;
+ task.state = ClassLoadingTask.FINISHED;
+ if( trace )
+ log.trace("End beginLoadTask, loadClassFromClassLoader");
+ return true;
+ }
+
+ // Else, fail the load
+ if( trace )
+ log.trace("End beginLoadTask, ClassNotFoundException");
+ String msg = "No ClassLoaders found for: "+task.classname;
+ throw new ClassNotFoundException(msg);
+ }
+
+ if (theUCL == null)
+ {
+ if( trace )
+ log.trace("End beginLoadTask, ClassNotFoundException");
+ String msg = "No ClassLoaders found for: "+task.classname;
+ throw new ClassNotFoundException(msg);
+ }
+
+ scheduleTask(task, theUCL, order, false, trace);
+ task.state = ClassLoadingTask.FOUND_CLASS_LOADER;
+ if( trace )
+ log.trace("End beginLoadTask, task="+task);
+
+ return false;
+ }
+
+ /** Called by threads owning a UCL3.loadLock from within UCL3.loadClass to
+ * process ThreadTasks assigned to them. This is the mechanism by which we
+ * avoid deadlock due to a given loadClass request requiring multiple UCLs
+ * to be involved. Any thread active in loadClass with the monitor held
+ * processes class loading tasks that must be handled by its UCL3. The
+ * active set of threads loading classes form a pool of cooperating threads.
+ */
+ public static void nextTask(Thread t, ClassLoadingTask task,
+ UnifiedLoaderRepository3 repository)
+ throws InterruptedException
+ {
+ boolean trace = log.isTraceEnabled();
+ List taskList = (List) loadTasksByThread.get(t);
+ synchronized( taskList )
+ {
+ // There may not be any ThreadTasks
+ while( taskList.size() == 0 && task.threadTaskCount != 0 )
+ {
+ /* There are no more tasks for the calling thread to execute, so the
+ calling thread must wait until the task.threadTaskCount reaches 0
+ */
+ if( trace )
+ log.trace("Begin nextTask(WAIT_ON_EVENT), task="+task);
+ try
+ {
+ task.state = ClassLoadingTask.WAIT_ON_EVENT;
+ taskList.wait();
+ }
+ catch(InterruptedException e)
+ {
+ if( trace )
+ log.trace("nextTask(WAIT_ON_EVENT), interrupted, task="+task, e);
+ // Abort this task attempt
+ throw e;
+ }
+ if( trace )
+ log.trace("nextTask(WAIT_ON_EVENT), notified, task="+task);
+ }
+
+ if( trace )
+ log.trace("Continue nextTask("+taskList.size()+"), task="+task);
+
+ // See if the task is complete
+ if( task.threadTaskCount == 0 )
+ {
+ task.state = ClassLoadingTask.FINISHED;
+ log.trace("End nextTask(FINISHED), task="+task);
+ return;
+ }
+ }
+
+ ThreadTask threadTask = (ThreadTask) taskList.remove(0);
+ ClassLoadingTask loadTask = threadTask.getLoadTask();
+ if( trace )
+ log.trace("Begin nextTask("+taskList.size()+"), loadTask="+loadTask);
+
+ RepositoryClassLoader ucl3 = threadTask.ucl;
+ try
+ {
+ if( threadTask.t == null )
+ {
+ /* This is a task that has been reassigned back to the original
+ requesting thread ClassLoadingTask, so a new ThreadTask must
+ be scheduled.
+ */
+ if( trace )
+ log.trace("Rescheduling threadTask="+threadTask);
+ scheduleTask(loadTask, ucl3, threadTask.order, true, trace);
+ }
+ else
+ {
+ if( trace )
+ log.trace("Running threadTask="+threadTask);
+ // Load the class using this thread
+ threadTask.run();
+ }
+ }
+ catch(Throwable e)
+ {
+ if( e instanceof ClassCircularityError /*&& taskList.size() > 0 */ )
+ {
+ /* Reschedule this task after all existing tasks to allow the
+ current load tasks which are conflicting to complete.
+ */
+ try
+ {
+ if( trace )
+ log.trace("Run failed with exception", e);
+ // Reschedule and update the loadTask.threadTaskCount
+ scheduleTask(loadTask, ucl3, Integer.MAX_VALUE, true, trace);
+ }
+ catch(ClassNotFoundException ex)
+ {
+ loadTask.setLoadError(ex);
+ log.warn("Failed to reschedule task after CCE", ex);
+ }
+ if( trace )
+ log.trace("Post CCE state, loadTask="+loadTask);
+ }
+ else
+ {
+ loadTask.setLoadError(e);
+ if( trace )
+ log.trace("Run failed with exception", e);
+ }
+ }
+ finally
+ {
+ // We must release the loadLock acquired in beginLoadTask
+ if( threadTask.releaseInNextTask == true )
+ {
+ if( trace )
+ log.trace("Releasing loadLock and ownership of UCL: "+threadTask.ucl);
+ synchronized( registrationLock )
+ {
+ loadClassThreads.remove(threadTask.ucl);
+ }
+ synchronized( threadTask.ucl )
+ {
+ ucl3.release();
+ ucl3.notifyAll();
+ }
+ }
+ }
+
+ // If the ThreadTasks are complete mark the ClassLoadingTask finished
+ if( loadTask.threadTaskCount == 0 )
+ {
+ Class loadedClass = threadTask.getLoadedClass();
+ if( loadedClass != null )
+ {
+ ClassLoader loader = loadedClass.getClassLoader();
+ ClassLoader wrapper = repository.getWrappingClassLoader(loader);
+ if (wrapper != null)
+ loader=wrapper;
+ // Place the loaded class into the repositry cache
+ repository.cacheLoadedClass(threadTask.getClassname(),
+ loadedClass, loader);
+ }
+ /*
+ synchronized( loadTask )
+ {
+ if( trace )
+ log.trace("Notifying task of thread completion, loadTask:"+loadTask);
+ loadTask.state = ClassLoadingTask.FINISHED;
+ loadTask.notify();
+ }
+ */
+ List loadTaskThreadTasks = (List) loadTasksByThread.get(loadTask.requestingThread);
+ synchronized( loadTaskThreadTasks )
+ {
+ if( trace )
+ log.trace("Notifying task of thread completion, loadTask:"+loadTask);
+ loadTask.state = ClassLoadingTask.FINISHED;
+ loadTaskThreadTasks.notify();
+ }
+ }
+ if( trace )
+ log.trace("End nextTask("+taskList.size()+"), loadTask="+loadTask);
+ }
+
+ /** Complete a ClassLoadingTask. This is called by UCL3.loadClass to indicate
+ * that the thread is existing the loadClass method.
+ */
+ public static void endLoadTask(ClassLoadingTask task)
+ {
+ boolean trace = log.isTraceEnabled();
+ if( trace )
+ log.trace("Begin endLoadTask, task="+task);
+
+ // Unregister as the owning thread and notify any waiting threads
+ synchronized( registrationLock )
+ {
+ loadClassThreads.remove(task.requestingClassLoader);
+ registrationLock.notifyAll();
+ }
+
+ // Any ThreadTasks associated with this thread must be reassigned
+ List taskList = (List) loadTasksByThread.get(task.requestingThread);
+ int size = taskList != null ? taskList.size() : 0;
+ synchronized( taskList )
+ {
+ for(int i = 0; i < size; i ++)
+ {
+ ThreadTask threadTask = (ThreadTask) taskList.remove(0);
+ ClassLoadingTask loadTask = threadTask.getLoadTask();
+ /* Synchronize on loadTask and reassign the thread task back to the
+ requesting thread of loadTask. We need to synchronize on loadTask
+ to ensure that the transfer of this task back to loadTask.requestingThread
+ is atomic wrt loadTask.requestingThread checking its task list.
+ synchronized( loadTask )
+ {
+ if( trace )
+ log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
+ threadTask.t = null;
+ // Insert the task into the front of requestingThread task list
+ List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
+ toTaskList.add(0, threadTask);
+ loadTask.state = ClassLoadingTask.NEXT_EVENT;
+ loadTask.notify();
+ }
+ */
+ if( trace )
+ log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
+ threadTask.t = null;
+ // Insert the task into the front of requestingThread task list
+ List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
+ synchronized( toTaskList )
+ {
+ toTaskList.add(0, threadTask);
+ loadTask.state = ClassLoadingTask.NEXT_EVENT;
+ toTaskList.notify();
+ }
+ }
+ }
+ }
+
+ /** Invoked to create a ThreadTask to assign a thread to the task of
+ * loading the class of ClassLoadingTask.
+ *
+ * @param task the orginating UCL3.loadClass task for which the thread
+ * @param ucl the UCL3 the ThreadTask will call loadClassLocally on
+ * @param order the heirachical ordering of the task
+ * @param reschedule a boolean indicating if this task is being rescheduled
+ * with another UCL3
+ * @param trace the Logger trace level flag
+ * @throws ClassNotFoundException
+ */
+ static private void scheduleTask(ClassLoadingTask task, RepositoryClassLoader ucl,
+ int order, boolean reschedule, boolean trace) throws ClassNotFoundException
+ {
+ Thread t = null;
+ boolean releaseInNextTask = false;
+ ThreadTask subtask = null;
+ List taskList = null;
+ synchronized( registrationLock )
+ {
+ // Find the thread that owns the ucl
+ t = (Thread) loadClassThreads.get(ucl);
+ if( t == null )
+ {
+ /* There is no thread in the UCL.loadClass yet that has registered
+ as the owning thread. We must attempt to acquire the loadLock
+ and if we cannot, wait until the thread entering UCL.loadClass
+ gets to the registerLoaderThread call. By the time we are
+ notified, the thread coule in fact have exited loadClass, so
+ we either assign the task to the thread, or take ownership of
+ the UCL.
+ */
+ while( t == null && ucl.attempt(1) == false )
+ {
+ if( trace )
+ log.trace("Waiting for owner of UCL: "+ucl);
+ try
+ {
+ registrationLock.wait();
+ }
+ catch(InterruptedException e)
+ {
+ String msg = "Interrupted waiting for registration notify,"
+ + " classame: "+task.classname;
+ throw new ClassNotFoundException(msg);
+ }
+
+ t = (Thread) loadClassThreads.get(ucl);
+ if( trace )
+ log.trace("Notified that UCL owner is: "+t);
+ }
+
+ // Get the thread registered as owning the UCL.loadClass lock
+ t = (Thread) loadClassThreads.get(ucl);
+ if( t == null )
+ {
+ // There is no such thread, register as the owner
+ releaseInNextTask = true;
+ t = task.requestingThread;
+ Object prevThread = loadClassThreads.put(ucl, t);
+ if( trace )
+ {
+ log.trace("scheduleTask, taking ownership of ucl="+ucl
+ +", t="+t+", prevT="+prevThread);
+ }
+ }
+ }
+
+ // Now that we have the UCL owner thread, create and assign the task
+ subtask = task.newThreadTask(ucl, t, order, reschedule,
+ releaseInNextTask);
+ // Add the task to the owning thread
+ taskList = (List) loadTasksByThread.get(t);
+ synchronized( taskList )
+ {
+ taskList.add(subtask);
+ // Order the tasks by either the heirarchial order, or the repository order
+ Collections.sort(taskList, task.taskComparator);
+ taskList.notify();
+ }
+ }
+
+ if( trace )
+ log.trace("scheduleTask("+taskList.size()+"), created subtask: "+subtask);
+ }
+}
Added: branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCL.java
===================================================================
--- branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCL.java 2006-08-29 02:01:28 UTC (rev 56380)
+++ branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCL.java 2006-08-29 02:02:32 UTC (rev 56381)
@@ -0,0 +1,1023 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt 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.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+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.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.classloading.spi.ClassLoadingDomain;
+import org.jboss.classloading.spi.DomainClassLoader;
+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 DomainClassLoaderUCLImpl 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 at jboss.org">Scott Stark</a>.
+ * @author <a href="mailto:Adrian.Brock at HappeningTimes.com">Adrian Brock</a>.
+ * @version $Revision: 37459 $
+ * just a hint... xdoclet not really used
+ * @jmx.name="JMImplementation:service=UnifiedLoaderRepository,name=Default"
+ */
+public class UnifiedLoaderRepositoryDCL extends LoaderRepositoryDomain
+ implements MBeanRegistration, NotificationBroadcaster,
+ UnifiedLoaderRepositoryDCLMBean
+{
+ // 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;
+
+ // 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<DomainClassLoader, 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, DomainClassLoader) 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 loaderToPackagesMap = new HashMap();
+
+ /**
+ * 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;
+
+
+ // Public --------------------------------------------------------
+
+ public DomainClassLoader newClassLoader(final URL url, boolean addToRepository)
+ throws Exception
+ {
+ DomainClassLoaderUCLImpl ucl = new DomainClassLoaderUCLImpl(url, null, this);
+ if (addToRepository)
+ this.registerClassLoader(ucl);
+ return ucl;
+ }
+
+ public DomainClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository)
+ throws Exception
+ {
+ DomainClassLoaderUCLImpl ucl = new DomainClassLoaderUCLImpl(url, origURL, this);
+ if (addToRepository)
+ this.registerClassLoader(ucl);
+ return ucl;
+ }
+
+ public ClassLoadingDomain getParent()
+ {
+ return null;
+ }
+
+ 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 DomainClassLoaderUCLImpl to load the class as UCL3s
+ * do not use this method.
+ */
+ public Class loadClass(String name, boolean resolve, DomainClassLoader cl) throws ClassNotFoundException
+ {
+ return cl.loadClass(name);
+ }
+
+ /** Called by LoadMgr to obtain all class loaders for the given className
+ * @return Set<DomainClassLoaderUCLImpl>, may be null
+ */
+ public Set getPackageClassLoaders(String className)
+ {
+ String pkgName = ClassLoaderUtils.getPackageName(className);
+ Set pkgSet = (Set) packagesMap.get(pkgName);
+ if (dynamicClassLoaders.size() > 0)
+ {
+ if (pkgSet == null)
+ pkgSet = ClassLoaderUtils.newPackageSet();
+ 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, DomainClassLoader 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, DomainClassLoader 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
+ }
+ return null;
+ }
+
+ /**
+ * Loads a resource following the Unified DomainClassLoader architecture
+ */
+ public URL getResource(String name, DomainClassLoader 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, DomainClassLoader cl, List urls)
+ {
+ // Go through all class loaders
+ Iterator iter = classLoaders.iterator();
+ while (iter.hasNext() == true)
+ {
+ DomainClassLoader nextCL = (DomainClassLoader) iter.next();
+ if (nextCL instanceof DomainClassLoader)
+ {
+ DomainClassLoader ucl = (DomainClassLoader) nextCL;
+ try
+ {
+ Enumeration resURLs = ucl.loadResourceLocally(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, DomainClassLoader 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, DomainClassLoader cl)
+ {
+ URL resource = null;
+ if (cl instanceof DomainClassLoader)
+ {
+ DomainClassLoader ucl = (DomainClassLoader) cl;
+ resource = ucl.loadResourceLocally(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, DomainClassLoader 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)
+ {
+ DomainClassLoader classloader = (DomainClassLoader) i.next();
+ if (classloader.equals(cl))
+ {
+ continue;
+ }
+
+ if (classloader instanceof DomainClassLoader)
+ {
+ url = ((DomainClassLoader) classloader).loadResourceLocally(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, DomainClassLoader 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, DomainClassLoader 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 DomainClassLoader)
+ {
+ DomainClassLoader cl = (DomainClassLoader) 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();
+ DomainClassLoader ucl = null;
+ if (classLoaders.size() > 0)
+ ucl = (DomainClassLoader) 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(DomainClassLoader loader, String className)
+ throws ClassNotFoundException
+ {
+ throw new ClassNotFoundException("NYI");
+ }
+
+ /**
+ * Get any wrapping classloader for the passed classloader
+ *
+ * @param cl the wrapped classloader
+ * @return the wrapping classloader or null if not wrapped
+ */
+ public DomainClassLoader getWrappingClassLoader(DomainClassLoader cl)
+ {
+ synchronized (classLoaders)
+ {
+ return (DomainClassLoader) nonUCLClassLoader.get(cl);
+ }
+ }
+
+ /** Add a class loader to the repository.
+ */
+ public void addClassLoader(DomainClassLoader loader)
+ {
+ // if you come to us as UCL we send you straight to the orbit
+ if (loader instanceof DomainClassLoader)
+ addDomainClassLoader((DomainClassLoader) loader);
+ else if (loader instanceof URLClassLoader)
+ {
+ addURLClassLoader((URLClassLoader) loader);
+ }
+ else
+ {
+ log.warn("Tried to add non-URLClassLoader. Ignored");
+ } // end of else
+ }
+
+ public boolean addClassLoaderURL(DomainClassLoader cl, URL url)
+ {
+ DomainClassLoader ucl = (DomainClassLoader) 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 addDomainClassLoader(DomainClassLoader cl)
+ {
+ cl.setDomain(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 addURLClassLoader(URLClassLoader loader)
+ {
+ URL[] urls = loader.getURLs();
+ int count = urls != null && urls.length > 0 ? urls.length : 0;
+ URL origURL = count > 0 ? urls[0] : null;
+ DomainClassLoaderUCLImpl ucl3 = new DomainClassLoaderUCLImpl(origURL, origURL, this);
+ addDomainClassLoader(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(DomainClassLoader cl)
+ {
+ try
+ {
+ String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl, packagesMap);
+ loaderToPackagesMap.put(cl, pkgNames);
+ }
+ 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);
+ }
+ }
+
+ /** Walk through the new URL to update the packages the DomainClassLoader is
+ * capable of handling
+ */
+ private void updatePackageMap(DomainClassLoader cl, URL url)
+ {
+ try
+ {
+ String[] prevPkgNames = (String[]) loaderToPackagesMap.get(cl);
+ String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl, packagesMap, url, prevPkgNames);
+ loaderToPackagesMap.put(cl, pkgNames);
+ }
+ 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(DomainClassLoader loader)
+ {
+ ArrayList removeNotifications = new ArrayList();
+ DomainClassLoader cl = loader;
+ synchronized (classLoaders)
+ {
+ if ((loader instanceof DomainClassLoader) == false)
+ {
+ cl = (DomainClassLoader) nonUCLClassLoader.remove(loader);
+ }
+ if (cl instanceof DomainClassLoader)
+ {
+ DomainClassLoader ucl = (DomainClassLoader) 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)
+ {
+ String[] pkgNames = (String[]) loaderToPackagesMap.remove(cl);
+ int length = pkgNames != null ? pkgNames.length : 0;
+ for (int p = 0; p < length; p++)
+ {
+ String pkgName = pkgNames[p];
+ Set pkgSet = (Set) packagesMap.get(pkgName);
+ if (pkgSet != null)
+ {
+ pkgSet.remove(cl);
+ 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();
+ pkgSet.remove(cl);
+ 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);
+ }
+
+ Notification msg = new Notification(CLASSLOADER_REMOVED, this, getNextSequenceNumber());
+ 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 LoaderRepositoryDomain registerClassLoader(DomainClassLoader ucl)
+ {
+ addClassLoader(ucl);
+ Notification msg = new Notification(CLASSLOADER_ADDED, this, getNextSequenceNumber());
+ msg.setUserData(ucl);
+ broadcaster.sendNotification(msg);
+
+ return this;
+ }
+
+ /**
+ * @jmx.managed-operation
+ */
+ public LoaderRepositoryDomain 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)
+ {
+ 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")
+
+ };
+ }
+ 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++;
+ }
+
+
+}
Added: branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCLMBean.java
===================================================================
--- branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCLMBean.java 2006-08-29 02:01:28 UTC (rev 56380)
+++ branches/MC_VDF_WORK/jmx/src/main/org/jboss/mx/loading/UnifiedLoaderRepositoryDCLMBean.java 2006-08-29 02:02:32 UTC (rev 56381)
@@ -0,0 +1,50 @@
+package org.jboss.mx.loading;
+
+import java.net.URL;
+import java.util.Set;
+
+import org.jboss.classloading.spi.DomainClassLoader;
+import org.jboss.util.loading.Translator;
+
+public interface UnifiedLoaderRepositoryDCLMBean
+{
+ public DomainClassLoader newClassLoader(final URL url, boolean addToRepository)
+ throws Exception;
+ public DomainClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository)
+ throws Exception;
+
+ public void removeClassLoader(DomainClassLoader cl);
+
+ public LoaderRepositoryDomain registerClassLoader(DomainClassLoader ucl);
+
+ public LoaderRepositoryDomain getInstance();
+
+ public URL[] getURLs();
+
+ // Aspect stuff
+ public Translator getTranslator();
+ public void setTranslator(Translator t);
+
+ /** Called by LoadMgr to obtain all class loaders for the given className
+ *@return LinkedList<UnifiedClassLoader3>, may be null
+ */
+ public Set getPackageClassLoaders(String className);
+
+ /** A utility method that iterates over all repository class loader and
+ * display the class information for every UCL that contains the given
+ * className
+ */
+ public String displayClassInfo(String className);
+
+ /** Get the number of classes loaded into the ULR cache.
+ * @return the classes cache size.
+ */
+ public int getCacheSize();
+ /** Get the number of UnifiedClassLoader3s (UCLs) in the ULR
+ * @return the number of UCLs in the ULR
+ */
+ public int getClassLoadersSize();
+ /** Flush the ULR classes cache
+ */
+ public void flush();
+}
More information about the jboss-cvs-commits
mailing list