Package org.jboss.mx.loading

Source Code of org.jboss.mx.loading.UnifiedLoaderRepository3

/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/

package org.jboss.mx.loading;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;

import org.jboss.mx.logging.Logger;
import org.jboss.mx.logging.SystemLogger;

/** 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 using the LoadMgr utility class.
*
* @author  <a href="mailto:scott.stark@jboss.org">Scott Stark</a>.
* @author  <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>.
* @version $Revision: 1.5 $
* just a hint... xdoclet not really used
* @jmx.name="JMImplementation:service=UnifiedLoaderRepository,name=Default"
*
*/
public class UnifiedLoaderRepository3 extends LoaderRepository
   implements NotificationEmitter, UnifiedLoaderRepository3MBean
{
   // Static --------------------------------------------------------
   private static final Logger log = SystemLogger.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 ----------------------------------------------------

   /**
    * The classloaders we use for loading classes.
    */
   private HashSet classLoaders = new HashSet();

   /** A set 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.
    */
   private HashSet classLoaderURLs = new HashSet();

   /**
    * The classes loaded, maps names to class objects.
    */
   private HashMap classes = new HashMap();

   /**
    * Maps class loaders to the set of classes they loaded.
    */
   private HashMap loaderToClassesMap = new HashMap();

   /**
    * Maps class loaders to the set of resource names they looked up.
    */
   private HashMap loaderToResourcesMap = new HashMap();

   /**
    * The global resources. Map of name to ResourceInfo
    */
   private HashMap globalResources = new HashMap();

   /** A map of package names to the set of ClassLoaders which
    have classes in the package.
    */
   private HashMap packagesMap = new HashMap();

   /** A map of class loaders to the array of pckages names they serve
    */
   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 NotificationBroadcasterSupport broadcaster = new NotificationBroadcasterSupport();

   /**
    * The NotificationInfo we emit.
    */
   private MBeanNotificationInfo[] info;


   // Public --------------------------------------------------------

   public UnifiedClassLoader newClassLoader(final URL url, boolean addToRepository)
      throws Exception
   {
      UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null, this);
      if( addToRepository )
      {
         this.addCL(ucl);
      }
      return ucl;
   }
   public UnifiedClassLoader newClassLoader(final URL url, final URL origURL, boolean addToRepository)
      throws Exception
   {
      UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL, this);
      if( addToRepository )
      {
         this.addCL(ucl);
      }
      return ucl;
   }

   /** 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
   {
      UnifiedClassLoader3 ucl = (UnifiedClassLoader3) cl;
      return ucl.loadClass(name, resolve);
   }

   /** Called by LoadMgr to obtain all class loaders for the given className
    *@return HashSet<UnifiedClassLoader3>, may be null
    */
   public HashSet getPackageClassLoaders(String className)
   {
      String pkgName = ClassLoaderUtils.getPackageName(className);
      HashSet pkgSet = (HashSet) this.packagesMap.get(pkgName);
      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('/', '.');
   }

   Class loadClassFromCache(String name)
   {
      // Return classes from the global cache
      // In this case the classloader is not used.
      Class cls = (Class) classes.get(name);
      return cls;
   }

   void cacheLoadedClass(String name, Class cls, ClassLoader cl)
   {
      // Update the global cache
      classes.put(name, cls);

      // Update the cache for this classloader
      // This is used to cycling classloaders
      HashSet classes = (HashSet)loaderToClassesMap.get(cl);
      if (classes == null)
      {
         classes = new HashSet();
         loaderToClassesMap.put(cl, classes);
      }
      classes.add(name);
   }

   Class loadClassFromClassLoader(String name, boolean resolve, UnifiedClassLoader cl)
   {
      if (cl instanceof UnifiedClassLoader)
      {
         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 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;
   }

   private URL getResourceFromCache(String name, ClassLoader cl)
   {
      // Differently from classes, resource are not looked up in a global cache,
      // as 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

      if (loaderToResourcesMap.containsKey(cl))
      {
         HashMap resources = (HashMap)loaderToResourcesMap.get(cl);
         return (URL)resources.get(name);
      }
      return null;
   }

   private URL getResourceFromClassLoader(String name, ClassLoader cl)
   {
      if (cl instanceof UnifiedClassLoader)
      {
         URL url = ((UnifiedClassLoader)cl).getResourceLocally(name);
         cacheLoadedResource(name, url, cl);
         return url;
      }
      return null;
   }

   private URL getResourceFromGlobalCache(String name)
   {
      // Invoked after the classloader is asked, caches the
      // results of global lookups
      ResourceInfo ri = (ResourceInfo) globalResources.get(name);
      if (ri != null)
         return ri.url;
      return null;
   }

   private URL getResourceFromRepository(String name, ClassLoader cl)
   {
      // Get the set of class loaders from the packages map
      String pkgName = getResourcePackageName(name);
      HashSet pkgSet = (HashSet) this.packagesMap.get(pkgName);
      Iterator i;
      if( pkgSet != null )
         i = pkgSet.iterator();
      // If no pkg match was found just go through all class loaders
      else
         i = classLoaders.iterator();

      while( i.hasNext() == true )
      {
         ClassLoader classloader = (ClassLoader)i.next();
         if (classloader.equals(cl))
         {
            continue;
         }

         if (classloader instanceof UnifiedClassLoader)
         {
            URL url = ((UnifiedClassLoader)classloader).getResourceLocally(name);
            if (url != null)
            {
               cacheLoadedResource(name, url, classloader);
               cacheGlobalResource(name, url, classloader);
               return url;
            }
            else
            {
               // Do nothing, go on with next classloader
            }
         }
      }
      return null;
   }

   private void cacheLoadedResource(String name, URL url, ClassLoader cl)
   {
      // Differently from classes there is no global cache.

      // Update the cache for this classloader only
      // This is used for cycling classloaders
      HashMap resources = (HashMap)loaderToResourcesMap.get(cl);
      if (resources == null)
      {
         resources = new HashMap();
         loaderToResourcesMap.put(cl, resources);
      }
      resources.put(name, url);
   }

   private void cacheGlobalResource(String name, URL url, ClassLoader cl)
   {
      // Resources looked up from one classloader, found in another.
      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();
      HashSet tmp = (HashSet) classLoaders.clone();
      for (Iterator iter = tmp.iterator(); iter.hasNext();)
      {
         Object obj = iter.next();
         if (obj instanceof UnifiedClassLoader)
         {
            UnifiedClassLoader cl = (UnifiedClassLoader)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 loader 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;
      StringBuffer results = new StringBuffer(className+" Information");
      HashSet tmp = (HashSet) classLoaders.clone();
      for (Iterator iter = tmp.iterator(); iter.hasNext();)
      {
         URLClassLoader cl = (URLClassLoader) iter.next();
         URL classURL = cl.findResource(classRsrcName);
         if (classURL != null)
         {
            try
            {
               // Since the class was found we can load it which should be a noop
               Class cls = cl.loadClass(className);
               results.append("\n\n### Instance"+count+" via UCL: "+cl+"\n");
               count ++;
               ClassLoaderUtils.displayClassInfo(cls, results);
            }
            catch (ClassNotFoundException e)
            {
               results.append("\nFailed to find class in UCL: "+cl);
            }
         }
      }

      // 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)
         {
            try
            {
               // Since the class was found we can load it which should be a noop
               Class cls = cl.loadClass(className);
               results.append("\n\n### Instance"+count+" via UCL: "+cl+"\n");
               count ++;
               ClassLoaderUtils.displayClassInfo(cls, results);
            }
            catch (ClassNotFoundException e)
            {
               results.append("\nFailed to find class in UCL: "+cl);
            }
         }
      }

      return results.toString();
   }

   // LoaderRepository overrides ------------------------------------

   /**
    */
   public Class loadClass(String className) throws ClassNotFoundException
   {
      ClassLoader scl = Thread.currentThread().getContextClassLoader();
      Class clazz = null;

      if( (scl instanceof UnifiedClassLoader3) == true )
      {
         clazz = loadClass(className, false, scl);
      }
      else
      {
         clazz = scl.loadClass(className);
      }
      return clazz;
   }

   /** This is a utility method that iterates through the current class loaders
    * and tries to find the given class name. It is never invoked as part of
    * class or resource loading.
    * @return the Class object for name if found, null otherwise.
    *
    * @jmx.managed-operation
    */
   public Class findClass(String name)
   {
      // 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 = name.replace('.', '/') + ".class";

      HashSet tmp = (HashSet) classLoaders.clone();
      for (Iterator iter = tmp.iterator(); iter.hasNext();)
      {
         URLClassLoader cl = (URLClassLoader) iter.next();
         URL classURL = cl.findResource(classRsrcName);
         log.trace("Checking CL for URL: "+classURL);
         if (classURL != null)
         {
            try
            {
               // Since the class was found we can load it which should be a noop
               Class cls = cl.loadClass(name);
               log.trace("Found class in: "+cls.getProtectionDomain());
               return cls;
            }
            catch (ClassNotFoundException e)
            {
               log.debug("Failed to load class: " + name, e);
            }
         }
      }
      log.trace("Class not found");
      return null;
   }

   public Class loadClassWithout(ClassLoader loader, String className)
      throws ClassNotFoundException
   {
      throw new ClassNotFoundException("NYI");
   }

   /**
    * Add a class loader to the repository.
    *
    * IMPORTANT: This method is for use by the MBeanServer,
    * we ignore requests to add UnifiedClassLoaders. They are
    * managed by the deployers.
    *
    * @param cl the class loader to add
    */
   public void addClassLoader(ClassLoader cl)
   {
      if (cl instanceof UnifiedClassLoader)
         return;
      addCL(cl);
   }

   /** Add a class loader to the repository. This synchronizes on the
    * LoadMgr.class
    */
   public void addCL(ClassLoader loader)
   {
      synchronized( LoadMgr.class )
      {
         // if you come to us as UCL we send you straight to the orbit
         if (loader instanceof UnifiedClassLoader3)
            addUnifiedClassLoader((UnifiedClassLoader3)loader);

         // if you come to us as URLCL we'll slice you up and
         // orbit UCL per URL
         else if (loader instanceof URLClassLoader)
         {
            URLClassLoader ucl = (URLClassLoader)loader;
            URL[] urls = ucl.getURLs();

            for (int i = 0; i < urls.length; ++i)
            {
               UnifiedClassLoader3 ucl3 = new UnifiedClassLoader3(urls[i], urls[i], this);
               addUnifiedClassLoader(ucl3);
            }
         }

         else
         {
            log.warn("Tried to add non- URLClassLoader.  Ignored");
         } // end of else
      }
   }

   private void addUnifiedClassLoader(UnifiedClassLoader3 cl)
   {
      cl.setRepository(this);
      // See if this URL already exists
      URL url = cl.getURL();
      boolean added = false;
      boolean exists = classLoaderURLs.contains(url);
      // If already present will not be added
      if(!exists)
      {
         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);
      }
   }

   /** Walk through the class loader URL to see what packages it is capable
    of handling
    */
   private void updatePackageMap(UnifiedClassLoader 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);
      }
   }

   /**
    * Remove a class loader from the repository.
    *
    * IMPORTANT: This method is for use by the MBeanServer,
    * we ignore, requests to remove UnifiedClassLoaders. They are
    * managed by the deployers.
    *
    * @param cl the class loader to remove
    */
   public void removeClassLoader(ClassLoader cl)
   {
      if (cl instanceof UnifiedClassLoader)
         return;
      removeCL(cl);
   }

   /** Remove the class loader from the repository. This synchronizes on the
    * LoadMgr.class
    */
   public void removeCL(ClassLoader cl)
   {
      synchronized( LoadMgr.class )
      {
         if( cl instanceof UnifiedClassLoader )
         {
            UnifiedClassLoader ucl = (UnifiedClassLoader) cl;
            URL url = ucl.getURL();
            classLoaderURLs.remove(url);
         }
         boolean removed = classLoaders.remove(cl);
         log.debug("UnifiedLoaderRepository removed("+removed+") " + cl);

         // Take care also of the cycling mapping for classes
         if (loaderToClassesMap.containsKey(cl))
         {
            HashSet loaded = (HashSet)loaderToClassesMap.remove(cl);
            // This classloader has loaded at least one class
            if (loaded != null)
            {
               // Notify that classes are about to be removed
               for (Iterator iter = loaded.iterator(); iter.hasNext();)
               {
                  broadcaster.sendNotification(new Notification(CLASS_REMOVED, this, getNextSequenceNumber(), (String)iter.next()));
               }

               // Remove the classes from the global cache
               for (Iterator i = loaded.iterator(); i.hasNext();)
               {
                  String cls = (String)i.next();
                  this.classes.remove(cls);
               }
            }
         }

         // Take care also of the cycling mapping for resources
         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
         String[] pkgNames = (String[]) loaderToPackagesMap.remove(cl);
         int length = pkgNames != null ? pkgNames.length : 0;
         for(int p = 0; p < length; p ++)
         {
            String pkgName = pkgNames[p];
            HashSet pkgSet = (HashSet) packagesMap.get(pkgName);
            if( pkgSet != null )
            {
               pkgSet.remove(cl);
               if( pkgSet.isEmpty() )
                  packagesMap.remove(pkgName);
            }
         }
      }
   }

   /**
    * 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(UnifiedClassLoader ucl)
   {
      addClassLoader(ucl);
      Notification msg = new Notification(CLASSLOADER_ADDED, this, getNextSequenceNumber());
      msg.setUserData(ucl);
      broadcaster.sendNotification(msg);

      return this;
   }

   /**
    * @jmx.managed-operation
    */
   public LoaderRepository getInstance()
   {
      return this;
   }

   // implementation of javax.management.NotificationEmitter interface

   public void addNotificationListener(NotificationListener listener,
      NotificationFilter filter, Object handback) throws IllegalArgumentException
   {
      broadcaster.addNotificationListener(listener, filter, handback);
   }

   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;
   }

   public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException
   {
      broadcaster.removeNotificationListener(listener);
   }

   public void removeNotificationListener(NotificationListener listener,
                                          NotificationFilter filter,
                                          Object handback)
      throws ListenerNotFoundException
   {
      broadcaster.removeNotificationListener(listener, filter, handback);
   }

   private long getNextSequenceNumber()
   {
      return sequenceNumber++;
   }

}
TOP

Related Classes of org.jboss.mx.loading.UnifiedLoaderRepository3

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.