Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.mx » loading » [javadoc | source]
    1   /*
    2    * JBoss, Home of Professional Open Source.
    3    * Copyright 2006, Red Hat Middleware LLC, and individual contributors
    4    * as indicated by the @author tags. See the copyright.txt file in the
    5    * distribution for a full listing of individual contributors.
    6    *
    7    * This is free software; you can redistribute it and/or modify it
    8    * under the terms of the GNU Lesser General Public License as
    9    * published by the Free Software Foundation; either version 2.1 of
   10    * the License, or (at your option) any later version.
   11    *
   12    * This software is distributed in the hope that it will be useful,
   13    * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15    * Lesser General Public License for more details.
   16    *
   17    * You should have received a copy of the GNU Lesser General Public
   18    * License along with this software; if not, write to the Free
   19    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21    */
   22   package org.jboss.mx.loading;
   23   
   24   import java.net.URL;
   25   import java.util.Collections;
   26   import java.util.HashMap;
   27   import java.util.Iterator;
   28   import java.util.LinkedList;
   29   import java.util.List;
   30   import java.util.Map;
   31   import java.util.Set;
   32   import java.util.WeakHashMap;
   33   import java.security.PrivilegedAction;
   34   import java.security.AccessController;
   35   
   36   import org.jboss.logging.Logger;
   37   import org.jboss.mx.loading.ClassLoadingTask.ThreadTask;
   38   
   39   
   40   /** A utility class used by the UnifiedClassLoader3 to manage the thread based
   41    * class loading tasks.
   42    *
   43    * @author Scott.Stark@jboss.org
   44    * @version $Revision: 68584 $
   45    */
   46   public class LoadMgr3
   47   {
   48      private static Logger log = Logger.getLogger(LoadMgr3.class);
   49      /** Used as a synchronization monitor during the setup/teardown of the
   50         thread owning a UCL.loadClass lock
   51       */
   52      private static Object registrationLock = new Object();
   53   
   54      /** A Map<UnifiedClassLoader3, Thread> of the active loadClass UCL3/threads.
   55       * This must be accessed under the registrationLock monitor.
   56       */
   57      private static HashMap loadClassThreads = new HashMap();
   58      /** A Map<Thread, LinkedList<ThreadTask> > of the class loading tasks
   59       * associated with a thread
   60       */
   61      private static Map loadTasksByThread = Collections.synchronizedMap(new WeakHashMap());
   62   
   63      private static SecurityManager sm = System.getSecurityManager();
   64   
   65      /** A UCL and its relative ordering with respect to the class loading.
   66       * The UCL with the lowest order to load a class is the UCL that will
   67       * populate the repository cache and be assigned as the UCL.loadClass
   68       * return value.
   69       */
   70      public static class PkgClassLoader
   71      {
   72         public final RepositoryClassLoader ucl;
   73         public final int order;
   74   
   75         public PkgClassLoader(RepositoryClassLoader ucl)
   76         {
   77            this(ucl, Integer.MAX_VALUE);
   78         }
   79         public PkgClassLoader(RepositoryClassLoader ucl, int order)
   80         {
   81            this.ucl = ucl;
   82            this.order = order;
   83         }
   84         
   85         public String toString()
   86         {
   87            StringBuffer buffer = new StringBuffer(100);
   88            buffer.append(super.toString());
   89            buffer.append("{ucl=").append(ucl);
   90            buffer.append(" order=").append(order);
   91            buffer.append('}');
   92            return buffer.toString();
   93         }
   94      }
   95      /** A PrivilegedAction for locating a class as a resource
   96       * 
   97       */ 
   98      private static class ResourceAction implements PrivilegedAction
   99      {
  100         RepositoryClassLoader ucl;
  101         String classRsrcName;
  102         ResourceAction(RepositoryClassLoader ucl, String classRsrcName)
  103         {
  104            this.ucl = ucl;
  105            this.classRsrcName = classRsrcName;
  106         }
  107         public Object run()
  108         {
  109            URL url = ucl.getResourceLocally(classRsrcName);
  110            ucl = null;
  111            classRsrcName = null;
  112            return url;
  113         }
  114      }
  115   
  116      /** Register that a thread owns the UCL3.loadClass monitor. This is called
  117       * from within UCL3.loadClass(String,boolean) and this method creates
  118       * entries in the loadClassThreads and loadTasksByThread maps.
  119       */
  120      public static void registerLoaderThread(RepositoryClassLoader ucl, Thread t)
  121      {
  122         synchronized( registrationLock )
  123         {
  124            Object prevThread = loadClassThreads.put(ucl, t);
  125            if( log.isTraceEnabled() )
  126               log.trace("registerLoaderThread, ucl="+ucl+", t="+t+", prevT="+prevThread);
  127   
  128            synchronized( loadTasksByThread )
  129            {
  130               List taskList = (List) loadTasksByThread.get(t);
  131               if( taskList == null )
  132               {
  133                  taskList = Collections.synchronizedList(new LinkedList());
  134                  loadTasksByThread.put(t, taskList);
  135                  if( log.isTraceEnabled() )
  136                     log.trace("created new task list");
  137               }
  138            }
  139            registrationLock.notifyAll();
  140         }
  141      }
  142   
  143      /** Initiate the class loading task. This is called by UCL3.loadClass to
  144       * initiate the process of loading the requested class. This first attempts
  145       * to load the class from the repository cache, and then the class loaders
  146       * in the repsository. If the package of the class is found in the repository
  147       * then one or more ThreadTask are created to complete the ClassLoadingTask.
  148       * The ThreadTask are assigned to the threads that own the associated UCL3
  149       * monitor. If no class loader serves the class package, then the requesting
  150       * class loader is asked if it can load the class.
  151       *
  152       * @return true if the class could be loaded from the cache or requesting
  153       * UCL3, false to indicate the calling thread must process the
  154       * tasks assigned to it until the ClassLoadingTask state is FINISHED
  155       * @exception ClassNotFoundException if there is no chance the class can
  156       * be loaded from the current repository class loaders.
  157       */
  158      public static boolean beginLoadTask(ClassLoadingTask task,
  159         UnifiedLoaderRepository3 repository)
  160         throws ClassNotFoundException
  161      {
  162         boolean trace = log.isTraceEnabled();
  163         if( trace )
  164            log.trace("Begin beginLoadTask, task="+task);
  165   
  166         // Try the cache before anything else.
  167         Class cls = repository.loadClassFromCache(task.classname);
  168         if( cls != null )
  169         {
  170            task.loadedClass = cls;
  171            task.state = ClassLoadingTask.FINISHED;
  172            if( trace )
  173               log.trace("End beginLoadTask, loadClassFromCache, classname: "+task.classname);
  174            return true;
  175         }
  176   
  177         // Next get the set of class loaders from the packages map
  178         Set pkgSet = repository.getPackageClassLoaders(task.classname);
  179         if( pkgSet == null || pkgSet.size() == 0 )
  180         {
  181            if (task.stopOrder == Integer.MAX_VALUE)
  182            {
  183               /* If there are no class loaders in the repository capable of handling
  184               the request ask the class loader itself in the event that its parent(s)
  185               can load the class.
  186               */
  187               try
  188               {
  189                  cls = repository.loadClassFromClassLoader(task.classname, false,
  190                     task.requestingClassLoader);
  191               }
  192               catch(LinkageError e)
  193               {
  194                  if( trace )
  195                     log.trace("End beginLoadTask, LinkageError for task: "+task, e);
  196                  throw e;
  197               }
  198               if( cls != null )
  199               {
  200                  task.loadedClass = cls;
  201                  task.state = ClassLoadingTask.FINISHED;
  202                  if( trace )
  203                     log.trace("End beginLoadTask, loadClassFromClassLoader");
  204                  return true;
  205               }
  206            }
  207   
  208            // Else, fail the load
  209            if( trace )
  210               log.trace("End beginLoadTask, ClassNotFoundException");
  211            String msg = "No ClassLoaders found for: "+task.classname;
  212            throw new ClassNotFoundException(msg);
  213         }
  214   
  215         /* A class loading task for each ClassLoader is needed. There can be
  216            multiple class loaders for a pkg due to the pkg being spread out over
  217            multiple jars, or duplicate classes due to versioning/patches, or
  218            just bad packaging.
  219   
  220            In the case of a non-scoped deployment of multiple classes which
  221            will provide a PkgClassLoader to define the ordering, we simply
  222            choose an ordering based on the order the UCL3s were added to the
  223            repository. At most one of the candidate UCL3s will load the class
  224            in order to avoid ClassCastExceptions or LinkageErrors due to the
  225            strong Java type system/security model.
  226   
  227            TODO: A simple ordering mechanism exists, but this probably needs
  228            to be augmented.
  229          */
  230         Iterator iter = pkgSet.iterator();
  231         RepositoryClassLoader theUCL = null;
  232         int order = Integer.MAX_VALUE;
  233         while( iter.hasNext() )
  234         {
  235            Object next = iter.next();
  236            int uclOrder;
  237            RepositoryClassLoader ucl;
  238            // This may be either a PkgClassLoader or a UCL3
  239            if( next instanceof RepositoryClassLoader )
  240            {
  241               ucl = (RepositoryClassLoader) next;
  242               uclOrder = ucl.getAddedOrder();
  243            }
  244            else
  245            {
  246               PkgClassLoader pkgUcl = (PkgClassLoader) next;
  247               ucl = pkgUcl.ucl;
  248               uclOrder = pkgUcl.order;
  249            }
  250   
  251            // If we have a stop order check it
  252            if (task.stopOrder != Integer.MAX_VALUE && task.stopOrder <= uclOrder)
  253               break;
  254            
  255            // Validate that the ucl has the class as a resource
  256            String classRsrcName = task.classname.replace('.', '/') + ".class";
  257            URL url = null;
  258            if( sm != null )
  259            {
  260               ResourceAction action = new ResourceAction(ucl, classRsrcName);
  261               url = (URL) AccessController.doPrivileged(action);
  262            }
  263            else
  264            {
  265               url = ucl.getResourceLocally(classRsrcName);
  266            }
  267   
  268            if( url != null && uclOrder < order )
  269            {
  270               if( trace && theUCL != null )
  271                  log.trace("Replacing UCL: "+theUCL+" with UCL:"+ucl);
  272               theUCL = ucl;
  273               order = uclOrder;
  274            }
  275            else if(trace)
  276            {
  277               if(url == null)
  278                  log.trace("No resource found for: "+classRsrcName);
  279               else
  280                  log.trace("Ignoring class loader based on order: "+ucl);
  281            }
  282         }
  283         if( theUCL == null && task.stopOrder == Integer.MAX_VALUE)
  284         {
  285            /* If there are no class loaders in the repository capable of handling
  286            the request ask the class loader itself in the event that its parent(s)
  287            can load the class. But not if we have a stopOrder.
  288            */
  289            try
  290            {
  291               cls = repository.loadClassFromClassLoader(task.classname, false,
  292                  task.requestingClassLoader);
  293            }
  294            catch(LinkageError e)
  295            {
  296               if( trace )
  297                  log.trace("End beginLoadTask, LinkageError for task: "+task, e);
  298               throw e;
  299            }
  300            if( cls != null )
  301            {
  302               task.loadedClass = cls;
  303               task.state = ClassLoadingTask.FINISHED;
  304               if( trace )
  305                  log.trace("End beginLoadTask, loadClassFromClassLoader");
  306               return true;
  307            }
  308   
  309            // Else, fail the load
  310            if( trace )
  311               log.trace("End beginLoadTask, ClassNotFoundException");
  312            String msg = "No ClassLoaders found for: "+task.classname;
  313            throw new ClassNotFoundException(msg);
  314         }
  315   
  316         if (theUCL == null)
  317         {
  318            if( trace )
  319               log.trace("End beginLoadTask, ClassNotFoundException");
  320            String msg = "No ClassLoaders found for: "+task.classname;
  321            throw new ClassNotFoundException(msg);
  322         }
  323   
  324         scheduleTask(task, theUCL, order, false, trace);
  325         task.state = ClassLoadingTask.FOUND_CLASS_LOADER;
  326         if( trace )
  327            log.trace("End beginLoadTask, task="+task);
  328   
  329         return false;
  330      }
  331   
  332      /** Called by threads owning a UCL3.loadLock from within UCL3.loadClass to
  333       * process ThreadTasks assigned to them. This is the mechanism by which we
  334       * avoid deadlock due to a given loadClass request requiring multiple UCLs
  335       * to be involved. Any thread active in loadClass with the monitor held
  336       * processes class loading tasks that must be handled by its UCL3. The
  337       * active set of threads loading classes form a pool of cooperating threads.
  338       */
  339      public static void nextTask(Thread t, ClassLoadingTask task,
  340         UnifiedLoaderRepository3 repository)
  341         throws InterruptedException
  342      {
  343         boolean trace = log.isTraceEnabled();
  344         List taskList = (List) loadTasksByThread.get(t);
  345         synchronized( taskList )
  346         {
  347            // There may not be any ThreadTasks
  348            while( taskList.size() == 0 && task.threadTaskCount != 0 )
  349            {
  350               /* There are no more tasks for the calling thread to execute, so the
  351               calling thread must wait until the task.threadTaskCount reaches 0
  352                */
  353               if( trace )
  354                  log.trace("Begin nextTask(WAIT_ON_EVENT), task="+task);
  355               try
  356               {
  357                  task.state = ClassLoadingTask.WAIT_ON_EVENT;
  358                  taskList.wait();
  359               }
  360               catch(InterruptedException e)
  361               {
  362                  if( trace )
  363                     log.trace("nextTask(WAIT_ON_EVENT), interrupted, task="+task, e);
  364                  // Abort this task attempt
  365                  throw e;
  366               }
  367               if( trace )
  368                  log.trace("nextTask(WAIT_ON_EVENT), notified, task="+task);
  369            }
  370   
  371            if( trace )
  372               log.trace("Continue nextTask("+taskList.size()+"), task="+task);
  373   
  374            // See if the task is complete
  375            if( task.threadTaskCount == 0 )
  376            {
  377               task.state = ClassLoadingTask.FINISHED;
  378               log.trace("End nextTask(FINISHED), task="+task);
  379               return;
  380            }
  381         }
  382   
  383         ThreadTask threadTask = (ThreadTask) taskList.remove(0);
  384         ClassLoadingTask loadTask = threadTask.getLoadTask();
  385         if( trace )
  386            log.trace("Begin nextTask("+taskList.size()+"), loadTask="+loadTask);
  387   
  388         RepositoryClassLoader ucl3 = threadTask.ucl;
  389         try
  390         {
  391            if( threadTask.t == null )
  392            {
  393               /* This is a task that has been reassigned back to the original
  394               requesting thread ClassLoadingTask, so a new ThreadTask must
  395               be scheduled.
  396               */
  397               if( trace )
  398                  log.trace("Rescheduling threadTask="+threadTask);
  399               scheduleTask(loadTask, ucl3, threadTask.order, true, trace);
  400            }
  401            else
  402            {
  403               if( trace )
  404                  log.trace("Running threadTask="+threadTask);
  405               // Load the class using this thread
  406               threadTask.run();
  407            }
  408         }
  409         catch(Throwable e)
  410         {
  411            boolean retry = e instanceof ClassCircularityError
  412               || e.getClass().equals(LinkageError.class);
  413            int numCCE = loadTask.incNumCCE();
  414            if( retry && numCCE <= 10 )
  415            {
  416               /* Reschedule this task after all existing tasks to allow the
  417               current load tasks which are conflicting to complete.
  418               */
  419               try
  420               {
  421                  if( trace )
  422                     log.trace("Run failed with exception", e);
  423                  // Reschedule and update the loadTask.threadTaskCount
  424                  scheduleTask(loadTask, ucl3, Integer.MAX_VALUE, true, trace);
  425               }
  426               catch(Throwable ex)
  427               {
  428                  loadTask.setLoadError(ex);
  429                  log.warn("Failed to reschedule task after LinkageError", ex);               
  430               }
  431               if( trace )
  432                  log.trace("Post LinkageError state, loadTask="+loadTask);
  433            }
  434            else
  435            {
  436               loadTask.setLoadError(e);
  437               if( trace )
  438                  log.trace("Run failed with exception, loadTask="+loadTask, e);
  439            }
  440         }
  441         finally
  442         {
  443            // We must release the loadLock acquired in beginLoadTask
  444            if( threadTask.releaseInNextTask == true )
  445            {
  446               if( trace )
  447                  log.trace("Releasing loadLock and ownership of UCL: "+threadTask.ucl);
  448               synchronized( registrationLock )
  449               {
  450                  loadClassThreads.remove(threadTask.ucl);
  451               }
  452               synchronized( threadTask.ucl )
  453               {
  454                  ucl3.release();
  455                  ucl3.notifyAll();
  456               }
  457            }
  458         }
  459   
  460         // If the ThreadTasks are complete mark the ClassLoadingTask finished
  461         if( loadTask.threadTaskCount == 0 )
  462         {
  463            Class loadedClass = threadTask.getLoadedClass();
  464            if( loadedClass != null )
  465            {
  466               ClassLoader loader = loadedClass.getClassLoader();
  467               ClassLoader wrapper = repository.getWrappingClassLoader(loader);
  468               if (wrapper != null)
  469                  loader=wrapper;
  470               // Place the loaded class into the repositry cache
  471               repository.cacheLoadedClass(threadTask.getClassname(),
  472                  loadedClass, loader);
  473            }
  474            /*
  475            synchronized( loadTask )
  476            {
  477               if( trace )
  478                  log.trace("Notifying task of thread completion, loadTask:"+loadTask);
  479               loadTask.state = ClassLoadingTask.FINISHED;
  480               loadTask.notify();
  481            }
  482            */
  483            List loadTaskThreadTasks = (List) loadTasksByThread.get(loadTask.requestingThread);
  484            synchronized( loadTaskThreadTasks )
  485            {
  486               if( trace )
  487                  log.trace("Notifying task of thread completion, loadTask:"+loadTask);
  488               loadTask.state = ClassLoadingTask.FINISHED;
  489               loadTaskThreadTasks.notify();
  490            }
  491         }
  492         if( trace )
  493            log.trace("End nextTask("+taskList.size()+"), loadTask="+loadTask);
  494      }
  495   
  496      /** Complete a ClassLoadingTask. This is called by UCL3.loadClass to indicate
  497       * that the thread is existing the loadClass method.
  498       */
  499      public static void endLoadTask(ClassLoadingTask task)
  500      {
  501         boolean trace = log.isTraceEnabled();
  502         if( trace )
  503            log.trace("Begin endLoadTask, task="+task);
  504   
  505         // Unregister as the owning thread and notify any waiting threads
  506         synchronized( registrationLock )
  507         {
  508            loadClassThreads.remove(task.requestingClassLoader);
  509            registrationLock.notifyAll();
  510         }
  511   
  512         // Any ThreadTasks associated with this thread must be reassigned
  513         List taskList = (List) loadTasksByThread.get(task.requestingThread);
  514         int size = taskList != null ? taskList.size() : 0;
  515         synchronized( taskList )
  516         {
  517            for(int i = 0; i < size; i ++)
  518            {
  519               ThreadTask threadTask = (ThreadTask) taskList.remove(0);
  520               ClassLoadingTask loadTask = threadTask.getLoadTask();
  521               /* Synchronize on loadTask and reassign the thread task back to the
  522               requesting thread of loadTask. We need to synchronize on loadTask
  523               to ensure that the transfer of this task back to loadTask.requestingThread
  524               is atomic wrt loadTask.requestingThread checking its task list.
  525               synchronized( loadTask )
  526               {
  527                  if( trace )
  528                     log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
  529                  threadTask.t = null;
  530                  // Insert the task into the front of requestingThread task list
  531                  List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
  532                  toTaskList.add(0, threadTask);
  533                  loadTask.state = ClassLoadingTask.NEXT_EVENT;
  534                  loadTask.notify();
  535               }
  536               */
  537               if( trace )
  538                  log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
  539               threadTask.t = null;
  540               // Insert the task into the front of requestingThread task list
  541               List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
  542               synchronized( toTaskList )
  543               {
  544                  toTaskList.add(0, threadTask);
  545                  loadTask.state = ClassLoadingTask.NEXT_EVENT;
  546                  toTaskList.notify();
  547               }
  548            }
  549         }
  550      }
  551   
  552      /** Invoked to create a ThreadTask to assign a thread to the task of
  553       * loading the class of ClassLoadingTask.
  554       *
  555       * @param task the orginating UCL3.loadClass task for which the thread
  556       * @param ucl the UCL3 the ThreadTask will call loadClassLocally on
  557       * @param order the heirachical ordering of the task
  558       * @param reschedule a boolean indicating if this task is being rescheduled
  559       *    with another UCL3
  560       * @param trace the Logger trace level flag
  561       * @throws ClassNotFoundException
  562       */
  563      static private void scheduleTask(ClassLoadingTask task, RepositoryClassLoader ucl,
  564         int order, boolean reschedule, boolean trace) throws ClassNotFoundException
  565      {
  566         Thread t = null;
  567         boolean releaseInNextTask = false;
  568         ThreadTask subtask = null;
  569         List taskList = null;
  570         synchronized( registrationLock )
  571         {
  572            // Find the thread that owns the ucl
  573            t = (Thread) loadClassThreads.get(ucl);
  574            if( t == null )
  575            {
  576               /* There is no thread in the UCL.loadClass yet that has registered
  577                  as the owning thread. We must attempt to acquire the loadLock
  578                  and if we cannot, wait until the thread entering UCL.loadClass
  579                  gets to the registerLoaderThread call. By the time we are
  580                  notified, the thread coule in fact have exited loadClass, so
  581                  we either assign the task to the thread, or take ownership of
  582                  the UCL.
  583                */
  584               while( t == null && ucl.attempt(1) == false )
  585               {
  586                  if( trace )
  587                     log.trace("Waiting for owner of UCL: "+ucl);
  588                  try
  589                  {
  590                     registrationLock.wait();
  591                  }
  592                  catch(InterruptedException e)
  593                  {
  594                     String msg = "Interrupted waiting for registration notify,"
  595                        + " classame: "+task.classname;
  596                     throw new ClassNotFoundException(msg);
  597                  }
  598   
  599                  t = (Thread) loadClassThreads.get(ucl);
  600                  if( trace )
  601                     log.trace("Notified that UCL owner is: "+t);
  602               }
  603   
  604               // Get the thread registered as owning the UCL.loadClass lock
  605               t = (Thread) loadClassThreads.get(ucl);
  606               if( t == null )
  607               {
  608                  // There is no such thread, register as the owner
  609                  releaseInNextTask = true;
  610                  t = task.requestingThread;
  611                  Object prevThread = loadClassThreads.put(ucl, t);
  612                  if( trace )
  613                  {
  614                     log.trace("scheduleTask, taking ownership of ucl="+ucl
  615                        +", t="+t+", prevT="+prevThread);
  616                  }
  617               }
  618            }
  619   
  620            // Now that we have the UCL owner thread, create and assign the task
  621            subtask = task.newThreadTask(ucl, t, order, reschedule,
  622               releaseInNextTask);
  623            // Add the task to the owning thread
  624            taskList = (List) loadTasksByThread.get(t);
  625            synchronized( taskList )
  626            {
  627               taskList.add(subtask);
  628               // Order the tasks by either the heirarchial order, or the repository order
  629               Collections.sort(taskList, ClassLoadingTask.taskComparator);
  630               taskList.notify();
  631            }
  632         }
  633   
  634         if( trace )
  635            log.trace("scheduleTask("+taskList.size()+"), created subtask: "+subtask);
  636      }
  637   }

Save This Page
Home » jboss-5.0.0.CR1-src » org.jboss.mx » loading » [javadoc | source]