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.MalformedURLException;
025: import java.net.URL;
026: import java.net.URLClassLoader;
027: import java.util.ArrayList;
028: import java.util.HashMap;
029: import java.util.HashSet;
030: import java.util.Iterator;
031: import java.util.List;
032: import java.util.Enumeration;
033: import java.util.Map;
034: import java.util.Set;
035: import java.io.IOException;
036:
037: import javax.management.ListenerNotFoundException;
038: import javax.management.MBeanNotificationInfo;
039: import javax.management.Notification;
040: import javax.management.NotificationEmitter;
041: import javax.management.NotificationFilter;
042: import javax.management.NotificationListener;
043: import javax.management.MBeanRegistration;
044: import javax.management.ObjectName;
045: import javax.management.MBeanServer;
046: import javax.management.loading.MLet;
047:
048: import org.jboss.logging.Logger;
049: import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
050:
051: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
052: import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
053:
054: /** A repository of class loaders that form a flat namespace of classes
055: * and resources. This version uses UnifiedClassLoader3 instances. Class
056: * and resource loading is synchronized by the acquiring the monitor to the
057: * associated repository structure monitor. See the variable javadoc comments
058: * for what monitor is used to access a given structure.
059: *
060: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>.
061: * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>.
062: * @version $Revision: 57200 $
063: * just a hint... xdoclet not really used
064: * @jmx.name="JMImplementation:service=UnifiedLoaderRepository,name=Default"
065: */
066: public class UnifiedLoaderRepository3 extends LoaderRepository
067: implements MBeanRegistration, NotificationEmitter,
068: UnifiedLoaderRepository3MBean {
069: // Static --------------------------------------------------------
070: private static final Logger log = Logger
071: .getLogger(UnifiedLoaderRepository3.class);
072: /** Used to provide a relative ordering of UCLs based on the order in
073: * which they are added to the repository */
074: private static int addedCount;
075:
076: // Attributes ----------------------------------------------------
077:
078: /** HashSet<UCL> of classloaders in the repository.
079: * Access synchronized via this.classLoaders monitor.
080: */
081: private CopyOnWriteArraySet classLoaders = new CopyOnWriteArraySet();
082: /** HashSet<UCL> of class loaders in the repository that have a dynamic
083: * URL associated with them. Such a class loader is added to every package
084: * class loader set in #getPackageClassLoaders(String).
085: */
086: private HashSet dynamicClassLoaders = new HashSet();
087: /** A HashMap<ClassLoader, UCL> of foreign (non-UCL) classloaders that
088: * have been added to the repository as the key and the value the UCL
089: * actually used by the ULR.
090: * Access synchronized via this.classLoaders monitor.
091: */
092: private HashMap nonUCLClassLoader = new HashMap();
093:
094: /** A HashSet<URL> used to check for duplicate URLs. Previously this was handled
095: by the UCL.equals, but this caused problems with Class.forName(String,
096: boolean, ClassLoader) caching.
097: Access synchronized via this.classLoaders monitor.
098: */
099: private HashSet classLoaderURLs = new HashSet();
100:
101: /** The loaded classes cache, HashMap<String, Class>.
102: * Access synchronized via this.classes monitor.
103: */
104: private ConcurrentReaderHashMap classes = new ConcurrentReaderHashMap();
105:
106: /** HashMap<UCL, HashSet<String>> class loaders to the set of class names
107: * loaded via the UCL.
108: * Access synchronized via this.classes monitor.
109: */
110: private HashMap loaderToClassesMap = new HashMap();
111:
112: /** HashMap<UCL, HashMap<String, URL>> class loaders to the set of
113: * resource names they looked up.
114: * Access synchronized via this.loaderToResourcesMap monitor.
115: */
116: private HashMap loaderToResourcesMap = new HashMap();
117:
118: /** HashMap<String, ResourceInfo(URL, UCL)> of global resources not unique
119: * to a UCL
120: * Access synchronized via this.loaderToResourcesMap monitor.
121: */
122: private HashMap globalResources = new HashMap();
123:
124: /** A HashMap<String, Set<UCL>> of package names to the set of
125: * ClassLoaders which have classes in the package.
126: * Access synchronized via this.packagesMap monitor.
127: */
128: private ConcurrentReaderHashMap packagesMap = new ConcurrentReaderHashMap();
129:
130: /** A HashMap<UCL, String[]> of class loaders to the array of pckages names
131: * they serve
132: * Access synchronized via this.packagesMap monitor.
133: */
134: private HashMap loaderToPackagesMap = new HashMap();
135:
136: /**
137: * The sequenceNumber used to number notifications.
138: */
139: private long sequenceNumber = 0;
140:
141: /**
142: * We delegate our notification sending to a support object.
143: */
144: private final JBossNotificationBroadcasterSupport broadcaster = new JBossNotificationBroadcasterSupport();
145:
146: /**
147: * The NotificationInfo we emit.
148: */
149: private MBeanNotificationInfo[] info;
150:
151: // Public --------------------------------------------------------
152:
153: public RepositoryClassLoader newClassLoader(final URL url,
154: boolean addToRepository) throws Exception {
155: UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null,
156: this );
157: if (addToRepository)
158: this .registerClassLoader(ucl);
159: return ucl;
160: }
161:
162: public RepositoryClassLoader newClassLoader(final URL url,
163: final URL origURL, boolean addToRepository)
164: throws Exception {
165: UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL,
166: this );
167: if (addToRepository)
168: this .registerClassLoader(ucl);
169: return ucl;
170: }
171:
172: public int getCacheSize() {
173: return classes.size();
174: }
175:
176: public int getClassLoadersSize() {
177: return classLoaders.size();
178: }
179:
180: public void flush() {
181: synchronized (classes) {
182: classes.clear();
183: }
184: }
185:
186: public Class getCachedClass(String classname) {
187: return (Class) classes.get(classname);
188: }
189:
190: /** Unlike other implementations of LoaderRepository, this method does
191: * nothing but ask the UnifiedClassLoader3 to load the class as UCL3s
192: * do not use this method.
193: */
194: public Class loadClass(String name, boolean resolve, ClassLoader cl)
195: throws ClassNotFoundException {
196: RepositoryClassLoader rcl = getRepositoryClassLoader(cl, name);
197: return rcl.loadClass(name, resolve);
198: }
199:
200: /** Called by LoadMgr to obtain all class loaders for the given className
201: * @return Set<UnifiedClassLoader3>, may be null
202: */
203: public Set getPackageClassLoaders(String className) {
204: String pkgName = ClassLoaderUtils.getPackageName(className);
205: Set pkgSet = (Set) packagesMap.get(pkgName);
206: if (dynamicClassLoaders.size() > 0) {
207: if (pkgSet == null)
208: pkgSet = ClassLoaderUtils.newPackageSet();
209: pkgSet.addAll(dynamicClassLoaders);
210: }
211: return pkgSet;
212: }
213:
214: private String getResourcePackageName(String rsrcName) {
215: int index = rsrcName.lastIndexOf('/');
216: String pkgName = rsrcName;
217: if (index > 0)
218: pkgName = rsrcName.substring(0, index);
219: return pkgName.replace('/', '.');
220: }
221:
222: /** Lookup a Class from the repository cache.
223: * @param name the fully qualified class name
224: * @return the cached Class if found, null otherwise
225: */
226: public Class loadClassFromCache(String name) {
227: Class cls = null;
228: synchronized (classes) {
229: cls = (Class) classes.get(name);
230: }
231: return cls;
232: }
233:
234: /** Add a Class to the repository cache.
235: * @param name the fully qualified class name
236: * @param cls the Class instance
237: * @param cl the repository UCL
238: */
239: public void cacheLoadedClass(String name, Class cls, ClassLoader cl) {
240: synchronized (classes) {
241: // Update the global cache
242: Object prevClass = classes.put(name, cls);
243: if (log.isTraceEnabled()) {
244: log.trace("cacheLoadedClass, classname: " + name
245: + ", class: " + cls + ", ucl: " + cl
246: + ", prevClass: " + prevClass);
247: }
248:
249: // Update the cache for this classloader
250: // This is used to cycling classloaders
251: HashSet loadedClasses = (HashSet) loaderToClassesMap
252: .get(cl);
253: if (loadedClasses == null) {
254: loadedClasses = new HashSet();
255: loaderToClassesMap.put(cl, loadedClasses);
256: }
257: loadedClasses.add(name);
258: }
259: }
260:
261: Class loadClassFromClassLoader(String name, boolean resolve,
262: RepositoryClassLoader cl) {
263: try {
264: Class cls = cl.loadClassLocally(name, resolve);
265: cacheLoadedClass(name, cls, cl);
266: return cls;
267: } catch (ClassNotFoundException x) {
268: // The class is not visible by the calling classloader
269: }
270: return null;
271: }
272:
273: /**
274: * Loads a resource following the Unified ClassLoader architecture
275: */
276: public URL getResource(String name, ClassLoader cl) {
277: // getResource() calls are not synchronized on the classloader from JDK code.
278: // First ask the cache (of the calling classloader)
279: URL resource = getResourceFromCache(name, cl);
280:
281: // The resource was already loaded by the calling classloader, we're done
282: if (resource != null)
283: return resource;
284:
285: // Not found in cache, ask the calling classloader
286: resource = getResourceFromClassLoader(name, cl);
287:
288: // The calling classloader sees the resource, we're done
289: if (resource != null)
290: return resource;
291:
292: // Not found in classloader, ask the global cache
293: resource = getResourceFromGlobalCache(name);
294:
295: // The cache has it, we are done
296: if (resource != null)
297: return resource;
298:
299: // Not visible in global cache, iterate on all classloaders
300: resource = getResourceFromRepository(name, cl);
301:
302: // Some other classloader sees the resource, we're done
303: if (resource != null)
304: return resource;
305:
306: // This resource is not visible
307: return null;
308: }
309:
310: /** Find all resource URLs for the given name. This is entails an
311: * exhuastive search of the repository and is an expensive operation.
312: *
313: * @param name the resource name
314: * @param cl the requesting class loader
315: * @param urls a list into which the located resource URLs will be placed
316: */
317: public void getResources(String name, ClassLoader cl, List urls) {
318: // Go through all class loaders
319: Iterator iter = classLoaders.iterator();
320: while (iter.hasNext() == true) {
321: ClassLoader nextCL = (ClassLoader) iter.next();
322: if (nextCL instanceof RepositoryClassLoader) {
323: RepositoryClassLoader ucl = (RepositoryClassLoader) nextCL;
324: try {
325: Enumeration resURLs = ucl
326: .findResourcesLocally(name);
327: while (resURLs.hasMoreElements()) {
328: Object res = resURLs.nextElement();
329: urls.add(res);
330: }
331: } catch (IOException ignore) {
332: }
333: }
334: }
335: }
336:
337: /** As opposed to classes, resource are not looked up in a global cache,
338: * since it is possible that 2 classloaders have the same resource name
339: * (ejb-jar.xml), a global cache will overwrite. Instead we look in the
340: * classloader's cache that we mantain to cycle the classloaders
341: * @param name the resource name
342: * @param cl the repository classloader
343: * @return the resource URL if found, null otherwise
344: */
345: private URL getResourceFromCache(String name, ClassLoader cl) {
346: URL resource = null;
347: synchronized (loaderToResourcesMap) {
348: if (loaderToResourcesMap.containsKey(cl)) {
349: HashMap resources = (HashMap) loaderToResourcesMap
350: .get(cl);
351: resource = (URL) resources.get(name);
352: }
353: }
354: return resource;
355: }
356:
357: private URL getResourceFromClassLoader(String name, ClassLoader cl) {
358: URL resource = null;
359: if (cl instanceof RepositoryClassLoader) {
360: RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
361: resource = ucl.getResourceLocally(name);
362: cacheLoadedResource(name, resource, cl);
363: }
364: return resource;
365: }
366:
367: /** Check for a resource in the global cache
368: * Synchronizes access to globalResources using the loaderToResourcesMap monitor
369: * @param name
370: * @return
371: */
372: protected URL getResourceFromGlobalCache(String name) {
373: ResourceInfo ri = null;
374: synchronized (loaderToResourcesMap) {
375: ri = (ResourceInfo) globalResources.get(name);
376: }
377: URL resource = null;
378: if (ri != null)
379: resource = ri.url;
380: return resource;
381: }
382:
383: protected URL getResourceFromRepository(String name, ClassLoader cl) {
384: // Get the set of class loaders from the packages map
385: String pkgName = getResourcePackageName(name);
386: Iterator i = null;
387: Set pkgSet = (Set) this .packagesMap.get(pkgName);
388: if (pkgSet != null) {
389: i = pkgSet.iterator();
390: }
391: if (i == null) {
392: // If no pkg match was found just go through all class loaders
393: i = classLoaders.iterator();
394: }
395:
396: URL url = null;
397: while (i.hasNext() == true) {
398: ClassLoader classloader = (ClassLoader) i.next();
399: if (classloader.equals(cl)) {
400: continue;
401: }
402:
403: if (classloader instanceof RepositoryClassLoader) {
404: url = ((RepositoryClassLoader) classloader)
405: .getResourceLocally(name);
406: if (url != null) {
407: cacheLoadedResource(name, url, classloader);
408: cacheGlobalResource(name, url, classloader);
409: break;
410: } else {
411: // Do nothing, go on with next classloader
412: }
413: }
414: }
415: return url;
416: }
417:
418: /** Update the loaderToResourcesMap
419: * @param name the resource name
420: * @param url the resource URL
421: * @param cl the UCL
422: */
423: private void cacheLoadedResource(String name, URL url,
424: ClassLoader cl) {
425: // Update the cache for this classloader only
426: // This is used for cycling classloaders
427: synchronized (loaderToResourcesMap) {
428: HashMap resources = (HashMap) loaderToResourcesMap.get(cl);
429: if (resources == null) {
430: resources = new HashMap();
431: loaderToResourcesMap.put(cl, resources);
432: }
433: resources.put(name, url);
434: }
435: }
436:
437: /** Update cache of resources looked up via one UCL, buf found in another UCL
438: * @param name the resource name
439: * @param url the resource URL
440: * @param cl the UCL
441: */
442: private void cacheGlobalResource(String name, URL url,
443: ClassLoader cl) {
444: synchronized (loaderToResourcesMap) {
445: globalResources.put(name, new ResourceInfo(url, cl));
446: }
447: }
448:
449: /** This is a utility method a listing of the URL for all UnifiedClassLoaders
450: * associated with the repository. It is never called in response to
451: * class or resource loading.
452: */
453: public URL[] getURLs() {
454: HashSet classpath = new HashSet();
455: Set tmp = classLoaders;
456: for (Iterator iter = tmp.iterator(); iter.hasNext();) {
457: Object obj = iter.next();
458: if (obj instanceof RepositoryClassLoader) {
459: RepositoryClassLoader cl = (RepositoryClassLoader) obj;
460: URL[] urls = cl.getClasspath();
461: int length = urls != null ? urls.length : 0;
462: for (int u = 0; u < length; u++) {
463: URL path = urls[u];
464: classpath.add(path);
465: }
466: }
467: } // for all ClassLoaders
468:
469: URL[] cp = new URL[classpath.size()];
470: classpath.toArray(cp);
471: return cp;
472: }
473:
474: /** A utility method that iterates over all repository class loaders and
475: * display the class information for every UCL that contains the given
476: * className
477: */
478: public String displayClassInfo(String className) {
479: /* We have to find the class as a resource as we don't want to invoke
480: loadClass(name) and cause the side-effect of loading new classes.
481: */
482: String classRsrcName = className.replace('.', '/') + ".class";
483:
484: int count = 0;
485: Class loadedClass = this .loadClassFromCache(className);
486: StringBuffer results = new StringBuffer(className
487: + " Information\n");
488: if (loadedClass != null) {
489: results.append("Repository cache version:");
490: ClassLoaderUtils.displayClassInfo(loadedClass, results);
491: } else {
492: results.append("Not loaded in repository cache\n");
493: }
494: Set tmp = classLoaders;
495: for (Iterator iter = tmp.iterator(); iter.hasNext();) {
496: URLClassLoader cl = (URLClassLoader) iter.next();
497: URL classURL = cl.findResource(classRsrcName);
498: if (classURL != null) {
499: results.append("\n\n### Instance" + count
500: + " found in UCL: " + cl + "\n");
501: count++;
502: }
503: }
504:
505: // Also look to the parent class loaders of the TCL
506: ClassLoader tcl = Thread.currentThread()
507: .getContextClassLoader();
508: URLClassLoader[] stack = ClassLoaderUtils
509: .getClassLoaderStack(tcl);
510: for (int s = 0; s < stack.length; s++) {
511: URLClassLoader cl = stack[s];
512: URL classURL = cl.findResource(classRsrcName);
513: if (classURL != null) {
514: results.append("\n\n### Instance" + count
515: + " via UCL: " + cl + "\n");
516: count++;
517: }
518: }
519:
520: return results.toString();
521: }
522:
523: // LoaderRepository overrides ------------------------------------
524:
525: /** First tries to load from any UCL in the ULR, and if the
526: * class is not found, next tries the current thread context
527: * class loader.
528: * @param className - the class to load
529: */
530: public Class loadClass(String className)
531: throws ClassNotFoundException {
532: // Try to load from a UCL in the ULR first
533: ClassLoader scl = Thread.currentThread()
534: .getContextClassLoader();
535: ClassLoader ucl = null;
536: if (classLoaders.size() > 0)
537: ucl = (ClassLoader) this .classLoaders.iterator().next();
538: try {
539: if (ucl != null)
540: return loadClass(className, false, ucl);
541: } catch (ClassNotFoundException ignore) {
542: // go on and try the next loader
543: }
544:
545: try {
546: // If there is no class try the TCL
547: return scl.loadClass(className);
548: } catch (ClassNotFoundException e) {
549: // go on and try the next loader
550: }
551:
552: // at last try a native
553: Class clazz = getNativeClassForName(className);
554: if (clazz != null)
555: return clazz;
556:
557: throw new ClassNotFoundException(className);
558: }
559:
560: /**
561: * Loads a class from the repository, excluding the given
562: * classloader.
563: *
564: * @param loader the classloader to exclude
565: * @param className the class to load
566: * @return the found class
567: * @exception ClassNotFoundException when there is no such class
568: */
569: public Class loadClassWithout(ClassLoader loader, String className)
570: throws ClassNotFoundException {
571: throw new ClassNotFoundException("NYI");
572: }
573:
574: /**
575: * Loads a class from the repository, using the classloaders that were
576: * registered before the given classloader.
577: *
578: * @param stop consult all the classloaders registered before this one
579: * in an attempt to load a class
580: * @param className name of the class to load
581: * @return loaded class instance
582: * @throws ClassNotFoundException if none of the consulted classloaders were
583: * able to load the requested class
584: */
585: public Class loadClassBefore(ClassLoader stop, String className)
586: throws ClassNotFoundException {
587: RepositoryClassLoader stopAt = getRepositoryClassLoader(stop,
588: className);
589: return stopAt.loadClassBefore(className);
590: }
591:
592: /**
593: * Get any wrapping classloader for the passed classloader
594: *
595: * @param cl the wrapped classloader
596: * @return the wrapping classloader or null if not wrapped
597: */
598: public RepositoryClassLoader getWrappingClassLoader(ClassLoader cl) {
599: synchronized (classLoaders) {
600: return (RepositoryClassLoader) nonUCLClassLoader.get(cl);
601: }
602: }
603:
604: /** Add a class loader to the repository.
605: */
606: public void addClassLoader(ClassLoader loader) {
607: // if you come to us as UCL we send you straight to the orbit
608: if (loader instanceof RepositoryClassLoader)
609: addRepositoryClassLoader((RepositoryClassLoader) loader);
610: else if (loader instanceof MLet) {
611: addMLetClassLoader((MLet) loader);
612: } else if (loader instanceof URLClassLoader) {
613: addURLClassLoader((URLClassLoader) loader);
614: } else {
615: log.warn("Tried to add non-URLClassLoader. Ignored");
616: } // end of else
617: }
618:
619: public boolean addClassLoaderURL(ClassLoader cl, URL url) {
620: RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
621: boolean added = false;
622: synchronized (classLoaders) {
623: // Strip any query parameter
624: String query = url.getQuery();
625: if (query != null) {
626: String ext = url.toExternalForm();
627: String ext2 = ext.substring(0, ext.length()
628: - query.length() - 1);
629: try {
630: url = new URL(ext2);
631: } catch (MalformedURLException e) {
632: log.warn("Failed to strip query from: " + url, e);
633: }
634: }
635:
636: // See if the URL is associated with a class loader
637: if (classLoaderURLs.contains(url) == false) {
638: updatePackageMap(ucl, url);
639: classLoaderURLs.add(url);
640: added = true;
641: // Check for a dynamic URL
642: if (query != null && query.indexOf("dynamic=true") >= 0)
643: dynamicClassLoaders.add(ucl);
644: }
645: }
646: return added;
647: }
648:
649: /** Add a UCL to the repository.
650: * This sychronizes on classLoaders.
651: * @param cl
652: */
653: private void addRepositoryClassLoader(RepositoryClassLoader cl) {
654: cl.setRepository(this );
655: // See if this URL already exists
656: URL url = cl.getURL();
657: boolean added = false;
658: synchronized (classLoaders) {
659: boolean exists = false;
660: if (cl instanceof UnifiedClassLoader)
661: exists = classLoaderURLs.contains(url);
662: // If already present will not be added
663: if (!exists) {
664: if (url != null)
665: classLoaderURLs.add(url);
666: added = classLoaders.add(cl);
667: }
668: if (added) {
669: log.debug("Adding " + cl);
670: addedCount++;
671: cl.setAddedOrder(addedCount);
672: updatePackageMap(cl);
673: } else {
674: log.debug("Skipping duplicate " + cl);
675: }
676: }
677: }
678:
679: private void addMLetClassLoader(MLet loader) {
680: MLetRepositoryClassLoader rcl = new MLetRepositoryClassLoader(
681: loader);
682: synchronized (classLoaders) {
683: nonUCLClassLoader.put(loader, rcl);
684: }
685: addRepositoryClassLoader(rcl);
686: }
687:
688: private void addURLClassLoader(URLClassLoader loader) {
689: URL[] urls = loader.getURLs();
690: int count = urls != null && urls.length > 0 ? urls.length : 0;
691: URL origURL = count > 0 ? urls[0] : null;
692: UnifiedClassLoader3 ucl3 = new UnifiedClassLoader3(origURL,
693: origURL, this );
694: addRepositoryClassLoader(ucl3);
695: synchronized (classLoaders) {
696: nonUCLClassLoader.put(loader, ucl3);
697: }
698: for (int i = 1; i < count; i++) {
699: this .addClassLoaderURL(ucl3, urls[i]);
700: }
701: }
702:
703: /** Walk through the class loader URL to see what packages it is capable
704: of handling
705: */
706: private void updatePackageMap(RepositoryClassLoader cl) {
707: try {
708: String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl,
709: packagesMap);
710: loaderToPackagesMap.put(cl, pkgNames);
711: } catch (Exception e) {
712: if (log.isTraceEnabled())
713: log.trace("Failed to update pkgs for cl=" + cl, e);
714: else
715: log.debug("Failed to update pkgs for cl=" + cl, e);
716: }
717: }
718:
719: /** Walk through the new URL to update the packages the ClassLoader is
720: * capable of handling
721: */
722: private void updatePackageMap(RepositoryClassLoader cl, URL url) {
723: try {
724: String[] prevPkgNames = (String[]) loaderToPackagesMap
725: .get(cl);
726: String[] pkgNames = ClassLoaderUtils.updatePackageMap(cl,
727: packagesMap, url, prevPkgNames);
728: loaderToPackagesMap.put(cl, pkgNames);
729: } catch (Exception e) {
730: if (log.isTraceEnabled())
731: log.trace("Failed to update pkgs for cl=" + cl, e);
732: else
733: log.debug("Failed to update pkgs for cl=" + cl, e);
734: }
735: }
736:
737: /** Remove the class loader from the repository. This synchronizes on the
738: * this.classLoaders
739: */
740: public void removeClassLoader(ClassLoader loader) {
741: ArrayList removeNotifications = new ArrayList();
742: ClassLoader cl = loader;
743: synchronized (classLoaders) {
744: if ((loader instanceof RepositoryClassLoader) == false) {
745: cl = (ClassLoader) nonUCLClassLoader.remove(loader);
746: }
747: if (cl instanceof RepositoryClassLoader) {
748: RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
749: if (getTranslator() != null)
750: getTranslator().unregisterClassLoader(ucl);
751: URL[] urls = ucl.getClasspath();
752: for (int u = 0; u < urls.length; u++)
753: classLoaderURLs.remove(urls[u]);
754: }
755: boolean dynamic = dynamicClassLoaders.remove(cl);
756: boolean removed = classLoaders.remove(cl);
757: log.debug("UnifiedLoaderRepository removed(" + removed
758: + ") " + cl);
759:
760: // Take care also of the cycling mapping for classes
761: HashSet loadedClasses = null;
762: boolean hasLoadedClasses = false;
763: synchronized (classes) {
764: hasLoadedClasses = loaderToClassesMap.containsKey(cl);
765: if (hasLoadedClasses)
766: loadedClasses = (HashSet) loaderToClassesMap
767: .remove(cl);
768: // This classloader has loaded at least one class
769: if (loadedClasses != null) {
770: // Notify that classes are about to be removed
771: for (Iterator iter = loadedClasses.iterator(); iter
772: .hasNext();) {
773: String className = (String) iter.next();
774: Notification n = new Notification(
775: CLASS_REMOVED, this ,
776: getNextSequenceNumber(), className);
777: removeNotifications.add(n);
778: }
779:
780: // Remove the classes from the global cache
781: for (Iterator i = loadedClasses.iterator(); i
782: .hasNext();) {
783: String cls = (String) i.next();
784: this .classes.remove(cls);
785: }
786: }
787: }
788:
789: // Take care also of the cycling mapping for resources
790: synchronized (loaderToResourcesMap) {
791: if (loaderToResourcesMap.containsKey(cl)) {
792: HashMap resources = (HashMap) loaderToResourcesMap
793: .remove(cl);
794:
795: // Remove the resources from the global cache that are from this classloader
796: if (resources != null) {
797: for (Iterator i = resources.keySet().iterator(); i
798: .hasNext();) {
799: String name = (String) i.next();
800: ResourceInfo ri = (ResourceInfo) globalResources
801: .get(name);
802: if (ri != null && ri.cl == cl)
803: globalResources.remove(name);
804: }
805: }
806: }
807: }
808:
809: // Clean up the package name to class loader mapping
810: if (dynamic == false) {
811: String[] pkgNames = (String[]) loaderToPackagesMap
812: .remove(cl);
813: int length = pkgNames != null ? pkgNames.length : 0;
814: for (int p = 0; p < length; p++) {
815: String pkgName = pkgNames[p];
816: Set pkgSet = (Set) packagesMap.get(pkgName);
817: if (pkgSet != null) {
818: pkgSet.remove(cl);
819: if (pkgSet.isEmpty())
820: packagesMap.remove(pkgName);
821: }
822: }
823: } else {
824: // A dynamic classloader could end up in any package set
825: loaderToPackagesMap.remove(cl);
826: for (Iterator i = packagesMap.entrySet().iterator(); i
827: .hasNext();) {
828: Map.Entry entry = (Map.Entry) i.next();
829: Set pkgSet = (Set) entry.getValue();
830: pkgSet.remove(cl);
831: if (pkgSet.isEmpty())
832: i.remove();
833: }
834: }
835: }
836:
837: // Send the class removal notfications outside of the synchronized block
838: for (int n = 0; n < removeNotifications.size(); n++) {
839: Notification msg = (Notification) removeNotifications
840: .get(n);
841: broadcaster.sendNotification(msg);
842: }
843:
844: Notification msg = new Notification(CLASSLOADER_REMOVED, this ,
845: getNextSequenceNumber());
846: msg.setUserData(cl);
847: broadcaster.sendNotification(msg);
848: }
849:
850: /**
851: * This method provides an mbean-accessible way to add a
852: * UnifiedClassloader, and sends a notification when it is added.
853: *
854: * @param ucl an <code>UnifiedClassLoader</code> value
855: * @return a <code>LoaderRepository</code> value
856: *
857: * @jmx.managed-operation
858: */
859: public LoaderRepository registerClassLoader(
860: RepositoryClassLoader ucl) {
861: addClassLoader(ucl);
862: Notification msg = new Notification(CLASSLOADER_ADDED, this ,
863: getNextSequenceNumber());
864: msg.setUserData(ucl);
865: broadcaster.sendNotification(msg);
866:
867: return this ;
868: }
869:
870: /**
871: * @jmx.managed-operation
872: */
873: public LoaderRepository getInstance() {
874: return this ;
875: }
876:
877: // implementation of javax.management.NotificationBroadcaster interface
878:
879: /**
880: * addNotificationListener delegates to the broadcaster object we hold.
881: *
882: * @param listener a <code>NotificationListener</code> value
883: * @param filter a <code>NotificationFilter</code> value
884: * @param handback an <code>Object</code> value
885: * @exception IllegalArgumentException if an error occurs
886: */
887: public void addNotificationListener(NotificationListener listener,
888: NotificationFilter filter, Object handback)
889: throws IllegalArgumentException {
890: broadcaster.addNotificationListener(listener, filter, handback);
891: }
892:
893: /**
894: *
895: * @return <description>
896: */
897: public MBeanNotificationInfo[] getNotificationInfo() {
898: if (info == null) {
899: info = new MBeanNotificationInfo[] {
900: new MBeanNotificationInfo(
901: new String[] { "CLASSLOADER_ADDED" },
902: "javax.management.Notification",
903: "Notification that a classloader has been added to the extensible classloader"),
904: new MBeanNotificationInfo(
905: new String[] { "CLASS_REMOVED" },
906: "javax.management.Notification",
907: "Notification that a class has been removed from the extensible classloader")
908:
909: };
910: }
911: return info;
912: }
913:
914: /**
915: * removeNotificationListener delegates to our broadcaster object
916: *
917: * @param listener a <code>NotificationListener</code> value
918: * @exception ListenerNotFoundException if an error occurs
919: */
920: public void removeNotificationListener(NotificationListener listener)
921: throws ListenerNotFoundException {
922: broadcaster.removeNotificationListener(listener);
923: }
924:
925: public void removeNotificationListener(
926: NotificationListener listener, NotificationFilter filter,
927: Object handback) throws ListenerNotFoundException {
928: broadcaster.removeNotificationListener(listener, filter,
929: handback);
930: }
931:
932: // MBeanRegistration
933: public ObjectName preRegister(MBeanServer server, ObjectName name)
934: throws Exception {
935: return name;
936: }
937:
938: public void postRegister(Boolean registrationDone) {
939: }
940:
941: public void preDeregister() throws Exception {
942: }
943:
944: public void postDeregister() {
945: log.debug("postDeregister, clearing all references");
946: classLoaders.clear();
947: dynamicClassLoaders.clear();
948: nonUCLClassLoader.clear();
949: classLoaderURLs.clear();
950: classes.clear();
951: loaderToClassesMap.clear();
952: loaderToResourcesMap.clear();
953: globalResources.clear();
954: packagesMap.clear();
955: loaderToPackagesMap.clear();
956: }
957:
958: private synchronized long getNextSequenceNumber() {
959: return sequenceNumber++;
960: }
961:
962: private RepositoryClassLoader getRepositoryClassLoader(
963: ClassLoader cl, String name) throws ClassNotFoundException {
964: if (cl instanceof RepositoryClassLoader)
965: return (RepositoryClassLoader) cl;
966: else {
967: RepositoryClassLoader rcl = getWrappingClassLoader(cl);
968: if (rcl == null)
969: throw new ClassNotFoundException("Class not found "
970: + name + " (Unknown classloader " + cl + ")");
971: return rcl;
972: }
973: }
974: }
|