KickJava   Java API By Example, From Geeks To Geeks.

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


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.classloading.spi.DomainClassLoader;
37 import org.jboss.logging.Logger;
38 import org.jboss.mx.loading.ClassLoadingTaskDCL.ThreadTask;
39
40
41 /** A utility class used by the DomainClassLoaderUCLImpl to manage the thread
42  * based class loading tasks.
43  *
44  * @author Scott.Stark@jboss.org
45  * @version $Revision: 57108 $
46  */

47 public class LoadMgrDCL
48 {
49    private static Logger log = Logger.getLogger(LoadMgrDCL.class);
50    /** Used as a synchronization monitor during the setup/teardown of the
51       thread owning a UCL.loadClass lock
52     */

53    private static Object JavaDoc registrationLock = new Object JavaDoc();
54
55    /** A Map<UnifiedClassLoader3, Thread> of the active loadClass UCL3/threads.
56     * This must be accessed under the registrationLock monitor.
57     */

58    private static HashMap JavaDoc loadClassThreads = new HashMap JavaDoc();
59    /** A Map<Thread, LinkedList<ThreadTask> > of the class loading tasks
60     * associated with a thread
61     */

62    private static Map JavaDoc loadTasksByThread = Collections.synchronizedMap(new WeakHashMap JavaDoc());
63
64    private static SecurityManager JavaDoc sm = System.getSecurityManager();
65
66    /** A UCL and its relative ordering with respect to the class loading.
67     * The UCL with the lowest order to load a class is the UCL that will
68     * populate the repository cache and be assigned as the UCL.loadClass
69     * return value.
70     */

71    public static class PkgClassLoader
72    {
73       public final DomainClassLoaderUCLImpl ucl;
74       public final int order;
75
76       public PkgClassLoader(DomainClassLoaderUCLImpl ucl)
77       {
78          this(ucl, Integer.MAX_VALUE);
79       }
80       public PkgClassLoader(DomainClassLoaderUCLImpl ucl, int order)
81       {
82          this.ucl = ucl;
83          this.order = order;
84       }
85       
86       public String JavaDoc toString()
87       {
88          StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(100);
89          buffer.append(super.toString());
90          buffer.append("{ucl=").append(ucl);
91          buffer.append(" order=").append(order);
92          buffer.append('}');
93          return buffer.toString();
94       }
95    }
96    /** A PrivilegedAction for locating a class as a resource
97     *
98     */

99    private static class ResourceAction implements PrivilegedAction JavaDoc
100    {
101       DomainClassLoader ucl;
102       String JavaDoc classRsrcName;
103       ResourceAction(DomainClassLoader ucl, String JavaDoc classRsrcName)
104       {
105          this.ucl = ucl;
106          this.classRsrcName = classRsrcName;
107       }
108       public Object JavaDoc run()
109       {
110          URL JavaDoc url = ucl.loadResourceLocally(classRsrcName);
111          ucl = null;
112          classRsrcName = null;
113          return url;
114       }
115    }
116
117    /** Register that a thread owns the UCL3.loadClass monitor. This is called
118     * from within UCL3.loadClass(String,boolean) and this method creates
119     * entries in the loadClassThreads and loadTasksByThread maps.
120     */

121    public static void registerLoaderThread(DomainClassLoader ucl, Thread JavaDoc t)
122    {
123       synchronized( registrationLock )
124       {
125          Object JavaDoc prevThread = loadClassThreads.put(ucl, t);
126          if( log.isTraceEnabled() )
127             log.trace("registerLoaderThread, ucl="+ucl+", t="+t+", prevT="+prevThread);
128
129          synchronized( loadTasksByThread )
130          {
131             List JavaDoc taskList = (List JavaDoc) loadTasksByThread.get(t);
132             if( taskList == null )
133             {
134                taskList = Collections.synchronizedList(new LinkedList JavaDoc());
135                loadTasksByThread.put(t, taskList);
136                if( log.isTraceEnabled() )
137                   log.trace("created new task list");
138             }
139          }
140          registrationLock.notifyAll();
141       }
142    }
143
144    /** Initiate the class loading task. This is called by UCL3.loadClass to
145     * initiate the process of loading the requested class. This first attempts
146     * to load the class from the repository cache, and then the class loaders
147     * in the repsository. If the package of the class is found in the repository
148     * then one or more ThreadTask are created to complete the ClassLoadingTaskDCL.
149     * The ThreadTask are assigned to the threads that own the associated UCL3
150     * monitor. If no class loader serves the class package, then the requesting
151     * class loader is asked if it can load the class.
152     *
153     * @return true if the class could be loaded from the cache or requesting
154     * UCL3, false to indicate the calling thread must process the
155     * tasks assigned to it until the ClassLoadingTaskDCL state is FINISHED
156     * @exception ClassNotFoundException if there is no chance the class can
157     * be loaded from the current repository class loaders.
158     */

159    public static boolean beginLoadTask(ClassLoadingTaskDCL task,
160          UnifiedLoaderRepositoryDCL repository)
161       throws ClassNotFoundException JavaDoc
162    {
163       boolean trace = log.isTraceEnabled();
164       if( trace )
165          log.trace("Begin beginLoadTask, task="+task);
166
167       // Try the cache before anything else.
168
Class JavaDoc cls = repository.loadClassFromCache(task.classname);
169       if( cls != null )
170       {
171          task.loadedClass = cls;
172          task.state = ClassLoadingTaskDCL.FINISHED;
173          if( trace )
174             log.trace("End beginLoadTask, loadClassFromCache, classname: "+task.classname);
175          return true;
176       }
177
178       // Next get the set of class loaders from the packages map
179
Set JavaDoc pkgSet = repository.getPackageClassLoaders(task.classname);
180       if( pkgSet == null || pkgSet.size() == 0 )
181       {
182          if (task.stopOrder == Integer.MAX_VALUE)
183          {
184             /* If there are no class loaders in the repository capable of handling
185             the request ask the class loader itself in the event that its parent(s)
186             can load the class.
187             */

188             try
189             {
190                cls = repository.loadClassFromClassLoader(task.classname, false,
191                   task.requestingClassLoader);
192             }
193             catch(LinkageError JavaDoc e)
194             {
195                if( trace )
196                   log.trace("End beginLoadTask, LinkageError for task: "+task, e);
197                throw e;
198             }
199             if( cls != null )
200             {
201                task.loadedClass = cls;
202                task.state = ClassLoadingTaskDCL.FINISHED;
203                if( trace )
204                   log.trace("End beginLoadTask, loadClassFromClassLoader");
205                return true;
206             }
207          }
208
209          // Else, fail the load
210
if( trace )
211             log.trace("End beginLoadTask, ClassNotFoundException");
212          String JavaDoc msg = "No ClassLoaders found for: "+task.classname;
213          throw new ClassNotFoundException JavaDoc(msg);
214       }
215
216       /* A class loading task for each ClassLoader is needed. There can be
217          multiple class loaders for a pkg due to the pkg being spread out over
218          multiple jars, or duplicate classes due to versioning/patches, or
219          just bad packaging.
220
221          In the case of a non-scoped deployment of multiple classes which
222          will provide a PkgClassLoader to define the ordering, we simply
223          choose an ordering based on the order the UCL3s were added to the
224          repository. At most one of the candidate UCL3s will load the class
225          in order to avoid ClassCastExceptions or LinkageErrors due to the
226          strong Java type system/security model.
227
228          TODO: A simple ordering mechanism exists, but this probably needs
229          to be augmented.
230        */

231       Iterator JavaDoc iter = pkgSet.iterator();
232       DomainClassLoaderUCLImpl theUCL = null;
233       int order = Integer.MAX_VALUE;
234       while( iter.hasNext() )
235       {
236          Object JavaDoc next = iter.next();
237          int uclOrder;
238          DomainClassLoaderUCLImpl ucl;
239          // This may be either a PkgClassLoader or a UCL3
240
if( next instanceof DomainClassLoaderUCLImpl )
241          {
242             ucl = (DomainClassLoaderUCLImpl) next;
243             uclOrder = 0; //ucl.getAddedOrder();
244
}
245          else
246          {
247             PkgClassLoader pkgUcl = (PkgClassLoader) next;
248             ucl = pkgUcl.ucl;
249             uclOrder = pkgUcl.order;
250          }
251
252          // If we have a stop order check it
253
if (task.stopOrder != Integer.MAX_VALUE && task.stopOrder <= uclOrder)
254             break;
255          
256          // Validate that the ucl has the class as a resource
257
String JavaDoc classRsrcName = task.classname.replace('.', '/') + ".class";
258          URL JavaDoc url = null;
259          if( sm != null )
260          {
261             ResourceAction action = new ResourceAction(ucl, classRsrcName);
262             url = (URL JavaDoc) AccessController.doPrivileged(action);
263          }
264          else
265          {
266             url = ucl.loadResourceLocally(classRsrcName);
267          }
268
269          if( url != null && uclOrder < order )
270          {
271             if( trace && theUCL != null )
272                log.trace("Replacing UCL: "+theUCL+" with UCL:"+ucl);
273             theUCL = ucl;
274             order = uclOrder;
275          }
276       }
277       if( theUCL == null && task.stopOrder == Integer.MAX_VALUE)
278       {
279          /* If there are no class loaders in the repository capable of handling
280          the request ask the class loader itself in the event that its parent(s)
281          can load the class. But not if we have a stopOrder.
282          */

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

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

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

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

410             try
411             {
412                if( trace )
413                   log.trace("Run failed with exception", e);
414                // Reschedule and update the loadTask.threadTaskCount
415
scheduleTask(loadTask, ucl3, Integer.MAX_VALUE, true, trace);
416             }
417             catch(ClassNotFoundException JavaDoc ex)
418             {
419                loadTask.setLoadError(ex);
420                log.warn("Failed to reschedule task after CCE", ex);
421             }
422             if( trace )
423                log.trace("Post CCE state, loadTask="+loadTask);
424          }
425          else
426          {
427             loadTask.setLoadError(e);
428             if( trace )
429                log.trace("Run failed with exception", e);
430          }
431       }
432       finally
433       {
434          // We must release the loadLock acquired in beginLoadTask
435
if( threadTask.releaseInNextTask == true )
436          {
437             if( trace )
438                log.trace("Releasing loadLock and ownership of UCL: "+threadTask.ucl);
439             synchronized( registrationLock )
440             {
441                loadClassThreads.remove(threadTask.ucl);
442             }
443             synchronized( threadTask.ucl )
444             {
445                ucl3.release();
446                ucl3.notifyAll();
447             }
448          }
449       }
450
451       // If the ThreadTasks are complete mark the ClassLoadingTaskDCL finished
452
if( loadTask.threadTaskCount == 0 )
453       {
454          Class JavaDoc loadedClass = threadTask.getLoadedClass();
455          if( loadedClass != null )
456          {
457             ClassLoader JavaDoc loader = loadedClass.getClassLoader();
458             ClassLoader JavaDoc wrapper = null;
459             if (wrapper != null)
460                loader=wrapper;
461             // Place the loaded class into the repositry cache
462
//repository.cacheLoadedClass(threadTask.getClassname(), loadedClass, loader);
463
}
464          /*
465          synchronized( loadTask )
466          {
467             if( trace )
468                log.trace("Notifying task of thread completion, loadTask:"+loadTask);
469             loadTask.state = ClassLoadingTaskDCL.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 = ClassLoadingTaskDCL.FINISHED;
479             loadTaskThreadTasks.notify();
480          }
481       }
482       if( trace )
483          log.trace("End nextTask("+taskList.size()+"), loadTask="+loadTask);
484    }
485
486    /** Complete a ClassLoadingTaskDCL. This is called by UCL3.loadClass to indicate
487     * that the thread is existing the loadClass method.
488     */

489    public static void endLoadTask(ClassLoadingTaskDCL 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             ClassLoadingTaskDCL 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 = ClassLoadingTaskDCL.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 = ClassLoadingTaskDCL.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 ClassLoadingTaskDCL.
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(ClassLoadingTaskDCL task, DomainClassLoaderUCLImpl 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