Package org.jboss.classloader.spi.base

Source Code of org.jboss.classloader.spi.base.BaseClassLoaderDomain

/*
* JBoss, Home of Professional Open Source.
* Copyright 2007, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.classloader.spi.base;

import java.io.IOException;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.jboss.classloader.plugins.ClassLoaderUtils;
import org.jboss.classloader.spi.CacheLoader;
import org.jboss.classloader.spi.ClassLoaderCache;
import org.jboss.classloader.spi.ClassLoaderPolicy;
import org.jboss.classloader.spi.DelegateLoader;
import org.jboss.classloader.spi.ImportType;
import org.jboss.classloader.spi.Loader;
import org.jboss.classloader.spi.ShutdownPolicy;
import org.jboss.classloader.spi.translator.TranslatorUtils;
import org.jboss.logging.Logger;
import org.jboss.util.collection.ConcurrentSet;
import org.jboss.util.loading.Translator;

/**
* BaseClassLoaderDomain.<p>
*
* This class hides some of the implementation details and allows
* package access to the protected methods.
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @author <a href="ales.justin@jboss.org">Ales Justin</a>
* @version $Revision: 1.1 $
*/
public abstract class BaseClassLoaderDomain implements CacheLoader
{
   /** The log */
   private static final Logger log = Logger.getLogger(BaseClassLoaderDomain.class);

   /** The classloader system to which we belong */
   private BaseClassLoaderSystem system;
  
   /** The translators */
   private List<Translator> translators;

   /** The classloaders  in the order they were registered */
   private List<ClassLoaderInformation> classLoaders = new CopyOnWriteArrayList<ClassLoaderInformation>();
  
   /** The classloaders by package name */
   private Map<String, List<ClassLoaderInformation>> classLoadersByPackageName = new ConcurrentHashMap<String, List<ClassLoaderInformation>>();

   /** The global class cache */
   private Map<String, ClassCacheItem> globalClassCache = new ConcurrentHashMap<String, ClassCacheItem>();
  
   /** The global class black list */
   private Set<String> globalClassBlackList = new ConcurrentSet<String>();

   /** The global resource cache */
   private Map<String, URL> globalResourceCache = new ConcurrentHashMap<String, URL>();
  
   /** The global resource black list */
   private Set<String> globalResourceBlackList = new ConcurrentSet<String>();
  
   /** Keep track of the added order */
   private int order = 0;
  
   /**
    * Flush the internal caches
    */
   public void flushCaches()
   {
      globalClassCache.clear();
      globalClassBlackList.clear();
      globalResourceCache.clear();
      globalResourceBlackList.clear();

      for (ClassLoaderInformation info : classLoaders)
         info.flushCaches();
   }

   public int getClassBlackListSize()
   {
      return globalClassBlackList.size();
   }

   public int getClassCacheSize()
   {
      return globalClassCache.size();
   }

   public int getResourceBlackListSize()
   {
      return globalResourceBlackList.size();
   }

   public int getResourceCacheSize()
   {
      return globalResourceCache.size();
   }
  
   public Set<String> listClassBlackList()
   {
      return Collections.unmodifiableSet(globalClassBlackList);
   }

   public Map<String, String> listClassCache()
   {
      Map<String, String> result = new HashMap<String, String>(globalClassCache.size());
      for (Map.Entry<String, ClassCacheItem> entry : globalClassCache.entrySet())
         result.put(entry.getKey(), entry.getValue().toString());
      return result;
   }

   public Set<String> listResourceBlackList()
   {
      return Collections.unmodifiableSet(globalResourceBlackList);
   }

   public Map<String, URL> listResourceCache()
   {
      return Collections.unmodifiableMap(globalResourceCache);
   }

   /**
    * Get the classloader system
    *
    * @return the classloader system
    */
   protected synchronized BaseClassLoaderSystem getClassLoaderSystem()
   {
      return system;
   }
  
   /**
    * Get the classloader system
    *
    * @param system the classloader system
    */
   synchronized void setClassLoaderSystem(BaseClassLoaderSystem system)
   {
      if (system == null)
         shutdownDomain();
      this.system = system;
   }

   /**
    * Shutdown the domain<p>
    *
    * The default implementation just unregisters all classloaders
    */
   protected void shutdownDomain()
   {
      log.debug(toString() + " shutdown!");

      // Unregister all classloaders
      while (true)
      {
         Iterator<ClassLoaderInformation> iterator = classLoaders.iterator();
         if (iterator.hasNext() == false)
            break;

         while (iterator.hasNext())
         {
            ClassLoaderInformation info = iterator.next();
            if (info != null)
               unregisterClassLoader(info.getClassLoader());
         }
      }
     
      flushCaches();
   }
  
   /**
    * Whether the domain has classloaders
    *
    * @return true when the domain has classloaders
    */
   public boolean hasClassLoaders()
   {
      return classLoaders.isEmpty() == false;
   }
  
   /**
    * Whether to use load class for parent
    *
    * @return true to load class on the parent loader
    */
   public abstract boolean isUseLoadClassForParent();

   /**
    * Get the shutdownPolicy.
    *
    * @return the shutdownPolicy.
    */
   protected abstract ShutdownPolicy getShutdownPolicy();

   /**
    * Transform the byte code<p>
    *
    * By default, this delegates to the classloader system
    *
    * @param classLoader the classloader
    * @param className the class name
    * @param byteCode the byte code
    * @param protectionDomain the protection domain
    * @return the transformed byte code
    * @throws Exception for any error
    */
   protected byte[] transform(ClassLoader classLoader, String className, byte[] byteCode, ProtectionDomain protectionDomain) throws Exception
   {
      byte[] result = byteCode;

      BaseClassLoaderSystem system = getClassLoaderSystem();
      if (system != null)
         result = system.transform(classLoader, className, result, protectionDomain);

      return TranslatorUtils.applyTranslatorsOnTransform(getTranslators(), classLoader, className, result, protectionDomain);
   }

   /**
    * Load a class from the domain
    *
    * @param classLoader the classloader
    * @param name the class name
    * @param allExports whether we should look at all exports
    * @return the class
    * @throws ClassNotFoundException for any error
    */
   protected Class<?> loadClass(BaseClassLoader classLoader, String name, boolean allExports) throws ClassNotFoundException
   {
      boolean trace = log.isTraceEnabled();
     
      String path = ClassLoaderUtils.classNameToPath(name);

      // JBCL-101 TODO need to rework the blacklist
      //checkClassBlackList(classLoader, name, path, allExports, true);
     
      boolean findInParent = (isUseLoadClassForParent() == false);
     
      // Should we directly load from the parent?
      if (findInParent == false)
      {
         Class<?> clazz = loadClassBefore(name);
         if (clazz != null)
            return clazz;
      }
     
      Loader loader = findLoader(classLoader, path, allExports, findInParent);
      if (loader != null)
      {
         Thread thread = Thread.currentThread();
         ClassLoadingTask task = new ClassLoadingTask(name, classLoader, thread);
         ClassLoaderManager.scheduleTask(task, loader, false);
         Class<?> result = ClassLoaderManager.process(thread, task);
         ClassCacheItem item = globalClassCache.get(path);
         if (item != null)
            item.clazz = result;
         return result;
      }
     
      // Should we directly load from the parent?
      if (findInParent == false)
      {
         Class<?> clazz = loadClassAfter(name);
         if (clazz != null)
            return clazz;
      }

      // Finally see whether this is the JDK assuming it can load its classes from any classloader
      if (classLoader != null)
      {
         BaseClassLoaderPolicy policy = classLoader.getPolicy();
         ClassLoader hack = policy.isJDKRequest(name);
         if (hack != null)
         {
            if (trace)
               log.trace(this + " trying to load " + name + " using hack " + hack);
            Class<?> result = Class.forName(name, false, hack);
            if (result != null)
            {
               if (trace)
                  log.trace(this + " loaded from hack " + hack + " " + ClassLoaderUtils.classToString(result));
               globalClassCache.put(path, new ClassCacheItem(result));
               return result;
            }
         }
      }
     
      // Didn't find it
      return null;
   }

   /**
    * Find a loader for a class
    *
    * @param name the class resource name
    * @return the loader
    */
   protected Loader findLoader(String name)
   {
      return findLoader(null, name, true, true);
   }

   /**
    * Find a loader for a class
    *
    * @param classLoader the classloader
    * @param path the class resource name
    * @param allExports whether we should look at all exports
    * @param findInParent should we try the parent
    * @return the loader
    */
   Loader findLoader(BaseClassLoader classLoader, String path, boolean allExports, boolean findInParent)
   {
      boolean trace = log.isTraceEnabled();
      if (trace)
         log.trace(this + " findLoader " + path + " classLoader=" + classLoader + " allExports=" + allExports + " findInParent=" + findInParent);
     
      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());
     
      // Try the before attempt (e.g. from the parent)
      Loader loader = null;
      if (findInParent)
         loader = findBeforeLoader(path);
      if (loader != null)
         return loader;

      // Work out the rules
      ClassLoaderCache cache = null;
      BaseClassLoaderPolicy policy;
      if (classLoader != null)
      {
         policy = classLoader.getPolicy();
         cache = policy.getCache();
         if (policy.isImportAll())
            allExports = true;
      }

      // Next we try the old "big ball of mud" model     
      if (allExports)
      {
         loader = findLoaderInExports(classLoader, path, trace);
         if (loader != null)
            return loader;
      }
      else if (trace)
         log.trace(this + " not loading " + path + " from all exports");
     
      // Next we try the before imports
      if (cache != null)
      {
         loader = findLoaderInImports(cache, path, ImportType.BEFORE, trace);
         if (loader != null)
            return loader;
      }

      // Next use any requesting classloader, this will look at everything not just what it exports
      if (classLoader != null)
      {
         if (trace)
            log.trace(this + " trying to load " + path + " from requesting " + classLoader);
         if (classLoader.getResourceLocally(path) != null)
         {
            loader = classLoader.getLoader();
            policy = classLoader.getPolicy();
            if (policy.isCacheable())
               globalClassCache.put(path, new ClassCacheItem(loader));
            return loader;
         }
      }

      // Next we try the after imports
      if (cache != null)
      {
         loader = findLoaderInImports(cache, path, ImportType.AFTER, trace);
         if (loader != null)
            return loader;
      }

      // Try the after attempt (e.g. from the parent)
      if (findInParent)
         return findAfterLoader(path);
     
      return null;
   }
  
   /**
    * Load a resource from the domain
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param allExports whether we should look at all exports
    * @return the url
    */
   URL getResource(BaseClassLoader classLoader, String name, boolean allExports)
   {
      boolean trace = log.isTraceEnabled();

      // Try the classloader first
      if (classLoader != null)
      {
         if (trace)
            log.trace(this + " trying to get resource " + name + " from requesting " + classLoader);
         URL result = classLoader.getResourceLocally(name);
         if (result != null)
         {
            if (trace)
               log.trace(this + " got resource from requesting " + classLoader + " " + result);
            return result;
         }
      }

      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());

      // Try the before attempt
      URL result = beforeGetResource(name);
      if (result != null)
         return result;

      // Work out the rules
      ClassLoaderCache cache = null;
      BaseClassLoaderPolicy policy;
      if (classLoader != null)
      {
         policy = classLoader.getPolicy();
         cache = policy.getCache();
         if (policy.isImportAll())
            allExports = true;
      }

      // Next we try the old "big ball of mud" model     
      if (allExports)
      {
         result = getResourceFromExports(classLoader, name, trace);
         if (result != null)
            return result;
      }
      else if (trace)
         log.trace(this + " not getting resource " + name + " from all exports");
     
      // Next we try the imports
      if (cache != null)
      {
         result = getResourceFromImports(cache, name, ImportType.ALL, trace);
         if (result != null)
            return result;
      }

      // Try the after attempt
      result = afterGetResource(name);
      if (result != null)
         return result;
     
      // Didn't find it
      return null;
   }
  
   /**
    * Load resources from the domain
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param allExports whether we should look at all exports
    * @param urls the urls to add to
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   void getResources(BaseClassLoader classLoader, String name, Set<URL> urls, boolean allExports) throws IOException
   {
      boolean trace = log.isTraceEnabled();

      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());

      // Try the before attempt
      beforeGetResources(name, urls);

      // Work out the rules
      ClassLoaderInformation info = null;
      BaseClassLoaderPolicy policy;
      if (classLoader != null)
      {
         policy = classLoader.getPolicy();
         info = policy.getInformation();
         if (policy.isImportAll())
            allExports = true;
      }

      // Next we try the old "big ball of mud" model     
      if (allExports)
         getResourcesFromExports(classLoader, name, urls, trace);
      else if (trace)
         log.trace(this + " not getting resource " + name + " from all exports");
     
      // Next we try the imports
      if (info != null)
         getResourcesFromImports(info, name, urls, ImportType.ALL, trace);

      // Finally use any requesting classloader
      if (classLoader != null)
      {
         if (trace)
            log.trace(this + " trying to get resources " + name + " from requesting " + classLoader);
         classLoader.getResourcesLocally(name, urls);
      }

      // Try the after attempt
      afterGetResources(name, urls);
   }
  
   /**
    * Load a package from the domain
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param allExports whether we should look at all exports
    * @return the package
    */
   Package getPackage(BaseClassLoader classLoader, String name, boolean allExports)
   {
      boolean trace = log.isTraceEnabled();

      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());

      // Try the before attempt
      Package result = beforeGetPackage(name);
      if (result != null)
         return result;

      // Work out the rules
      ClassLoaderInformation info = null;
      BaseClassLoaderPolicy policy;
      if (classLoader != null)
      {
         policy = classLoader.getPolicy();
         info = policy.getInformation();
         if (policy.isImportAll())
            allExports = true;
      }

      // Next we try the old "big ball of mud" model     
      if (allExports)
      {
         result = getPackageFromExports(classLoader, name, trace);
         if (result != null)
            return result;
      }
      else if (trace)
         log.trace(this + " not getting package " + name + " from all exports");
     
      // Next we try the before imports
      if (info != null)
      {
         result = getPackageFromImports(info, name, ImportType.BEFORE, trace);
         if (result != null)
            return result;
      }

      // Finally use any requesting classloader
      if (classLoader != null)
      {
         if (trace)
            log.trace(this + " trying to get package " + name + " from requesting " + classLoader);
         result = classLoader.getPackageLocally(name);
         if (result != null)
         {
            if (trace)
               log.trace(this + " got package from requesting " + classLoader + " " + result);
            return result;
         }
      }

      // Next we try the after imports
      if (info != null)
      {
         result = getPackageFromImports(info, name, ImportType.AFTER, trace);
         if (result != null)
            return result;
      }

      // Try the after attempt
      result = afterGetPackage(name);
      if (result != null)
         return result;
     
      // Didn't find it
      return null;
   }
  
   /**
    * Load packages from the domain
    *
    * @param classLoader the classloader
    * @param packages the packages to add to
    * @param allExports whether we should look at all exports
    */
   void getPackages(BaseClassLoader classLoader, Set<Package> packages, boolean allExports)
   {
      boolean trace = log.isTraceEnabled();

      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());

      // Try the before attempt
      beforeGetPackages(packages);

      // Work out the rules
      ClassLoaderInformation info = null;
      BaseClassLoaderPolicy policy;
      if (classLoader != null)
      {
         policy = classLoader.getPolicy();
         info = policy.getInformation();
         if (policy.isImportAll())
            allExports = true;
      }

      // Next we try the old "big ball of mud" model     
      if (allExports)
         getPackagesFromExports(classLoader, packages, trace);
      else if (trace)
         log.trace(this + " not getting packages from all exports");
     
      // Next we try the imports
      if (info != null)
         getPackagesFromImports(info, packages, ImportType.ALL, trace);

      // Finally use any requesting classloader
      if (classLoader != null)
      {
         if (trace)
            log.trace(this + " trying to get packages from requesting " + classLoader);
         classLoader.getPackagesLocally(packages);
      }

      // Try the after attempt
      afterGetPackages(packages);
   }
  
   /**
    * Find a loader for class in exports
    *
    * @param classLoader the classloader
    * @param name the class resource name
    * @param trace whether trace is enabled
    * @return the loader
    */
   private Loader findLoaderInExports(BaseClassLoader classLoader, String name, boolean trace)
   {
      ClassCacheItem item = globalClassCache.get(name);
      if (item != null)
      {
         Loader loader = item.loader;
         if (loader != null)
         {
            if (trace)
               log.trace(this + " found loader " + loader + " in global class cache " + name);
            return loader;
         }
      }

      if (isBlackListedClass(name))
      {
         if (trace)
            log.trace(this + " class is black listed " + name);
         return null;
      }

      boolean canCache = true;
      boolean canBlackList = true;
     
      String packageName = ClassLoaderUtils.getResourcePackageName(name);
      List<ClassLoaderInformation> list = classLoadersByPackageName.get(packageName);
      if (trace)
         log.trace(this + " trying to load " + name + " from all exports of package " + packageName + " " + list);
      if (list != null && list.isEmpty() == false)
      {
         for (ClassLoaderInformation info : list)
         {
            BaseDelegateLoader exported = info.getExported();
           
            // See whether the policies allow caching/blacklisting
            BaseClassLoaderPolicy loaderPolicy = exported.getPolicy();
            if (loaderPolicy == null || loaderPolicy.isCacheable() == false)
               canCache = false;
            if (loaderPolicy == null || loaderPolicy.isBlackListable() == false)
               canBlackList = false;

            if (exported.getResource(name) != null)
            {
               if (canCache)
                  globalClassCache.put(name, new ClassCacheItem(exported));
               return exported;
            }
         }
      }
      // Here is not found in the exports so can we blacklist it?
      if (canBlackList)
         globalClassBlackList.add(name);
     
      return null;
   }

   /**
    * Check whether this is a black listed class
    *
    * @param name the class name
    * @return true when black listed, false otherwise
    */
   protected boolean isBlackListedClass(String name)
   {
      return globalClassBlackList.contains(name);
   }

   /**
    * Load a resource from the exports
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param trace whether trace is enabled
    * @return the url
    */
   private URL getResourceFromExports(BaseClassLoader classLoader, String name, boolean trace)
   {
      URL result = globalResourceCache.get(name);
      if (result != null)
      {
         if (trace)
            log.trace(this + " got resource from cache " + name);
      }
     
      if (globalResourceBlackList.contains(name))
      {
         if (trace)
            log.trace(this + " resource is black listed, not looking at exports " + name);
         return null;
      }

      boolean canCache = true;
      boolean canBlackList = true;
     
      String packageName = ClassLoaderUtils.getResourcePackageName(name);
      List<ClassLoaderInformation> list = classLoadersByPackageName.get(packageName);
      if (trace)
         log.trace(this + " trying to get resource " + name + " from all exports " + list);
      if (list != null && list.isEmpty() == false)
      {
         for (ClassLoaderInformation info : list)
         {
            BaseDelegateLoader loader = info.getExported();
           
            // See whether the policies allow caching/blacklisting
            BaseClassLoaderPolicy loaderPolicy = loader.getPolicy();
            if (loaderPolicy == null || loaderPolicy.isCacheable() == false)
               canCache = false;
            if (loaderPolicy == null || loaderPolicy.isBlackListable() == false)
               canBlackList = false;

            result = loader.getResource(name);
            if (result != null)
            {
               if (canCache)
                  globalResourceCache.put(name, result);
               return result;
            }
         }
      }
      // Here is not found in the exports so can we blacklist it?
      if (canBlackList)
         globalResourceBlackList.add(name);
     
      return null;
   }
  
   /**
    * Load resources from the exports
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param urls the urls to add to
    * @param trace whether trace is enabled
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   void getResourcesFromExports(BaseClassLoader classLoader, String name, Set<URL> urls, boolean trace) throws IOException
   {
      String packageName = ClassLoaderUtils.getResourcePackageName(name);
      List<ClassLoaderInformation> list = classLoadersByPackageName.get(packageName);
      if (trace)
         log.trace(this + " trying to get resources " + name + " from all exports " + list);
      if (list != null && list.isEmpty() == false)
      {
         for (ClassLoaderInformation info : list)
         {
            BaseDelegateLoader loader = info.getExported();
            loader.getResources(name, urls);
         }
      }
   }
  
   /**
    * Load a package from the exports
    *
    * @param classLoader the classloader
    * @param name the package name
    * @param trace whether trace is enabled
    * @return the package
    */
   private Package getPackageFromExports(BaseClassLoader classLoader, String name, boolean trace)
   {
      List<ClassLoaderInformation> list = classLoadersByPackageName.get(name);
      if (trace)
         log.trace(this + " trying to get package " + name + " from all exports " + list);
      if (list != null && list.isEmpty() == false)
      {
         for (ClassLoaderInformation info : list)
         {
            BaseDelegateLoader loader = info.getExported();

            Package result = loader.getPackage(name);
            if (result != null)
               return result;
         }
      }
      return null;
   }
  
   /**
    * Load packages from the exports
    *
    * @param classLoader the classloader
    * @param packages the packages to add to
    * @param trace whether trace is enabled
    */
   void getPackagesFromExports(BaseClassLoader classLoader, Set<Package> packages, boolean trace)
   {
      List<ClassLoaderInformation> list = classLoaders;
      if (trace)
         log.trace(this + " trying to get all packages from all exports " + list);
      if (list != null && list.isEmpty() == false)
      {
         for (ClassLoaderInformation info : list)
         {
            BaseDelegateLoader loader = info.getExported();
            loader.getPackages(packages);
         }
      }
   }

   /**
    * Find a loader for a class in imports
    *
    * @param cache the classloader cache
    * @param name the class resource name
    * @param type the import type
    * @param trace whether trace is enabled
    * @return the loader
    */
   Loader findLoaderInImports(ClassLoaderCache cache, String name, ImportType type, boolean trace)
   {
      boolean relevant = cache.isRelevant(type);
      if (relevant == false)
      {
         if (trace)
            log.trace(this + " not loading " + name + " from imports, it's not relevant: " + cache.getInfo(type));
         return null;
      }

      Loader loader = cache.getCachedLoader(name);
      if (loader != null)
      {
         if (trace)
            log.trace(this + " found in import cache " + name);
         return loader;
      }
     
      if (cache.isBlackListedClass(name))
      {
         if (trace)
            log.trace(this + " class is black listed in imports " + name);
         return null;
      }

      if (trace)
         log.trace(this + " trying to load " + name + " from imports: " + cache.getInfo(type));

      loader = cache.findLoader(type, name);
      if (loader != null)
         return loader;

      if (type == ImportType.AFTER) // TODO -- is this really OK?
         cache.blackListClass(name);
      return null;
   }
  
   /**
    * Load a resource from the imports
    *
    * @param cache the classloader cache
    * @param name the resource name
    * @param type the import type
    * @param trace whether trace is enabled
    * @return the url
    */
   private URL getResourceFromImports(ClassLoaderCache cache, String name, ImportType type, boolean trace)
   {
      boolean relevant = cache.isRelevant(type);
      if (relevant == false)
      {
         if (trace)
            log.trace(this + " not getting resource " + name + " from imports, it's not relevant: " + cache.getInfo(type));
         return null;
      }

      URL url = cache.getCachedResource(name);
      if (url != null)
      {
         if (trace)
            log.trace(this + " found resource in import cache " + name);
         return url;
      }
     
      if (cache.isBlackListedResource(name))
      {
         if (trace)
            log.trace(this + " resource is black listed in imports " + name);
         return null;
      }

      if (trace)
         log.trace(this + " trying to get resource " + name + " from imports: " + cache.getInfo(type));


      url = cache.findResource(type, name);
      if (url != null)
         return url;

      if (type == ImportType.AFTER) // TODO -- check
         cache.blackListResource(name);
      return null;
   }
  
   /**
    * Load resources from the imports
    *
    * @param info the classloader info
    * @param name the resource name
    * @param urls the urls to add to
    * @param type the import type
    * @param trace whether trace is enabled
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   void getResourcesFromImports(ClassLoaderInformation info, String name, Set<URL> urls, ImportType type, boolean trace) throws IOException
   {
      List<? extends DelegateLoader> delegates = info.getDelegates(type);
      if (delegates == null || delegates.isEmpty())
      {
         if (trace)
            log.trace(this + " not getting resource " + name + " from imports it has no delegates");
         return;
      }
      if (trace)
         log.trace(this + " trying to get resources " + name + " from imports " + delegates + " for " + info.getClassLoader());
      for (DelegateLoader delegate : delegates)
         delegate.getResources(name, urls);
   }
  
   /**
    * Load a package from the imports
    *
    * @param info the classloader information
    * @param name the pacakge name
    * @param type the import type
    * @param trace whether trace is enabled
    * @return the package
    */
   private Package getPackageFromImports(ClassLoaderInformation info, String name, ImportType type, boolean trace)
   {
      List<? extends DelegateLoader> delegates = info.getDelegates(type);
      if (delegates == null || delegates.isEmpty())
      {
         if (trace)
            log.trace(this + " not getting package " + name + " from imports it has no delegates");
         return null;
      }

      if (trace)
         log.trace(this + " trying to get package " + name + " from imports " + delegates + " for " + info.getClassLoader());

      for (DelegateLoader delegate : delegates)
      {
         Package result = delegate.getPackage(name);
         if (result != null)
            return result;
      }
      return null;
   }
  
   /**
    * Load packages from the imports
    *
    * @param info the classloader info
    * @param packages the packages to add to
    * @param type the import type
    * @param trace whether trace is enabled
    */
   void getPackagesFromImports(ClassLoaderInformation info, Set<Package> packages, ImportType type, boolean trace)
   {
      List<? extends DelegateLoader> delegates = info.getDelegates(type);
      if (delegates == null || delegates.isEmpty())
      {
         if (trace)
            log.trace(this + " not getting all packages from imports it has no delegates");
         return;
      }
      if (trace)
         log.trace(this + " trying to get all pacakges from imports " + delegates + " for " + info.getClassLoader());
      for (DelegateLoader delegate : delegates)
         delegate.getPackages(packages);
   }

   /**
    * Invoked before classloading is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param name the class name
    * @return the loader if found or null otherwise
    */
   protected abstract Class<?> loadClassBefore(String name);
  
   /**
    * Invoked after classloading is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param name the class name
    * @return the loader if found or null otherwise
    */
   protected abstract Class<?> loadClassAfter(String name);

   /**
    * Invoked before classloading is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param name the class resource name
    * @return the loader if found or null otherwise
    */
   protected abstract Loader findBeforeLoader(String name);
  
   /**
    * Invoked after classloading is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param name the class resource name
    * @return the loader if found or null otherwise
    */
   protected abstract Loader findAfterLoader(String name);
  
   /**
    * Invoked before getResources is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param name the resource name
    * @param urls the urls to add to
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   protected abstract void beforeGetResources(String name,  Set<URL> urls) throws IOException;
  
   /**
    * Invoked after getResources is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param name the resource name
    * @param urls the urls to add to
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   protected abstract void afterGetResources(String name, Set<URL> urls) throws IOException;
  
   /**
    * Invoked before getResource is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param name the resource name
    * @return the url if found or null otherwise
    */
   protected abstract URL beforeGetResource(String name);
  
   /**
    * Invoked after getResource is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param name the resource name
    * @return the url if found or null otherwise
    */
   protected abstract URL afterGetResource(String name);
  
   /**
    * Invoked before getPackages is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param packages the packages to add to
    */
   protected abstract void beforeGetPackages(Set<Package> packages);
  
   /**
    * Invoked after getPackages is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param packages the packages to add to
    */
   protected abstract void afterGetPackages(Set<Package> packages);
  
   /**
    * Invoked before getPackage is attempted to allow a preload attempt, e.g. from the parent
    *
    * @param name the package name
    * @return the package if found or null otherwise
    */
   protected abstract Package beforeGetPackage(String name);
  
   /**
    * Invoked after getPackage is attempted to allow a postload attempt, e.g. from the parent
    *
    * @param name the package name
    * @return the url if found or null otherwise
    */
   protected abstract Package afterGetPackage(String name);
  
   public Class<?> loadClass(String name)
   {
      try
      {
         return loadClass(null, name, true);
      }
      catch (ClassNotFoundException e)
      {
         return null;
      }
   }
  
   /**
    * Load a class from the domain
    *
    * @param classLoader the classloader
    * @param name the class name
    * @return the class
    * @throws ClassNotFoundException for any error
    */
   Class<?> loadClass(BaseClassLoader classLoader, String name) throws ClassNotFoundException
   {
      return loadClass(classLoader, name, false);
   }
  
   public URL getResource(String name)
   {
      return getResource(null, name, true);
   }
  
   /**
    * Get a resource from the domain
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @return the url
    */
   URL getResource(BaseClassLoader classLoader, String name)
   {
      return getResource(classLoader, name, false);
   }
  
   // FindBugs: The Set doesn't use equals/hashCode
   public void getResources(String name, Set<URL> urls) throws IOException
   {
      getResources(null, name, urls, true);
   }
  
   /**
    * Get a resource from the domain
    *
    * @param classLoader the classloader
    * @param name the resource name
    * @param urls the urls to add to
    * @throws IOException for any error
    */
   // FindBugs: The Set doesn't use equals/hashCode
   void getResources(BaseClassLoader classLoader, String name, Set<URL> urls) throws IOException
   {
      getResources(classLoader, name, urls, false);
   }
  
   public Package getPackage(String name)
   {
      return getPackage(null, name, true);
   }
  
   /**
    * Get a package from the specified classloader
    *
    * @param classLoader the classloader
    * @param name the package name
    * @return the package
    */
   Package getPackage(BaseClassLoader classLoader, String name)
   {
      return getPackage(classLoader, name, false);
   }
  
   public void getPackages(Set<Package> packages)
   {
      getPackages(null, packages, true);
   }
  
   /**
    * Get the packages from a specified classloader
    *
    * @param classLoader the classloader
    * @param packages the packages
    */
   void getPackages(BaseClassLoader classLoader, Set<Package> packages)
   {
      getPackages(classLoader, packages, false);
   }

   /**
    * Get the classloader policy associated with an object
    *
    * @param object the  object
    * @return the classloader policy or null if one is not associated
    */
   protected ClassLoaderPolicy getClassLoaderPolicy(Object object)
   {
      if (object instanceof BaseClassLoader)
         return ((BaseClassLoader) object).getPolicy();
     
      if (object instanceof BaseClassLoaderSource)
         return getClassLoaderPolicy(((BaseClassLoaderSource) object).getClassLoader());
     
      return null;
   }
  
   /**
    * A long version of toString()
    *
    * @return the long string
    */
   public String toLongString()
   {
      StringBuilder builder = new StringBuilder();
      builder.append(getClass().getSimpleName());
      builder.append("@").append(Integer.toHexString(System.identityHashCode(this)));
      builder.append("{");
      toLongString(builder);
      builder.append('}');
      return builder.toString();
   }
  
   /**
    * For subclasses to add information for toLongString()
    *
    * @param builder the builder
    */
   protected void toLongString(StringBuilder builder)
   {
   }
  
   /**
    * Invoked before adding a classloader policy
    *
    * @param classLoader the classloader
    * @param policy the classloader policy
    */
   protected void beforeRegisterClassLoader(ClassLoader classLoader, ClassLoaderPolicy policy)
   {
      // nothing
   }

   /**
    * Invoked after adding a classloader policy
    *
    * @param classLoader the classloader
    * @param policy the classloader policy
    */
   protected void afterRegisterClassLoader(ClassLoader classLoader, ClassLoaderPolicy policy)
   {
      // nothing
   }
  
   /**
    * Invoked before adding a classloader policy
    *
    * @param classLoader the classloader
    * @param policy the classloader policy
    */
   protected void beforeUnregisterClassLoader(ClassLoader classLoader, ClassLoaderPolicy policy)
   {
      // nothing
   }
  
   /**
    * Invoked after adding a classloader policy
    *
    * @param classLoader the classloader
    * @param policy the classloader policy
    */
   protected void afterUnregisterClassLoader(ClassLoader classLoader, ClassLoaderPolicy policy)
   {
      // nothing
   }

   /**
    * Get the parent classloader
    *
    * @return the parent classloader
    */
   protected ClassLoader getParentClassLoader()
   {
      return getClass().getClassLoader();
   }

   /**
    * Register a classloader
    *
    * @param classLoader the classloader
    */
   void registerClassLoader(BaseClassLoader classLoader)
   {
      log.debug(this + " registerClassLoader " + classLoader.toString());

      if (getClassLoaderSystem() == null)
         throw new IllegalStateException("Domain is not registered with a classloader system: " + toLongString());
     
      ClassLoaderPolicy policy = classLoader.getPolicy();
      BaseDelegateLoader exported = policy.getExported();
      if (exported != null && exported.getPolicy() == null)
         throw new IllegalStateException("The exported delegate " + exported + " is too lazy for " + policy.toLongString());
     
      try
      {
         beforeRegisterClassLoader(classLoader, policy);
      }
      catch (Throwable t)
      {
         log.warn("Error in beforeRegisterClassLoader: " + this + " classLoader=" + classLoader.toLongString(), t);
      }
     
      BaseClassLoaderPolicy basePolicy = classLoader.getPolicy();
      basePolicy.setClassLoaderDomain(this);

      // FINDBUGS: This synchronization is correct - more than addIfNotPresent behaviour
      synchronized (classLoaders)
      {
         // Create the information
         ClassLoaderInformation info = new ClassLoaderInformation(classLoader, policy, order++);
         classLoaders.add(info);
         basePolicy.setInformation(info);

         // Index the packages
         String[] packageNames = policy.getPackageNames();
         if (packageNames != null && info.getExported() != null)
         {
            for (String packageName : packageNames)
            {
               List<ClassLoaderInformation> list = classLoadersByPackageName.get(packageName);
               if (list == null)
               {
                  list = new CopyOnWriteArrayList<ClassLoaderInformation>();
                  classLoadersByPackageName.put(packageName, list);
               }
               list.add(info);
               log.trace("Registered " + policy + " as providing package=" + packageName);
            }
         }
        
         flushCaches();
      }

      ClassLoaderCache cache = policy.getCache();
      if (cache != null)
         cache.flushCaches();

      try
      {
         afterRegisterClassLoader(classLoader, classLoader.getPolicy());
      }
      catch (Throwable t)
      {
         log.warn("Error in afterRegisterClassLoader: " + this + " classLoader=" + classLoader.toLongString(), t);
      }
   }
  
   /**
    * Remove a classloader
    *
    * @param classLoader the classloader
    */
   void unregisterClassLoader(BaseClassLoader classLoader)
   {
      log.debug(this + " unregisterClassLoader " + classLoader.toString());

      try
      {
         beforeUnregisterClassLoader(classLoader, classLoader.getPolicy());
      }
      catch (Throwable t)
      {
         log.warn("Error in beforeUnegisterClassLoader: " + this + " classLoader=" + classLoader.toLongString(), t);
      }

      BaseClassLoaderPolicy policy = classLoader.getPolicy();
      ShutdownPolicy shutdownPolicy = determineShutdownPolicy(policy);

      boolean shutdownNow = (ShutdownPolicy.UNREGISTER == shutdownPolicy);
      if (shutdownNow)
         policy.unsetClassLoaderDomain(this);

      // FINDBUGS: This synchronization is correct - more than addIfNotPresent behaviour
      synchronized (classLoaders)
      {
         // Remove the classloader
         ClassLoaderInformation info = policy.getInformation();
         classLoaders.remove(info);
         if (shutdownNow)
            policy.setInformation(null);
        
         // Remove the package index
         String[] packageNames = policy.getPackageNames();
         if (packageNames != null && info.getExported() != null)
         {
            for (String packageName : packageNames)
            {
               List<ClassLoaderInformation> list = classLoadersByPackageName.get(packageName);
               if (list != null)
               {
                  list.remove(info);
                  log.trace("Unregistered " + policy + " as providing package=" + packageName);
                  if (list.isEmpty())
                     classLoadersByPackageName.remove(packageName);
               }
            }
         }

         flushCaches();
      }

      ClassLoaderCache cache = policy.getCache();
      if (cache != null)
         cache.flushCaches();

      try
      {
         afterUnregisterClassLoader(classLoader, classLoader.getPolicy());
      }
      catch (Throwable t)
      {
         log.warn("Error in afterUnegisterClassLoader: " + this + " classLoader=" + classLoader.toLongString(), t);
      }
   }

   /**
    * Determine the shutdown policy for the classloader policy
    *
    * @param policy the classloader policy
    * @return the shutdown policy
    */
   ShutdownPolicy determineShutdownPolicy(BaseClassLoaderPolicy policy)
   {
      if (policy == null)
         throw new IllegalArgumentException("Null policy");

      // From the policy
      ShutdownPolicy shutdownPolicy = policy.getShutdownPolicy();
     
      // From the domain (us)
      if (shutdownPolicy == null)
         shutdownPolicy = this.getShutdownPolicy();
     
      // From the clasloader system
      if (shutdownPolicy == null)
      {
         BaseClassLoaderSystem system = getClassLoaderSystem();
         if (system != null)
            shutdownPolicy = system.getShutdownPolicy();
      }
     
      // The default behaviour
      if (shutdownPolicy == null)
         shutdownPolicy = ShutdownPolicy.UNREGISTER;
     
      return shutdownPolicy;
   }
  
   /**
    * Get all the classloaders
    *
    * @return the list of classloaders
    */
   protected List<ClassLoader> getAllClassLoaders()
   {
      List<ClassLoader> result = new ArrayList<ClassLoader>();
      for (ClassLoaderInformation info : classLoaders)
         result.add(info.getClassLoader());
      return result;
   }

   /**
    * Get a map of packages to classloader
    *
    * @return a map of packages to a list of classloaders for that package
    */
   protected Map<String, List<ClassLoader>> getClassLoadersByPackage()
   {
      HashMap<String, List<ClassLoader>> result = new HashMap<String, List<ClassLoader>>();
      for (Entry<String, List<ClassLoaderInformation>> entry : classLoadersByPackageName.entrySet())
      {
         List<ClassLoader> cls = new ArrayList<ClassLoader>();
         for (ClassLoaderInformation info : entry.getValue())
            cls.add(info.getClassLoader());
         result.put(entry.getKey(), cls);
      }
      return result;
   }

   protected List<ClassLoader> getClassLoaders(String packageName)
   {
      if (packageName == null)
         throw new IllegalArgumentException("Null package name");
     
      List<ClassLoader> result = new ArrayList<ClassLoader>();
      List<ClassLoaderInformation> infos = classLoadersByPackageName.get(packageName);
      if (infos != null)
      {
         for (ClassLoaderInformation info : infos)
            result.add(info.getClassLoader());
      }
      return result;
   }

   public Class<?> checkClassCache(BaseClassLoader classLoader, String name, String path, boolean allExports)
   {
      Class<?> result = checkCacheBefore(classLoader, name, path, allExports);
      if (result != null)
         return result;

      result = checkClassCacheLocally(classLoader, name, path, allExports);
      if (result != null)
         return result;

      result = checkCacheAfter(classLoader, name, path, allExports);
      if (result != null)
         return result;

      return null;
   }

   /**
    * Check the class cache
    *
    * @param classLoader the reference classloader (possibly null)
    * @param name the name of the class
    * @param path the path of the class resource
    * @param allExports whether to look at all exports
    * @return the class if cached
    */
   Class<?> checkClassCacheLocally(BaseClassLoader classLoader, String name, String path, boolean allExports)
   {
      if (allExports)
      {
         ClassCacheItem item = globalClassCache.get(path);
         if (item != null)
         {
            if (log.isTraceEnabled())
               log.trace("Found " + name + " in global cache: " + this);

            return item.clazz;
         }
      }
      else
      {
         BaseClassLoaderPolicy policy = classLoader.getPolicy();
         if (policy != null)
         {
            ClassLoaderCache cache = policy.getCache();
            if (cache != null)
            {
               Loader loader = cache.getCachedLoader(path);
               return (loader != null) ? loader.loadClass(name) : null;
            }
         }
      }
      return null;
   }

   /**
    * Check the class blacklist
    *
    * @param classLoader the classloader (possibly null)
    * @param name the name
    * @param path the path of the class resource
    * @param allExports whether to look at all exports
    * @param failIfBlackListed <code>true</code> if a blacklisted class should
    *                          result in ClassNotFoundException; <code>false</code>
    *                          if a <code>null</code> return value is acceptable
    * @throws ClassNotFoundException when the class is blacklisted
    */
   void checkClassBlackList(BaseClassLoader classLoader, String name, String path, boolean allExports, boolean failIfBlackListed) throws ClassNotFoundException
   {
      if (failIfBlackListed)
      {
         if (allExports)
         {
            if (isBlackListedClass(path))
            {
               if (log.isTraceEnabled())
                  log.trace("Found " + name + " in global blacklist");
               throw new ClassNotFoundException(name + " not found - blacklisted");
            }
         }
         else
         {
            BaseClassLoaderPolicy policy = classLoader.getPolicy();
            if (policy != null)
            {
               ClassLoaderCache cache = policy.getCache();
               if (cache != null && cache.isBlackListedClass(path))
               {
                  if (log.isTraceEnabled())
                     log.trace("Found " + name + " in policy cache blacklist: " + cache.getInfo(ImportType.ALL));
                  throw new ClassNotFoundException(name + " not found - blacklisted");
               }
            }
         }
      }
   }
  
   /**
    * Check the cache and blacklist
    *
    * @param classLoader the classloader (possibly null)
    * @param name the name
    * @param path the path of the class resource
    * @param allExports whether to look at all exports
    * @param failIfBlackListed <code>true</code> if a blacklisted class should
    *                          result in ClassNotFoundException; <code>false</code>
    *                          if a <code>null</code> return value is acceptable
    * @return the class when found in the cache
    * @throws ClassNotFoundException when the class is blacklisted and
    *                               <code>failIfBlackListed</code> is <code>true</code>
    */
   protected Class<?> checkClassCacheAndBlackList(BaseClassLoader classLoader, String name, String path, boolean allExports, boolean failIfBlackListed) throws ClassNotFoundException
   {
      if (path == null)
         path = ClassLoaderUtils.classNameToPath(name);

      Class<?> cached = checkClassCache(classLoader, name, path, allExports);
      if (cached != null)
         return cached;

      checkClassBlackList(classLoader, name, path, allExports, failIfBlackListed);
     
      return null;
   }

   /**
    * Check the cache before checking this domain.
    * e.g. check parent's domain cache
    *
    * @param classLoader the classloader (possibly null)
    * @param name the name
    * @param path the path of the class resource
    * @param allExports whether to look at all exports
    * @return the class when found in the cache
    */
   protected Class<?> checkCacheBefore(BaseClassLoader classLoader, String name, String path, boolean allExports)
   {
      return null;
   }

   /**
    * Check the cache after checking before domain.
    * e.g. check parent's domain cache only if this one blacklisted the resource
    *
    * @param classLoader the classloader (possibly null)
    * @param name the name
    * @param path the path of the class resource
    * @param allExports whether to look at all exports
    * @return the class when found in the cache
    */
   protected Class<?> checkCacheAfter(BaseClassLoader classLoader, String name, String path, boolean allExports)
   {
      return null;
   }

   /**
    * Cleans the entry with the given name from the blackList
    *
    * @param name the name of the resource to clear from the blackList
    */
   protected void clearBlackList(String name)
   {
      if (globalClassBlackList != null)
      {
         globalClassBlackList.remove(name);
      }
      if (globalResourceBlackList != null)
      {
         globalResourceBlackList.remove(name);
      }

      // Need to clear the import caches as well
      List<ClassLoaderInformation> infos = classLoaders;
      for (ClassLoaderInformation info : infos)
         info.clearBlackList(name);
   }

   /**
    * Get the policy's translators.
    *
    * @return the translators
    */
   public synchronized List<Translator> getTranslators()
   {
      if (translators == null || translators.isEmpty())
         return Collections.emptyList();
      else
         return Collections.unmodifiableList(translators);
   }

   /**
    * Set the translators.
    *
    * @param translators the translators
    */
   public synchronized void setTranslators(List<Translator> translators)
   {
      this.translators = translators;
   }

   /**
    * Add the translator.
    *
    * @param translator the translator to add
    * @throws IllegalArgumentException for null translator
    */
   public synchronized void addTranslator(Translator translator)
   {
      if (translator == null)
         throw new IllegalArgumentException("Null translator");

      if (translators == null)
         translators = new ArrayList<Translator>();

      translators.add(translator);
   }

   /**
    * Remove the translator.
    *
    * @param translator the translator to remove
    * @throws IllegalArgumentException for null translator
    */
   public synchronized void removeTranslator(Translator translator)
   {
      if (translator == null)
         throw new IllegalArgumentException("Null translator");

      if (translators != null)
         translators.remove(translator);
   }

   /**
    * ClassCacheItem.
    */
   static class ClassCacheItem
   {
      Loader loader;
      Class<?> clazz;
     
      public ClassCacheItem(Loader loader)
      {
         this.loader = loader;
      }
     
      public ClassCacheItem(Class<?> clazz)
      {
         this.clazz = clazz;
      }

      @Override
      public String toString()
      {
         if (loader != null)
            return loader.toString();
         return "";
      }
   }
}
TOP

Related Classes of org.jboss.classloader.spi.base.BaseClassLoaderDomain

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.