Package org.jboss.mx.loading

Source Code of org.jboss.mx.loading.LoadMgr$PkgClassLoader

/*
* 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.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.WeakHashMap;

import org.jboss.mx.logging.Logger;
import org.jboss.mx.logging.SystemLogger;
import org.jboss.mx.loading.ClassLoadingTask.ThreadTask;


/** A utility class used by the UnifiedClassLoader3 to manage the thread based
* class loading tasks.
*
* @author Scott.Stark@jboss.org
* @version $Revision: 1.3 $
*/
public class LoadMgr
{
   private static Logger log = SystemLogger.getLogger(LoadMgr.class);
   private static Object registrationLock = new Object();

   /** A Map<UnifiedClassLoader3, Thread> of the active loadClass UCL3/threads
    */
   private static Map loadClassThreads = Collections.synchronizedMap(new HashMap());
   /** A Map<Thread, LinkedList<ThreadTask> > of the class loading tasks
    * associated with a thread
    */
   private static Map loadTasksByThread = Collections.synchronizedMap(new WeakHashMap());

   /** 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.
    */
   static class PkgClassLoader
   {
      UnifiedClassLoader3 ucl;
      int order;
      PkgClassLoader(UnifiedClassLoader3 ucl)
      {
         this(ucl, Integer.MAX_VALUE);
      }
      PkgClassLoader(UnifiedClassLoader3 ucl, int order)
      {
         this.ucl = ucl;
         this.order = order;
      }
   }

   /** 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.
    */
   static void registerLoaderThread(UnifiedClassLoader3 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 )
         {
            LinkedList taskList = (LinkedList) loadTasksByThread.get(t);
            if( taskList == null )
            {
               taskList = new LinkedList();
               loadTasksByThread.put(t, taskList);
               if( log.isTraceEnabled() )
                  log.trace("created new task list");
            }
         }
         registrationLock.notify();
      }
   }

   /** 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 task
    * requesting UCL3. If these fail then one or more ThreadTask are created
    * to complete the ClassLoadingTask. The ThreadTask are assigned to the
    * threads that own the associated UCL3 monitor.
    *
    * @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.
    */
   static synchronized 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 )
      {
         if( trace )
            log.trace("End beginLoadTask, loadClassFromCache");
         task.loadedClass = cls;
         task.state = ClassLoadingTask.FINISHED;
         return true;
      }
      cls = repository.loadClassFromClassLoader(task.classname, false,
         task.requestingClassLoader);
      if( cls != null )
      {
         if( trace )
            log.trace("End beginLoadTask, loadClassFromClassLoader");
         task.loadedClass = cls;
         task.state = ClassLoadingTask.FINISHED;
         return true;
      }

      // Get the set of class loaders from the packages map
      HashSet pkgSet = repository.getPackageClassLoaders(task.classname);
      // If no pkg match was found there is no point looking any further
      if( pkgSet == null || pkgSet.size() == 0 )
      {
         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();
      while( iter.hasNext() )
      {
         Object next = iter.next();
         UnifiedClassLoader3 ucl;
         int order = 0;
         // This may be either a PkgClassLoader or a UCL3
         if( next instanceof UnifiedClassLoader3 )
         {
            ucl = (UnifiedClassLoader3) next;
         }
         else
         {
            PkgClassLoader pkgUcl = (PkgClassLoader) next;
            ucl = pkgUcl.ucl;
            order = pkgUcl.order;
         }

         scheduleTask(task, ucl, 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 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.
    */
   static synchronized void nextTask(Thread t, ClassLoadingTask task,
      UnifiedLoaderRepository3 repository)
   {
      boolean trace = log.isTraceEnabled();
      LinkedList taskList = (LinkedList) loadTasksByThread.get(t);
      // There may not be any ThreadTasks
      if( taskList.size() == 0 )
      {
         if( trace )
            log.trace("Begin/End nextTask(-1), task="+task);
         /* There are no more tasks for the calling thread to execute, so the
         calling thread must wait until the task.threadTaskCount reaches 0
          */
         if( task.threadTaskCount != 0 )
         {
            task.state = ClassLoadingTask.WAIT_ON_EVENT;
         }
         return;
      }

      ThreadTask threadTask = (ThreadTask) taskList.removeFirst();
      ClassLoadingTask loadTask = threadTask.getLoadTask();

      if( trace )
         log.trace("Begin nextTask("+taskList.size()+"), LoadTask="+loadTask);
      try
      {
         if( threadTask.t == null )
         {
            if( trace )
               log.trace("Rescheduling threadTask="+threadTask);
            // This is a task without a thread, schedule it
            scheduleTask(loadTask, threadTask.ucl, threadTask.order, true, trace);
         }
         else
         {
            if( trace )
               log.trace("Running threadTask="+threadTask);
            // Load the class using this thread
            threadTask.run();
         }
      }
      catch(Throwable e)
      {
         loadTask.loadException = 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);
            loadClassThreads.remove(threadTask.ucl);
            synchronized( threadTask.ucl )
            {
               threadTask.ucl.release();
               threadTask.ucl.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();
            // Place the loaded class into the repositry cache
            repository.cacheLoadedClass(threadTask.getClassname(),
               loadedClass, loader);
         }
         synchronized( loadTask )
         {
            loadTask.state = ClassLoadingTask.FINISHED;
            loadTask.notify();
         }
      }
      if( trace )
         log.trace("End nextTask("+taskList.size()+"), task="+loadTask);
   }

   /** Complete a ClassLoadingTask. This is called by UCL3.loadClass to in
    */
   static synchronized void endLoadTask(ClassLoadingTask task)
   {
      boolean trace = log.isTraceEnabled();
      if( trace )
         log.trace("Begin endLoadTask, task="+task);
      loadClassThreads.remove(task.requestingClassLoader);
      // Any ThreadTasks associated with this thread must be reassigned
      LinkedList taskList = (LinkedList) loadTasksByThread.get(task.requestingThread);
      int size = taskList != null ? taskList.size() : 0;
      synchronized( taskList )
      {
         for(int i = 0; i < size; i ++)
         {
            ThreadTask threadTask = (ThreadTask) taskList.removeFirst();
            ClassLoadingTask loadTask = threadTask.getLoadTask();
            // Insert the task into the requestingThread task list
            if( trace )
               log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
            threadTask.t = null;
            LinkedList toTaskList = (LinkedList) loadTasksByThread.get(loadTask.requestingThread);
            toTaskList.add(0, threadTask);
            /* Need to notify the loadTask in case the UCL is waiting for these
            tasks that are being reassigned to complete
            */
            synchronized( loadTask )
            {
               loadTask.state = ClassLoadingTask.NEXT_EVENT;
               loadTask.notify();
            }
         }
      }
   }

   /** This simply synchronizes resource loading against the LoadMgr class
    * to ensure class loading does not interfere.
    */
   static synchronized URL getResource(String name, UnifiedClassLoader3 ucl,
      UnifiedLoaderRepository3 repository)
   {
      return repository.getResource(name, ucl);
   }

   /** 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, UnifiedClassLoader3 ucl,
      int order, boolean reschedule, boolean trace) throws ClassNotFoundException
   {
      Thread t = null;
      boolean releaseInNextTask = false;
      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.
             */
            if( 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( t == null )
               {
                  String msg = "Failed to obtain UCL thread after notify,"
                     + " classame: "+task.classname;
                  throw new ClassNotFoundException(msg);
               }
               if( trace )
                  log.trace("Notified that UCL owner is: "+t);
            }
            /* This task requestingThread now owns the UCL loadLock and will
               be able to use the UCL for class loading. We must register
               as the UCL owner via the loadClassThreads map. The UCL
               loadLock will have to be released in nextTask, and the
               loadClassThreads map entry removed.
             */
            else
            {
               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);
               }
            }
         }
      }

      ThreadTask subtask = task.newThreadTask(ucl, t, order, reschedule,
         releaseInNextTask);
      // Add the task to the owning thread
      LinkedList taskList = (LinkedList) loadTasksByThread.get(t);
      taskList.add(subtask);
      // Order the tasks by either the heirarchial order, or the repository order
      Collections.sort(taskList, task.taskComparator);
      if( trace )
         log.trace("scheduleTask("+taskList.size()+"), created subtask: "+subtask);
   }
}
TOP

Related Classes of org.jboss.mx.loading.LoadMgr$PkgClassLoader

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.