KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > mx > loading > LoadMgr3


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * 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 JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.LinkedList JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.WeakHashMap JavaDoc;
33 import java.security.PrivilegedAction JavaDoc;
34 import java.security.AccessController JavaDoc;
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: 37459 $
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 JavaDoc registrationLock = new Object JavaDoc();
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 JavaDoc loadClassThreads = new HashMap JavaDoc();
58    /** A Map<Thread, LinkedList<ThreadTask> > of the class loading tasks
59     * associated with a thread
60     */

61    private static Map JavaDoc loadTasksByThread = Collections.synchronizedMap(new WeakHashMap JavaDoc());
62
63    private static SecurityManager JavaDoc 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 JavaDoc toString()
86       {
87          StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(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 JavaDoc
99    {
100       RepositoryClassLoader ucl;
101       String JavaDoc classRsrcName;
102       ResourceAction(RepositoryClassLoader ucl, String JavaDoc classRsrcName)
103       {
104          this.ucl = ucl;
105          this.classRsrcName = classRsrcName;
106       }
107       public Object JavaDoc run()
108       {
109          URL JavaDoc 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 JavaDoc t)
121    {
122       synchronized( registrationLock )
123       {
124          Object JavaDoc 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 JavaDoc taskList = (List JavaDoc) loadTasksByThread.get(t);
131             if( taskList == null )
132             {
133                taskList = Collections.synchronizedList(new LinkedList JavaDoc());
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 JavaDoc
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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc msg = "No ClassLoaders found for: "+task.classname;
212          throw new ClassNotFoundException JavaDoc(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 JavaDoc iter = pkgSet.iterator();
231       RepositoryClassLoader theUCL = null;
232       int order = Integer.MAX_VALUE;
233       while( iter.hasNext() )
234       {
235          Object JavaDoc 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 JavaDoc classRsrcName = task.classname.replace('.', '/') + ".class";
257          URL JavaDoc url = null;
258          if( sm != null )
259          {
260             ResourceAction action = new ResourceAction(ucl, classRsrcName);
261             url = (URL JavaDoc) 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       }
276       if( theUCL == null && task.stopOrder == Integer.MAX_VALUE)
277       {
278          /* If there are no class loaders in the repository capable of handling
279          the request ask the class loader itself in the event that its parent(s)
280          can load the class. But not if we have a stopOrder.
281          */

282          try
283          {
284             cls = repository.loadClassFromClassLoader(task.classname, false,
285                task.requestingClassLoader);
286          }
287          catch(LinkageError JavaDoc e)
288          {
289             if( trace )
290                log.trace("End beginLoadTask, LinkageError for task: "+task, e);
291             throw e;
292          }
293          if( cls != null )
294          {
295             task.loadedClass = cls;
296             task.state = ClassLoadingTask.FINISHED;
297             if( trace )
298                log.trace("End beginLoadTask, loadClassFromClassLoader");
299             return true;
300          }
301
302          // Else, fail the load
303
if( trace )
304             log.trace("End beginLoadTask, ClassNotFoundException");
305          String JavaDoc msg = "No ClassLoaders found for: "+task.classname;
306          throw new ClassNotFoundException JavaDoc(msg);
307       }
308
309       if (theUCL == null)
310       {
311          if( trace )
312             log.trace("End beginLoadTask, ClassNotFoundException");
313          String JavaDoc msg = "No ClassLoaders found for: "+task.classname;
314          throw new ClassNotFoundException JavaDoc(msg);
315       }
316
317       scheduleTask(task, theUCL, order, false, trace);
318       task.state = ClassLoadingTask.FOUND_CLASS_LOADER;
319       if( trace )
320          log.trace("End beginLoadTask, task="+task);
321
322       return false;
323    }
324
325    /** Called by threads owning a UCL3.loadLock from within UCL3.loadClass to
326     * process ThreadTasks assigned to them. This is the mechanism by which we
327     * avoid deadlock due to a given loadClass request requiring multiple UCLs
328     * to be involved. Any thread active in loadClass with the monitor held
329     * processes class loading tasks that must be handled by its UCL3. The
330     * active set of threads loading classes form a pool of cooperating threads.
331     */

332    public static void nextTask(Thread JavaDoc t, ClassLoadingTask task,
333       UnifiedLoaderRepository3 repository)
334       throws InterruptedException JavaDoc
335    {
336       boolean trace = log.isTraceEnabled();
337       List JavaDoc taskList = (List JavaDoc) loadTasksByThread.get(t);
338       synchronized( taskList )
339       {
340          // There may not be any ThreadTasks
341
while( taskList.size() == 0 && task.threadTaskCount != 0 )
342          {
343             /* There are no more tasks for the calling thread to execute, so the
344             calling thread must wait until the task.threadTaskCount reaches 0
345              */

346             if( trace )
347                log.trace("Begin nextTask(WAIT_ON_EVENT), task="+task);
348             try
349             {
350                task.state = ClassLoadingTask.WAIT_ON_EVENT;
351                taskList.wait();
352             }
353             catch(InterruptedException JavaDoc e)
354             {
355                if( trace )
356                   log.trace("nextTask(WAIT_ON_EVENT), interrupted, task="+task, e);
357                // Abort this task attempt
358
throw e;
359             }
360             if( trace )
361                log.trace("nextTask(WAIT_ON_EVENT), notified, task="+task);
362          }
363
364          if( trace )
365             log.trace("Continue nextTask("+taskList.size()+"), task="+task);
366
367          // See if the task is complete
368
if( task.threadTaskCount == 0 )
369          {
370             task.state = ClassLoadingTask.FINISHED;
371             log.trace("End nextTask(FINISHED), task="+task);
372             return;
373          }
374       }
375
376       ThreadTask threadTask = (ThreadTask) taskList.remove(0);
377       ClassLoadingTask loadTask = threadTask.getLoadTask();
378       if( trace )
379          log.trace("Begin nextTask("+taskList.size()+"), loadTask="+loadTask);
380
381       RepositoryClassLoader ucl3 = threadTask.ucl;
382       try
383       {
384          if( threadTask.t == null )
385          {
386             /* This is a task that has been reassigned back to the original
387             requesting thread ClassLoadingTask, so a new ThreadTask must
388             be scheduled.
389             */

390             if( trace )
391                log.trace("Rescheduling threadTask="+threadTask);
392             scheduleTask(loadTask, ucl3, threadTask.order, true, trace);
393          }
394          else
395          {
396             if( trace )
397                log.trace("Running threadTask="+threadTask);
398             // Load the class using this thread
399
threadTask.run();
400          }
401       }
402       catch(Throwable JavaDoc e)
403       {
404          if( e instanceof ClassCircularityError JavaDoc /*&& taskList.size() > 0 */ )
405          {
406             /* Reschedule this task after all existing tasks to allow the
407             current load tasks which are conflicting to complete.
408             */

409             try
410             {
411                if( trace )
412                   log.trace("Run failed with exception", e);
413                // Reschedule and update the loadTask.threadTaskCount
414
scheduleTask(loadTask, ucl3, Integer.MAX_VALUE, true, trace);
415             }
416             catch(ClassNotFoundException JavaDoc ex)
417             {
418                loadTask.setLoadError(ex);
419                log.warn("Failed to reschedule task after CCE", ex);
420             }
421             if( trace )
422                log.trace("Post CCE state, loadTask="+loadTask);
423          }
424          else
425          {
426             loadTask.setLoadError(e);
427             if( trace )
428                log.trace("Run failed with exception", e);
429          }
430       }
431       finally
432       {
433          // We must release the loadLock acquired in beginLoadTask
434
if( threadTask.releaseInNextTask == true )
435          {
436             if( trace )
437                log.trace("Releasing loadLock and ownership of UCL: "+threadTask.ucl);
438             synchronized( registrationLock )
439             {
440                loadClassThreads.remove(threadTask.ucl);
441             }
442             synchronized( threadTask.ucl )
443             {
444                ucl3.release();
445                ucl3.notifyAll();
446             }
447          }
448       }
449
450       // If the ThreadTasks are complete mark the ClassLoadingTask finished
451
if( loadTask.threadTaskCount == 0 )
452       {
453          Class JavaDoc loadedClass = threadTask.getLoadedClass();
454          if( loadedClass != null )
455          {
456             ClassLoader JavaDoc loader = loadedClass.getClassLoader();
457             ClassLoader JavaDoc wrapper = repository.getWrappingClassLoader(loader);
458             if (wrapper != null)
459                loader=wrapper;
460             // Place the loaded class into the repositry cache
461
repository.cacheLoadedClass(threadTask.getClassname(),
462                loadedClass, loader);
463          }
464          /*
465          synchronized( loadTask )
466          {
467             if( trace )
468                log.trace("Notifying task of thread completion, loadTask:"+loadTask);
469             loadTask.state = ClassLoadingTask.FINISHED;
470             loadTask.notify();
471          }
472          */

473          List JavaDoc loadTaskThreadTasks = (List JavaDoc) loadTasksByThread.get(loadTask.requestingThread);
474          synchronized( loadTaskThreadTasks )
475          {
476             if( trace )
477                log.trace("Notifying task of thread completion, loadTask:"+loadTask);
478             loadTask.state = ClassLoadingTask.FINISHED;
479             loadTaskThreadTasks.notify();
480          }
481       }
482       if( trace )
483          log.trace("End nextTask("+taskList.size()+"), loadTask="+loadTask);
484    }
485
486    /** Complete a ClassLoadingTask. This is called by UCL3.loadClass to indicate
487     * that the thread is existing the loadClass method.
488     */

489    public static void endLoadTask(ClassLoadingTask task)
490    {
491       boolean trace = log.isTraceEnabled();
492       if( trace )
493          log.trace("Begin endLoadTask, task="+task);
494
495       // Unregister as the owning thread and notify any waiting threads
496
synchronized( registrationLock )
497       {
498          loadClassThreads.remove(task.requestingClassLoader);
499          registrationLock.notifyAll();
500       }
501
502       // Any ThreadTasks associated with this thread must be reassigned
503
List JavaDoc taskList = (List JavaDoc) loadTasksByThread.get(task.requestingThread);
504       int size = taskList != null ? taskList.size() : 0;
505       synchronized( taskList )
506       {
507          for(int i = 0; i < size; i ++)
508          {
509             ThreadTask threadTask = (ThreadTask) taskList.remove(0);
510             ClassLoadingTask loadTask = threadTask.getLoadTask();
511             /* Synchronize on loadTask and reassign the thread task back to the
512             requesting thread of loadTask. We need to synchronize on loadTask
513             to ensure that the transfer of this task back to loadTask.requestingThread
514             is atomic wrt loadTask.requestingThread checking its task list.
515             synchronized( loadTask )
516             {
517                if( trace )
518                   log.trace("Reassigning task: "+threadTask+", to: "+loadTask.requestingThread);
519                threadTask.t = null;
520                // Insert the task into the front of requestingThread task list
521                List toTaskList = (List) loadTasksByThread.get(loadTask.requestingThread);
522                toTaskList.add(0, threadTask);
523                loadTask.state = ClassLoadingTask.NEXT_EVENT;
524                loadTask.notify();
525             }
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 JavaDoc toTaskList = (List JavaDoc) loadTasksByThread.get(loadTask.requestingThread);
532             synchronized( toTaskList )
533             {
534                toTaskList.add(0, threadTask);
535                loadTask.state = ClassLoadingTask.NEXT_EVENT;
536                toTaskList.notify();
537             }
538          }
539       }
540    }
541
542    /** Invoked to create a ThreadTask to assign a thread to the task of
543     * loading the class of ClassLoadingTask.
544     *
545     * @param task the orginating UCL3.loadClass task for which the thread
546     * @param ucl the UCL3 the ThreadTask will call loadClassLocally on
547     * @param order the heirachical ordering of the task
548     * @param reschedule a boolean indicating if this task is being rescheduled
549     * with another UCL3
550     * @param trace the Logger trace level flag
551     * @throws ClassNotFoundException
552     */

553    static private void scheduleTask(ClassLoadingTask task, RepositoryClassLoader ucl,
554       int order, boolean reschedule, boolean trace) throws ClassNotFoundException JavaDoc
555    {
556       Thread JavaDoc t = null;
557       boolean releaseInNextTask = false;
558       ThreadTask subtask = null;
559       List JavaDoc taskList = null;
560       synchronized( registrationLock )
561       {
562          // Find the thread that owns the ucl
563
t = (Thread JavaDoc) loadClassThreads.get(ucl);
564          if( t == null )
565          {
566             /* There is no thread in the UCL.loadClass yet that has registered
567                as the owning thread. We must attempt to acquire the loadLock
568                and if we cannot, wait until the thread entering UCL.loadClass
569                gets to the registerLoaderThread call. By the time we are
570                notified, the thread coule in fact have exited loadClass, so
571                we either assign the task to the thread, or take ownership of
572                the UCL.
573              */

574             while( t == null && ucl.attempt(1) == false )
575             {
576                if( trace )
577                   log.trace("Waiting for owner of UCL: "+ucl);
578                try
579                {
580                   registrationLock.wait();
581                }
582                catch(InterruptedException JavaDoc e)
583                {
584                   String JavaDoc msg = "Interrupted waiting for registration notify,"
585                      + " classame: "+task.classname;
586                   throw new ClassNotFoundException JavaDoc(msg);
587                }
588
589                t = (Thread JavaDoc) loadClassThreads.get(ucl);
590                if( trace )
591                   log.trace("Notified that UCL owner is: "+t);
592             }
593
594             // Get the thread registered as owning the UCL.loadClass lock
595
t = (Thread JavaDoc) loadClassThreads.get(ucl);
596             if( t == null )
597             {
598                // There is no such thread, register as the owner
599
releaseInNextTask = true;
600                t = task.requestingThread;
601                Object JavaDoc prevThread = loadClassThreads.put(ucl, t);
602                if( trace )
603                {
604                   log.trace("scheduleTask, taking ownership of ucl="+ucl
605                      +", t="+t+", prevT="+prevThread);
606                }
607             }
608          }
609
610          // Now that we have the UCL owner thread, create and assign the task
611
subtask = task.newThreadTask(ucl, t, order, reschedule,
612             releaseInNextTask);
613          // Add the task to the owning thread
614
taskList = (List JavaDoc) loadTasksByThread.get(t);
615          synchronized( taskList )
616          {
617             taskList.add(subtask);
618             // Order the tasks by either the heirarchial order, or the repository order
619
Collections.sort(taskList, task.taskComparator);
620             taskList.notify();
621          }
622       }
623
624       if( trace )
625          log.trace("scheduleTask("+taskList.size()+"), created subtask: "+subtask);
626    }
627 }
628
Popular Tags