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