Package org.jboss.mx.loading

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

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

import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.security.PrivilegedAction;
import java.security.AccessController;

import org.jboss.logging.Logger;
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: 88027 $
*/
public class LoadMgr3
{
   private static Logger log = Logger.getLogger(LoadMgr3.class);
   /** Used as a synchronization monitor during the setup/teardown of the
      thread owning a UCL.loadClass lock
    */
   private static Object registrationLock = new Object();

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

   private static SecurityManager sm = System.getSecurityManager();

   /** A UCL and its relative ordering with respect to the class loading.
    * The UCL with the lowest order to load a class is the UCL that will
    * populate the repository cache and be assigned as the UCL.loadClass
    * return value.
    */
   public static class PkgClassLoader
   {
      public final RepositoryClassLoader ucl;
      public final int order;

      public PkgClassLoader(RepositoryClassLoader ucl)
      {
         this(ucl, Integer.MAX_VALUE);
      }
      public PkgClassLoader(RepositoryClassLoader ucl, int order)
      {
         this.ucl = ucl;
         this.order = order;
      }
     
      public String toString()
      {
         StringBuffer buffer = new StringBuffer(100);
         buffer.append(super.toString());
         buffer.append("{ucl=").append(ucl);
         buffer.append(" order=").append(order);
         buffer.append('}');
         return buffer.toString();
      }
   }
   /** A PrivilegedAction for locating a class as a resource
    *
    */
   private static class ResourceAction implements PrivilegedAction
   {
      RepositoryClassLoader ucl;
      String classRsrcName;
      ResourceAction(RepositoryClassLoader ucl, String classRsrcName)
      {
         this.ucl = ucl;
         this.classRsrcName = classRsrcName;
      }
      public Object run()
      {
         URL url = ucl.getResourceLocally(classRsrcName);
         ucl = null;
         classRsrcName = null;
         return url;
      }
   }

   /** Register that a thread owns the UCL3.loadClass monitor. This is called
    * from within UCL3.loadClass(String,boolean) and this method creates
    * entries in the loadClassThreads and loadTasksByThread maps.
    */
   public static void registerLoaderThread(RepositoryClassLoader ucl, Thread t)
   {
      synchronized( registrationLock )
      {
         Object prevThread = loadClassThreads.put(ucl, t);
         if( log.isTraceEnabled() )
            log.trace("registerLoaderThread, ucl="+ucl+", t="+t+", prevT="+prevThread);

         synchronized( loadTasksByThread )
         {
            List taskList = (List) loadTasksByThread.get(t);
            if( taskList == null )
            {
               taskList = Collections.synchronizedList(new LinkedList());
               loadTasksByThread.put(t, taskList);
               if( log.isTraceEnabled() )
                  log.trace("created new task list");
            }
         }
         registrationLock.notifyAll();
      }
   }

   /** Initiate the class loading task. This is called by UCL3.loadClass to
    * initiate the process of loading the requested class. This first attempts
    * to load the class from the repository cache, and then the class loaders
    * in the repsository. If the package of the class is found in the repository
    * then one or more ThreadTask are created to complete the ClassLoadingTask.
    * The ThreadTask are assigned to the threads that own the associated UCL3
    * monitor. If no class loader serves the class package, then the requesting
    * class loader is asked if it can load the class.
    *
    * @return true if the class could be loaded from the cache or requesting
    * UCL3, false to indicate the calling thread must process the
    * tasks assigned to it until the ClassLoadingTask state is FINISHED
    * @exception ClassNotFoundException if there is no chance the class can
    * be loaded from the current repository class loaders.
    */
   public static boolean beginLoadTask(ClassLoadingTask task,
      UnifiedLoaderRepository3 repository)
      throws ClassNotFoundException
   {
      boolean trace = log.isTraceEnabled();
      if( trace )
         log.trace("Begin beginLoadTask, task="+task);

      // Try the cache before anything else.
      Class cls = repository.loadClassFromCache(task.classname);
      if( cls != null )
      {
         task.loadedClass = cls;
         task.state = ClassLoadingTask.FINISHED;
         if( trace )
            log.trace("End beginLoadTask, loadClassFromCache, classname: "+task.classname);
         return true;
      }

      // Next get the set of class loaders from the packages map
      Set pkgSet = repository.getPackageClassLoaders(task.classname);
      if( pkgSet == null || pkgSet.size() == 0 )
      {
         if (task.stopOrder == Integer.MAX_VALUE)
         {
            /* If there are no class loaders in the repository capable of handling
            the request ask the class loader itself in the event that its parent(s)
            can load the class.
            */
            try
            {
               cls = repository.loadClassFromClassLoader(task.classname, false,
                  task.requestingClassLoader);
            }
            catch(LinkageError e)
            {
               if( trace )
                  log.trace("End beginLoadTask, LinkageError for task: "+task, e);
               throw e;
            }
            if( cls != null )
            {
               task.loadedClass = cls;
               task.state = ClassLoadingTask.FINISHED;
               if( trace )
                  log.trace("End beginLoadTask, loadClassFromClassLoader");
               return true;
            }
         }

         // Else, fail the load
         if( trace )
            log.trace("End beginLoadTask, ClassNotFoundException");
         String msg = "No ClassLoaders found for: "+task.classname;
         throw new ClassNotFoundException(msg);
      }

      /* A class loading task for each ClassLoader is needed. There can be
         multiple class loaders for a pkg due to the pkg being spread out over
         multiple jars, or duplicate classes due to versioning/patches, or
         just bad packaging.

         In the case of a non-scoped deployment of multiple classes which
         will provide a PkgClassLoader to define the ordering, we simply
         choose an ordering based on the order the UCL3s were added to the
         repository. At most one of the candidate UCL3s will load the class
         in order to avoid ClassCastExceptions or LinkageErrors due to the
         strong Java type system/security model.

         TODO: A simple ordering mechanism exists, but this probably needs
         to be augmented.
       */
      Iterator iter = pkgSet.iterator();
      RepositoryClassLoader theUCL = null;
      int order = Integer.MAX_VALUE;
      while( iter.hasNext() )
      {
         Object next = iter.next();
         int uclOrder;
         RepositoryClassLoader ucl;
         // This may be either a PkgClassLoader or a UCL3
         if( next instanceof RepositoryClassLoader )
         {
            ucl = (RepositoryClassLoader) next;
            uclOrder = ucl.getAddedOrder();
         }
         else
         {
            PkgClassLoader pkgUcl = (PkgClassLoader) next;
            ucl = pkgUcl.ucl;
            uclOrder = pkgUcl.order;
         }

         // If we have a stop order check it
         if (task.stopOrder != Integer.MAX_VALUE && task.stopOrder <= uclOrder)
            break;
        
         // Validate that the ucl has the class as a resource
         String classRsrcName = task.classname.replace('.', '/') + ".class";
         URL url = null;
         if( sm != null )
         {
            ResourceAction action = new ResourceAction(ucl, classRsrcName);
            url = (URL) AccessController.doPrivileged(action);
         }
         else
         {
            url = ucl.getResourceLocally(classRsrcName);
         }

         if( url != null && uclOrder < order )
         {
            if( trace && theUCL != null )
               log.trace("Replacing UCL: "+theUCL+" with UCL:"+ucl);
            theUCL = ucl;
            order = uclOrder;
         }
         else if(trace)
         {
            if(url == null)
               log.trace("No resource found for: "+classRsrcName);
            else
               log.trace("Ignoring class loader based on order: "+ucl);
         }
      }
      if( theUCL == null && task.stopOrder == Integer.MAX_VALUE)
      {
         /* If there are no class loaders in the repository capable of handling
         the request ask the class loader itself in the event that its parent(s)
         can load the class. But not if we have a stopOrder.
         */
         try
         {
            cls = repository.loadClassFromClassLoader(task.classname, false,
               task.requestingClassLoader);
         }
         catch(LinkageError e)
         {
            if( trace )
               log.trace("End beginLoadTask, LinkageError for task: "+task, e);
            throw e;
         }
         if( cls != null )
         {
            task.loadedClass = cls;
            task.state = ClassLoadingTask.FINISHED;
            if( trace )
               log.trace("End beginLoadTask, loadClassFromClassLoader");
            return true;
         }

         // Else, fail the load
         if( trace )
            log.trace("End beginLoadTask, ClassNotFoundException");
         String msg = "No ClassLoaders found for: "+task.classname;
         throw new ClassNotFoundException(msg);
      }

      if (theUCL == null)
      {
         if( trace )
            log.trace("End beginLoadTask, ClassNotFoundException");
         String msg = "No ClassLoaders found for: "+task.classname;
         throw new ClassNotFoundException(msg);
      }

      scheduleTask(task, theUCL, order, false, trace);
      task.state = ClassLoadingTask.FOUND_CLASS_LOADER;
      if( trace )
         log.trace("End beginLoadTask, task="+task);

      return false;
   }

   /** Called by threads owning a UCL3.loadLock from within UCL3.loadClass to
    * process ThreadTasks assigned to them. This is the mechanism by which we
    * avoid deadlock due to a given loadClass request requiring multiple UCLs
    * to be involved. Any thread active in loadClass with the monitor held
    * processes class loading tasks that must be handled by its UCL3. The
    * active set of threads loading classes form a pool of cooperating threads.
    */
   public static void nextTask(Thread t, ClassLoadingTask task,
      UnifiedLoaderRepository3 repository)
      throws InterruptedException
   {
      boolean trace = log.isTraceEnabled();
      List taskList = (List) loadTasksByThread.get(t);
      synchronized( taskList )
      {
         // There may not be any ThreadTasks
         while( taskList.size() == 0 && task.threadTaskCount != 0 )
         {
            /* There are no more tasks for the calling thread to execute, so the
            calling thread must wait until the task.threadTaskCount reaches 0
             */
            if( trace )
               log.trace("Begin nextTask(WAIT_ON_EVENT), task="+task);
            try
            {
               task.state = ClassLoadingTask.WAIT_ON_EVENT;
               taskList.wait();
            }
            catch(InterruptedException e)
            {
               if( trace )
                  log.trace("nextTask(WAIT_ON_EVENT), interrupted, task="+task, e);
               // Abort this task attempt
               throw e;
            }
            if( trace )
               log.trace("nextTask(WAIT_ON_EVENT), notified, task="+task);
         }

         if( trace )
            log.trace("Continue nextTask("+taskList.size()+"), task="+task);

         // See if the task is complete
         if( task.threadTaskCount == 0 )
         {
            task.state = ClassLoadingTask.FINISHED;
            log.trace("End nextTask(FINISHED), task="+task);
            return;
         }
      }

      ThreadTask threadTask = (ThreadTask) taskList.remove(0);
      ClassLoadingTask loadTask = threadTask.getLoadTask();
      if( trace )
         log.trace("Begin nextTask("+taskList.size()+"), loadTask="+loadTask);

      RepositoryClassLoader ucl3 = threadTask.ucl;
      try
      {
         if( threadTask.t == null )
         {
            /* This is a task that has been reassigned back to the original
            requesting thread ClassLoadingTask, so a new ThreadTask must
            be scheduled.
            */
            if( trace )
               log.trace("Rescheduling threadTask="+threadTask);
            scheduleTask(loadTask, ucl3, threadTask.order, true, trace);
         }
         else
         {
            if( trace )
               log.trace("Running threadTask="+threadTask);
            // Load the class using this thread
            threadTask.run();
         }
      }
      catch(Throwable e)
      {
       log.trace("Class loading Task Run failed with exception", e);
       boolean retry = e instanceof ClassCircularityError
            || e.getClass().equals(LinkageError.class);
         int numCCE = loadTask.incNumCCE();
        
         if( retry && numCCE <= 10 )
         {
            log.info("Rescheduling failed Class loading Task");
          /* Reschedule this task after all existing tasks to allow the
            current load tasks which are conflicting to complete.
            */
            try
            {
               // Reschedule and update the loadTask.threadTaskCount
               scheduleTask(loadTask, ucl3, Integer.MAX_VALUE, true, trace);
            }
            catch(Throwable ex)
            {
               loadTask.setLoadError(ex);
               log.error("Failed to reschedule task after LinkageError", ex);              
            }
            if( trace )
               log.trace("Post LinkageError state, loadTask="+loadTask);
         }
         else
         {
            loadTask.setLoadError(e);
            log.error("Not resheduling failed loading task, loadTask="+loadTask, e);
         }
      }
      finally
      {
         // We must release the loadLock acquired in beginLoadTask
         if( threadTask.releaseInNextTask == true )
         {
            if( trace )
               log.trace("Releasing loadLock and ownership of UCL: "+threadTask.ucl);
            synchronized( registrationLock )
            {
               loadClassThreads.remove(threadTask.ucl);
            }
            synchronized( threadTask.ucl )
            {
               ucl3.release();
               ucl3.notifyAll();
            }
         }
      }

      // If the ThreadTasks are complete mark the ClassLoadingTask finished
      if( loadTask.threadTaskCount == 0 )
      {
         Class loadedClass = threadTask.getLoadedClass();
         if( loadedClass != null )
         {
            ClassLoader loader = loadedClass.getClassLoader();
            ClassLoader wrapper = repository.getWrappingClassLoader(loader);
            if (wrapper != null)
               loader=wrapper;
            // Place the loaded class into the repositry cache
            repository.cacheLoadedClass(threadTask.getClassname(),
               loadedClass, loader);
         }
         /*
         synchronized( loadTask )
         {
            if( trace )
               log.trace("Notifying task of thread completion, loadTask:"+loadTask);
            loadTask.state = ClassLoadingTask.FINISHED;
            loadTask.notify();
         }
         */
         List loadTaskThreadTasks = (List) loadTasksByThread.get(loadTask.requestingThread);
         synchronized( loadTaskThreadTasks )
         {
            if( trace )
               log.trace("Notifying task of thread completion, loadTask:"+loadTask);
            loadTask.state = ClassLoadingTask.FINISHED;
            loadTaskThreadTasks.notify();
         }
      }
      if( trace )
         log.trace("End nextTask("+taskList.size()+"), loadTask="+loadTask);
   }

   /** Complete a ClassLoadingTask. This is called by UCL3.loadClass to indicate
    * that the thread is existing the loadClass method.
    */
   public static void endLoadTask(ClassLoadingTask task)
   {
      boolean trace = log.isTraceEnabled();
      if( trace )
         log.trace("Begin endLoadTask, task="+task);

      // Unregister as the owning thread and notify any waiting threads
      synchronized( registrationLock )
      {
         loadClassThreads.remove(task.requestingClassLoader);
         registrationLock.notifyAll();
      }

      // Any ThreadTasks associated with this thread must be reassigned
      List taskList = (List) loadTasksByThread.get(task.requestingThread);
      int size = taskList != null ? taskList.size() : 0;
      synchronized( taskList )
      {
         for(int i = 0; i < size; i ++)
         {
            ThreadTask threadTask = (ThreadTask) taskList.remove(0);
            ClassLoadingTask loadTask = threadTask.getLoadTask();
            /* Synchronize on loadTask and reassign the thread task back to the
            requesting thread of loadTask. We need to synchronize on loadTask
            to ensure that the transfer of this task back to loadTask.requestingThread
            is atomic wrt loadTask.requestingThread checking its task list.
            synchronized( loadTask )
            {
               if( trace )
                  log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
               threadTask.t = null;
               // Insert the task into the front of requestingThread task list
               List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
               toTaskList.add(0, threadTask);
               loadTask.state = ClassLoadingTask.NEXT_EVENT;
               loadTask.notify();
            }
            */
            if( trace )
               log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
            threadTask.t = null;
            // Insert the task into the front of requestingThread task list
            List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
            synchronized( toTaskList )
            {
               toTaskList.add(0, threadTask);
               loadTask.state = ClassLoadingTask.NEXT_EVENT;
               toTaskList.notify();
            }
         }
      }
   }

   /** Invoked to create a ThreadTask to assign a thread to the task of
    * loading the class of ClassLoadingTask.
    *
    * @param task the orginating UCL3.loadClass task for which the thread
    * @param ucl the UCL3 the ThreadTask will call loadClassLocally on
    * @param order the heirachical ordering of the task
    * @param reschedule a boolean indicating if this task is being rescheduled
    *    with another UCL3
    * @param trace the Logger trace level flag
    * @throws ClassNotFoundException
    */
   static private void scheduleTask(ClassLoadingTask task, RepositoryClassLoader ucl,
      int order, boolean reschedule, boolean trace) throws ClassNotFoundException
   {
      Thread t = null;
      boolean releaseInNextTask = false;
      ThreadTask subtask = null;
      List taskList = null;
      synchronized( registrationLock )
      {
         // Find the thread that owns the ucl
         t = (Thread) loadClassThreads.get(ucl);
         if( t == null )
         {
            /* There is no thread in the UCL.loadClass yet that has registered
               as the owning thread. We must attempt to acquire the loadLock
               and if we cannot, wait until the thread entering UCL.loadClass
               gets to the registerLoaderThread call. By the time we are
               notified, the thread coule in fact have exited loadClass, so
               we either assign the task to the thread, or take ownership of
               the UCL.
             */
            while( t == null && ucl.attempt(1) == false )
            {
               if( trace )
                  log.trace("Waiting for owner of UCL: "+ucl);
               try
               {
                  registrationLock.wait();
               }
               catch(InterruptedException e)
               {
                  String msg = "Interrupted waiting for registration notify,"
                     + " classame: "+task.classname;
                  throw new ClassNotFoundException(msg);
               }

               t = (Thread) loadClassThreads.get(ucl);
               if( trace )
                  log.trace("Notified that UCL owner is: "+t);
            }

            // Get the thread registered as owning the UCL.loadClass lock
            t = (Thread) loadClassThreads.get(ucl);
            if( t == null )
            {
               // There is no such thread, register as the owner
               releaseInNextTask = true;
               t = task.requestingThread;
               Object prevThread = loadClassThreads.put(ucl, t);
               if( trace )
               {
                  log.trace("scheduleTask, taking ownership of ucl="+ucl
                     +", t="+t+", prevT="+prevThread);
               }
            }
         }

         // Now that we have the UCL owner thread, create and assign the task
         subtask = task.newThreadTask(ucl, t, order, reschedule,
            releaseInNextTask);
         // Add the task to the owning thread
         taskList = (List) loadTasksByThread.get(t);
         synchronized( taskList )
         {
            taskList.add(subtask);
            // Order the tasks by either the heirarchial order, or the repository order
            Collections.sort(taskList, ClassLoadingTask.taskComparator);
            taskList.notify();
         }
      }

      if( trace )
         log.trace("scheduleTask("+taskList.size()+"), created subtask: "+subtask);
   }
}
TOP

Related Classes of org.jboss.mx.loading.LoadMgr3$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.