KickJava   Java API By Example, From Geeks To Geeks.

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


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.MalformedURLException JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.net.URLClassLoader JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Arrays JavaDoc;
29 import java.util.Comparator JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Enumeration JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.TreeSet JavaDoc;
38 import java.util.Vector JavaDoc;
39 import java.io.IOException JavaDoc;
40
41 import javax.management.ListenerNotFoundException JavaDoc;
42 import javax.management.MBeanNotificationInfo JavaDoc;
43 import javax.management.Notification JavaDoc;
44 import javax.management.NotificationBroadcaster JavaDoc;
45 import javax.management.NotificationFilter JavaDoc;
46 import javax.management.NotificationListener JavaDoc;
47 import javax.management.MBeanRegistration JavaDoc;
48 import javax.management.ObjectName JavaDoc;
49 import javax.management.MBeanServer JavaDoc;
50
51 import org.jboss.classloading.spi.ClassLoadingDomain;
52 import org.jboss.classloading.spi.DomainClassLoader;
53 import org.jboss.logging.Logger;
54 import org.jboss.mx.loading.LoadMgr3.PkgClassLoader;
55 import org.jboss.mx.util.JBossNotificationBroadcasterSupport;
56 import org.jboss.util.Classes;
57
58 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
59 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
60
61 /** A repository of class loaders that form a flat namespace of classes
62  * and resources. This version uses DomainClassLoaderUCLImpl instances. Class
63  * and resource loading is synchronized by the acquiring the monitor to the
64  * associated repository structure monitor. See the variable javadoc comments
65  * for what monitor is used to access a given structure.
66  *
67  * @author <a HREF="mailto:scott.stark@jboss.org">Scott Stark</a>.
68  * @author <a HREF="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>.
69  * @version $Revision: 37459 $
70  * just a hint... xdoclet not really used
71  * @jmx.name="JMImplementation:service=UnifiedLoaderRepository,name=Default"
72  */

73 public class UnifiedLoaderRepositoryDCL extends LoaderRepositoryDomain
74    implements MBeanRegistration JavaDoc, NotificationBroadcaster JavaDoc,
75    UnifiedLoaderRepositoryDCLMBean
76 {
77    // Static --------------------------------------------------------
78
private static final Logger log = Logger.getLogger(UnifiedLoaderRepository3.class);
79    /** Used to provide a relative ordering of UCLs based on the order in
80     * which they are added to the repository */

81    private static int addedCount;
82
83    // Attributes ----------------------------------------------------
84

85    /** HashSet<UCL> of classloaders in the repository.
86     * Access synchronized via this.classLoaders monitor.
87     */

88    private CopyOnWriteArraySet classLoaders = new CopyOnWriteArraySet();
89    /** HashSet<UCL> of class loaders in the repository that have a dynamic
90     * URL associated with them. Such a class loader is added to every package
91     * class loader set in #getPackageClassLoaders(String).
92     */

93    private HashSet JavaDoc dynamicClassLoaders = new HashSet JavaDoc();
94    /** A HashMap<DomainClassLoader, UCL> of foreign (non-UCL) classloaders that
95     * have been added to the repository as the key and the value the UCL
96     * actually used by the ULR.
97     * Access synchronized via this.classLoaders monitor.
98     */

99    private HashMap JavaDoc nonUCLClassLoader = new HashMap JavaDoc();
100
101    /** A HashSet<URL> used to check for duplicate URLs. Previously this was handled
102     by the UCL.equals, but this caused problems with Class.forName(String,
103     boolean, DomainClassLoader) caching.
104     Access synchronized via this.classLoaders monitor.
105     */

106    private HashSet JavaDoc classLoaderURLs = new HashSet JavaDoc();
107
108    /** The loaded classes cache, HashMap<String, Class>.
109     * Access synchronized via this.classes monitor.
110     */

111    private ConcurrentReaderHashMap classes = new ConcurrentReaderHashMap();
112
113    /** HashMap<UCL, HashSet<String>> class loaders to the set of class names
114     * loaded via the UCL.
115     * Access synchronized via this.classes monitor.
116     */

117    private HashMap JavaDoc loaderToClassesMap = new HashMap JavaDoc();
118
119    /** HashMap<UCL, HashMap<String, URL>> class loaders to the set of
120     * resource names they looked up.
121     * Access synchronized via this.loaderToResourcesMap monitor.
122     */

123    private HashMap JavaDoc loaderToResourcesMap = new HashMap JavaDoc();
124
125    /** HashMap<String, ResourceInfo(URL, UCL)> of global resources not unique
126     * to a UCL
127     * Access synchronized via this.loaderToResourcesMap monitor.
128     */

129    private HashMap JavaDoc globalResources = new HashMap JavaDoc();
130
131    /** A HashMap<String, Set<UCL>> of package names to the set of
132     * ClassLoaders which have classes in the package.
133     * Access synchronized via this.packagesMap monitor.
134     */

135    private ConcurrentReaderHashMap packagesMap = new ConcurrentReaderHashMap();
136
137    /** A HashMap<UCL, String[]> of class loaders to the array of pckages names
138     * they serve
139     * Access synchronized via this.packagesMap monitor.
140     */

141    private HashMap JavaDoc<DomainClassLoader, List JavaDoc<String JavaDoc>> loaderToPackagesMap = new HashMap JavaDoc<DomainClassLoader, List JavaDoc<String JavaDoc>>();
142
143    /**
144     * The sequenceNumber used to number notifications.
145     */

146    private long sequenceNumber = 0;
147
148    /**
149     * We delegate our notification sending to a support object.
150     */

151    private final JBossNotificationBroadcasterSupport broadcaster = new JBossNotificationBroadcasterSupport();
152
153    /**
154     * The NotificationInfo we emit.
155     */

156    private MBeanNotificationInfo JavaDoc[] info;
157
158
159    // Public --------------------------------------------------------
160

161    public DomainClassLoader newClassLoader(final URL JavaDoc url, boolean addToRepository)
162            throws Exception JavaDoc
163    {
164       URL JavaDoc[] cp = {url};
165       DomainClassLoaderUCLImpl ucl = new DomainClassLoaderUCLImpl(cp, this);
166       if (addToRepository)
167          this.registerClassLoader(ucl);
168       return ucl;
169    }
170
171    public DomainClassLoader newClassLoader(final URL JavaDoc url, final URL JavaDoc origURL, boolean addToRepository)
172            throws Exception JavaDoc
173    {
174       URL JavaDoc[] cp = {url};
175       DomainClassLoaderUCLImpl ucl = new DomainClassLoaderUCLImpl(cp, this);
176       if (addToRepository)
177          this.registerClassLoader(ucl);
178       return ucl;
179    }
180
181    public ClassLoadingDomain getParent()
182    {
183       return null;
184    }
185
186    public int getCacheSize()
187    {
188       return classes.size();
189    }
190
191    public int getClassLoadersSize()
192    {
193       return classLoaders.size();
194    }
195
196    public void flush()
197    {
198       synchronized (classes)
199       {
200          classes.clear();
201       }
202    }
203
204    public Class JavaDoc getCachedClass(String JavaDoc classname)
205    {
206       return (Class JavaDoc) classes.get(classname);
207    }
208
209    /** Calls loadClassFromClassLoader(name, resolve, cl)
210     */

211    public Class JavaDoc loadClass(String JavaDoc name, boolean resolve, DomainClassLoader cl)
212       throws ClassNotFoundException JavaDoc
213    {
214       return this.loadClassFromClassLoader(name, resolve, cl);
215    }
216
217    /** Called by LoadMgr to obtain all class loaders for the given className
218     * @return Set<DomainClassLoaderUCLImpl>, may be null
219     */

220    public Set JavaDoc getPackageClassLoaders(String JavaDoc className)
221    {
222       String JavaDoc pkgName = ClassLoaderUtils.getPackageName(className);
223       Set JavaDoc pkgSet = (Set JavaDoc) packagesMap.get(pkgName);
224       if (dynamicClassLoaders.size() > 0)
225       {
226          if (pkgSet == null)
227             pkgSet = ClassLoaderUtils.newPackageSet();
228          pkgSet.addAll(dynamicClassLoaders);
229       }
230       return pkgSet;
231    }
232
233    private String JavaDoc getResourcePackageName(String JavaDoc rsrcName)
234    {
235       int index = rsrcName.lastIndexOf('/');
236       String JavaDoc pkgName = rsrcName;
237       if (index > 0)
238          pkgName = rsrcName.substring(0, index);
239       return pkgName.replace('/', '.');
240    }
241
242    /** Lookup a Class from the repository cache.
243     * @param name the fully qualified class name
244     * @return the cached Class if found, null otherwise
245     */

246    public Class JavaDoc loadClassFromCache(String JavaDoc name)
247    {
248       Class JavaDoc cls = null;
249       synchronized (classes)
250       {
251          cls = (Class JavaDoc) classes.get(name);
252       }
253       return cls;
254    }
255
256    /** Add a Class to the repository cache.
257     * @param name the fully qualified class name
258     * @param cls the Class instance
259     * @param cl the repository UCL
260     */

261    public void cacheLoadedClass(String JavaDoc name, Class JavaDoc cls, DomainClassLoader cl)
262    {
263       synchronized (classes)
264       {
265          // Update the global cache
266
Object JavaDoc prevClass = classes.put(name, cls);
267          if (log.isTraceEnabled())
268          {
269             log.trace("cacheLoadedClass, classname: " + name + ", class: " + cls
270                     + ", ucl: " + cl + ", prevClass: " + prevClass);
271          }
272
273          // Update the cache for this classloader
274
// This is used to cycling classloaders
275
HashSet JavaDoc loadedClasses = (HashSet JavaDoc) loaderToClassesMap.get(cl);
276          if (loadedClasses == null)
277          {
278             loadedClasses = new HashSet JavaDoc();
279             loaderToClassesMap.put(cl, loadedClasses);
280          }
281          loadedClasses.add(name);
282       }
283    }
284
285    Class JavaDoc loadClassFromClassLoader(String JavaDoc name, boolean resolve, DomainClassLoader cl)
286    {
287       try
288       {
289          Class JavaDoc cls = cl.loadClassLocally(name, resolve);
290          cacheLoadedClass(name, cls, cl);
291          return cls;
292       }
293       catch (ClassNotFoundException JavaDoc x)
294       {
295          // The class is not visible by the calling classloader
296
}
297       return null;
298    }
299
300    /**
301     * Loads a resource following the Unified DomainClassLoader architecture
302     */

303    public URL JavaDoc getResource(String JavaDoc name, DomainClassLoader cl)
304    {
305       // getResource() calls are not synchronized on the classloader from JDK code.
306
// First ask the cache (of the calling classloader)
307
URL JavaDoc resource = getResourceFromCache(name, cl);
308
309       // The resource was already loaded by the calling classloader, we're done
310
if (resource != null)
311          return resource;
312
313       // Not found in cache, ask the calling classloader
314
resource = getResourceFromClassLoader(name, cl);
315
316       // The calling classloader sees the resource, we're done
317
if (resource != null)
318          return resource;
319
320       // Not found in classloader, ask the global cache
321
resource = getResourceFromGlobalCache(name);
322
323       // The cache has it, we are done
324
if (resource != null)
325          return resource;
326
327       // Not visible in global cache, iterate on all classloaders
328
resource = getResourceFromRepository(name, cl);
329
330       // Some other classloader sees the resource, we're done
331
if (resource != null)
332          return resource;
333
334       // This resource is not visible
335
return null;
336    }
337
338    /** Find all resource URLs for the given name. This is entails an
339     * exhuastive search of the repository and is an expensive operation.
340     *
341     * @param name the resource name
342     * @param cl the requesting class loader
343     * @param urls a list into which the located resource URLs will be placed
344     */

345    public void getResources(String JavaDoc name, DomainClassLoader cl, List JavaDoc urls)
346    {
347       // Go through all class loaders
348
Iterator JavaDoc iter = classLoaders.iterator();
349       while (iter.hasNext() == true)
350       {
351          DomainClassLoader nextCL = (DomainClassLoader) iter.next();
352          if (nextCL instanceof DomainClassLoader)
353          {
354             DomainClassLoader ucl = (DomainClassLoader) nextCL;
355             try
356             {
357                Enumeration JavaDoc<URL JavaDoc> resURLs = ucl.findResourcesLocally(name);
358                while (resURLs.hasMoreElements())
359                {
360                   Object JavaDoc res = resURLs.nextElement();
361                   urls.add(res);
362                }
363             }
364             catch (IOException JavaDoc ignore)
365             {
366             }
367          }
368       }
369    }
370    public Enumeration JavaDoc<URL JavaDoc> findResources(String JavaDoc name)
371    {
372       Vector JavaDoc<URL JavaDoc> resources = new Vector JavaDoc<URL JavaDoc>();
373       getResources(name, null, resources);
374       return resources.elements();
375    }
376
377    /** As opposed to classes, resource are not looked up in a global cache,
378     * since it is possible that 2 classloaders have the same resource name
379     * (ejb-jar.xml), a global cache will overwrite. Instead we look in the
380     * classloader's cache that we mantain to cycle the classloaders
381     * @param name the resource name
382     * @param cl the repository classloader
383     * @return the resource URL if found, null otherwise
384     */

385    private URL JavaDoc getResourceFromCache(String JavaDoc name, DomainClassLoader cl)
386    {
387       URL JavaDoc resource = null;
388       synchronized (loaderToResourcesMap)
389       {
390          if (loaderToResourcesMap.containsKey(cl))
391          {
392             HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.get(cl);
393             resource = (URL JavaDoc) resources.get(name);
394          }
395       }
396       return resource;
397    }
398
399    private URL JavaDoc getResourceFromClassLoader(String JavaDoc name, DomainClassLoader cl)
400    {
401       URL JavaDoc resource = null;
402       if (cl instanceof DomainClassLoader)
403       {
404          DomainClassLoader ucl = (DomainClassLoader) cl;
405          resource = ucl.loadResourceLocally(name);
406          cacheLoadedResource(name, resource, cl);
407       }
408       return resource;
409    }
410
411    /** Check for a resource in the global cache
412     * Synchronizes access to globalResources using the loaderToResourcesMap monitor
413     * @param name
414     * @return
415     */

416    protected URL JavaDoc getResourceFromGlobalCache(String JavaDoc name)
417    {
418       ResourceInfo ri = null;
419       synchronized (loaderToResourcesMap)
420       {
421          ri = (ResourceInfo) globalResources.get(name);
422       }
423       URL JavaDoc resource = null;
424       if (ri != null)
425          resource = ri.url;
426       return resource;
427    }
428
429    protected URL JavaDoc getResourceFromRepository(String JavaDoc name, DomainClassLoader cl)
430    {
431       // Get the set of class loaders from the packages map
432
String JavaDoc pkgName = getResourcePackageName(name);
433       Iterator JavaDoc i = null;
434       Set JavaDoc pkgSet = (Set JavaDoc) this.packagesMap.get(pkgName);
435       if (pkgSet != null)
436       {
437          i = pkgSet.iterator();
438       }
439       if (i == null)
440       {
441          // If no pkg match was found just go through all class loaders
442
i = classLoaders.iterator();
443       }
444
445       URL JavaDoc url = null;
446       while (i.hasNext() == true)
447       {
448          DomainClassLoader classloader = (DomainClassLoader) i.next();
449          if (classloader.equals(cl))
450          {
451             continue;
452          }
453
454          if (classloader instanceof DomainClassLoader)
455          {
456             url = ((DomainClassLoader) classloader).loadResourceLocally(name);
457             if (url != null)
458             {
459                cacheLoadedResource(name, url, classloader);
460                cacheGlobalResource(name, url, classloader);
461                break;
462             }
463             else
464             {
465                // Do nothing, go on with next classloader
466
}
467          }
468       }
469       return url;
470    }
471
472    /** Update the loaderToResourcesMap
473     * @param name the resource name
474     * @param url the resource URL
475     * @param cl the UCL
476     */

477    private void cacheLoadedResource(String JavaDoc name, URL JavaDoc url, DomainClassLoader cl)
478    {
479       // Update the cache for this classloader only
480
// This is used for cycling classloaders
481
synchronized (loaderToResourcesMap)
482       {
483          HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.get(cl);
484          if (resources == null)
485          {
486             resources = new HashMap JavaDoc();
487             loaderToResourcesMap.put(cl, resources);
488          }
489          resources.put(name, url);
490       }
491    }
492
493    /** Update cache of resources looked up via one UCL, buf found in another UCL
494     * @param name the resource name
495     * @param url the resource URL
496     * @param cl the UCL
497     */

498    private void cacheGlobalResource(String JavaDoc name, URL JavaDoc url, DomainClassLoader cl)
499    {
500       synchronized (loaderToResourcesMap)
501       {
502          globalResources.put(name, new ResourceInfo(url, cl));
503       }
504    }
505
506    /** This is a utility method a listing of the URL for all UnifiedClassLoaders
507     * associated with the repository. It is never called in response to
508     * class or resource loading.
509     */

510    public URL JavaDoc[] getURLs()
511    {
512       HashSet JavaDoc classpath = new HashSet JavaDoc();
513       Set JavaDoc tmp = classLoaders;
514       for (Iterator JavaDoc iter = tmp.iterator(); iter.hasNext();)
515       {
516          Object JavaDoc obj = iter.next();
517          if (obj instanceof DomainClassLoader)
518          {
519             DomainClassLoader cl = (DomainClassLoader) obj;
520             URL JavaDoc[] urls = cl.getClasspath();
521             int length = urls != null ? urls.length : 0;
522             for (int u = 0; u < length; u++)
523             {
524                URL JavaDoc path = urls[u];
525                classpath.add(path);
526             }
527          }
528       } // for all ClassLoaders
529

530       URL JavaDoc[] cp = new URL JavaDoc[classpath.size()];
531       classpath.toArray(cp);
532       return cp;
533    }
534
535    /** A utility method that iterates over all repository class loaders and
536     * display the class information for every UCL that contains the given
537     * className
538     */

539    public String JavaDoc displayClassInfo(String JavaDoc className)
540    {
541       /* We have to find the class as a resource as we don't want to invoke
542       loadClass(name) and cause the side-effect of loading new classes.
543       */

544       String JavaDoc classRsrcName = className.replace('.', '/') + ".class";
545
546       int count = 0;
547       Class JavaDoc loadedClass = this.loadClassFromCache(className);
548       StringBuffer JavaDoc results = new StringBuffer JavaDoc(className + " Information\n");
549       if (loadedClass != null)
550       {
551          results.append("Repository cache version:");
552          Classes.displayClassInfo(loadedClass, results);
553       }
554       else
555       {
556          results.append("Not loaded in repository cache\n");
557       }
558       Set JavaDoc tmp = classLoaders;
559       for (Iterator JavaDoc iter = tmp.iterator(); iter.hasNext();)
560       {
561          URLClassLoader JavaDoc cl = (URLClassLoader JavaDoc) iter.next();
562          URL JavaDoc classURL = cl.findResource(classRsrcName);
563          if (classURL != null)
564          {
565             results.append("\n\n### Instance" + count + " found in UCL: " + cl + "\n");
566             count++;
567          }
568       }
569
570       // Also look to the parent class loaders of the TCL
571
ClassLoader JavaDoc tcl = Thread.currentThread().getContextClassLoader();
572       URLClassLoader JavaDoc[] stack = ClassLoaderUtils.getClassLoaderStack(tcl);
573       for (int s = 0; s < stack.length; s++)
574       {
575          URLClassLoader JavaDoc cl = stack[s];
576          URL JavaDoc classURL = cl.findResource(classRsrcName);
577          if (classURL != null)
578          {
579             results.append("\n\n### Instance" + count + " via UCL: " + cl + "\n");
580             count++;
581          }
582       }
583
584       return results.toString();
585    }
586
587    // LoaderRepository overrides ------------------------------------
588

589    /** First tries to load from any UCL in the ULR, and if the
590     * class is not found, next tries the current thread context
591     * class loader.
592     * @param className - the class to load
593     */

594    public Class JavaDoc loadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc
595    {
596       // Try to load from a UCL in the ULR first
597
ClassLoader JavaDoc scl = Thread.currentThread().getContextClassLoader();
598       DomainClassLoader ucl = null;
599       if (classLoaders.size() > 0)
600          ucl = (DomainClassLoader) this.classLoaders.iterator().next();
601       try
602       {
603          if (ucl != null)
604             return loadClass(className, false, ucl);
605       }
606       catch (ClassNotFoundException JavaDoc ignore)
607       {
608          // go on and try the next loader
609
}
610
611       try
612       {
613          // If there is no class try the TCL
614
return scl.loadClass(className);
615       }
616       catch (ClassNotFoundException JavaDoc e)
617       {
618          // go on and try the next loader
619
}
620
621       // at last try a native
622
Class JavaDoc clazz = getNativeClassForName(className);
623       if (clazz != null) return clazz;
624
625       throw new ClassNotFoundException JavaDoc(className);
626    }
627
628    /**
629     * Loads a class from the repository, excluding the given
630     * classloader.
631     *
632     * @param loader the classloader to exclude
633     * @param className the class to load
634     * @return the found class
635     * @exception ClassNotFoundException when there is no such class
636     */

637    public Class JavaDoc loadClassWithout(DomainClassLoader loader, String JavaDoc className)
638            throws ClassNotFoundException JavaDoc
639    {
640       throw new ClassNotFoundException JavaDoc("NYI");
641    }
642
643    /**
644     * Get any wrapping classloader for the passed classloader
645     *
646     * @param cl the wrapped classloader
647     * @return the wrapping classloader or null if not wrapped
648     */

649    public DomainClassLoader getWrappingClassLoader(DomainClassLoader cl)
650    {
651       synchronized (classLoaders)
652       {
653          return (DomainClassLoader) nonUCLClassLoader.get(cl);
654       }
655    }
656    
657    /** Add a class loader to the repository.
658     */

659    public void addClassLoader(DomainClassLoader loader)
660    {
661       // if you come to us as UCL we send you straight to the orbit
662
addDomainClassLoader((DomainClassLoaderUCLImpl) loader);
663    }
664
665    public boolean addClassLoaderURL(DomainClassLoader cl, URL JavaDoc url)
666    {
667       DomainClassLoader ucl = (DomainClassLoader) cl;
668       boolean added = false;
669       synchronized (classLoaders)
670       {
671          // Strip any query parameter
672
String JavaDoc query = url.getQuery();
673          if (query != null)
674          {
675             String JavaDoc ext = url.toExternalForm();
676             String JavaDoc ext2 = ext.substring(0, ext.length() - query.length() - 1);
677             try
678             {
679                url = new URL JavaDoc(ext2);
680             }
681             catch (MalformedURLException JavaDoc e)
682             {
683                log.warn("Failed to strip query from: " + url, e);
684             }
685          }
686
687          // See if the URL is associated with a class loader
688
if (classLoaderURLs.contains(url) == false)
689          {
690             updatePackageMap(ucl);
691             classLoaderURLs.add(url);
692             added = true;
693             // Check for a dynamic URL
694
if (query != null && query.indexOf("dynamic=true") >= 0)
695                dynamicClassLoaders.add(ucl);
696          }
697       }
698       return added;
699    }
700    
701    /** Add a UCL to the repository.
702     * This sychronizes on classLoaders.
703     * @param cl
704     */

705    private void addDomainClassLoader(DomainClassLoaderUCLImpl cl)
706    {
707       cl.setDomain(this);
708       boolean added = false;
709       synchronized (classLoaders)
710       {
711          boolean exists = false;
712          URL JavaDoc[] cp = cl.getClasspath();
713          classLoaderURLs.addAll(Arrays.asList(cp));
714          added = classLoaders.add(cl);
715          if (added)
716          {
717             log.debug("Adding " + cl);
718             addedCount++;
719             cl.setAddedOrder(addedCount);
720             updatePackageMap(cl);
721          }
722          else
723          {
724             log.debug("Skipping duplicate " + cl);
725          }
726       }
727    }
728
729    /** Walk through the class loader URL to see what packages it is capable
730     of handling
731     */

732    private synchronized void updatePackageMap(DomainClassLoader cl)
733    {
734       boolean trace = log.isTraceEnabled();
735       try
736       {
737          PackageMapper listener = new PackageMapper(cl);
738          URL JavaDoc[] cp = cl.getClasspath();
739          for(URL JavaDoc url : cp)
740          {
741             ClassLoaderUtils.updatePackageMap(url, listener);
742          }
743       }
744       catch (Exception JavaDoc e)
745       {
746          if (log.isTraceEnabled())
747             log.trace("Failed to update pkgs for cl=" + cl, e);
748          else
749             log.debug("Failed to update pkgs for cl=" + cl, e);
750       }
751    }
752
753    /** Remove the class loader from the repository. This synchronizes on the
754     * this.classLoaders
755     */

756    public void removeClassLoader(DomainClassLoader loader)
757    {
758       ArrayList JavaDoc removeNotifications = new ArrayList JavaDoc();
759       DomainClassLoader cl = loader;
760       synchronized (classLoaders)
761       {
762          if ((loader instanceof DomainClassLoader) == false)
763          {
764             cl = (DomainClassLoader) nonUCLClassLoader.remove(loader);
765          }
766          if (cl instanceof DomainClassLoader)
767          {
768             DomainClassLoader ucl = (DomainClassLoader) cl;
769             if (getTranslator() != null)
770                getTranslator().unregisterClassLoader(ucl);
771             URL JavaDoc[] urls = ucl.getClasspath();
772             for (int u = 0; u < urls.length; u++)
773                classLoaderURLs.remove(urls[u]);
774          }
775          boolean dynamic = dynamicClassLoaders.remove(cl);
776          boolean removed = classLoaders.remove(cl);
777          log.debug("UnifiedLoaderRepository removed(" + removed + ") " + cl);
778
779          // Take care also of the cycling mapping for classes
780
HashSet JavaDoc loadedClasses = null;
781          boolean hasLoadedClasses = false;
782          synchronized (classes)
783          {
784             hasLoadedClasses = loaderToClassesMap.containsKey(cl);
785             if (hasLoadedClasses)
786                loadedClasses = (HashSet JavaDoc) loaderToClassesMap.remove(cl);
787             // This classloader has loaded at least one class
788
if (loadedClasses != null)
789             {
790                // Notify that classes are about to be removed
791
for (Iterator JavaDoc iter = loadedClasses.iterator(); iter.hasNext();)
792                {
793                   String JavaDoc className = (String JavaDoc) iter.next();
794                   Notification JavaDoc n = new Notification JavaDoc(CLASS_REMOVED, this,
795                           getNextSequenceNumber(), className);
796                   removeNotifications.add(n);
797                }
798
799                // Remove the classes from the global cache
800
for (Iterator JavaDoc i = loadedClasses.iterator(); i.hasNext();)
801                {
802                   String JavaDoc cls = (String JavaDoc) i.next();
803                   this.classes.remove(cls);
804                }
805             }
806          }
807
808          // Take care also of the cycling mapping for resources
809
synchronized (loaderToResourcesMap)
810          {
811             if (loaderToResourcesMap.containsKey(cl))
812             {
813                HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.remove(cl);
814
815                // Remove the resources from the global cache that are from this classloader
816
if (resources != null)
817                {
818                   for (Iterator JavaDoc i = resources.keySet().iterator(); i.hasNext();)
819                   {
820                      String JavaDoc name = (String JavaDoc) i.next();
821                      ResourceInfo ri = (ResourceInfo) globalResources.get(name);
822                      if (ri != null && ri.cl == cl)
823                         globalResources.remove(name);
824                   }
825                }
826             }
827          }
828
829          // Clean up the package name to class loader mapping
830
if (dynamic == false)
831          {
832             List JavaDoc<String JavaDoc> pkgNames = loaderToPackagesMap.remove(cl);
833             if( pkgNames != null )
834             {
835                for(String JavaDoc pkgName : pkgNames)
836                {
837                   Set JavaDoc pkgSet = (Set JavaDoc) packagesMap.get(pkgName);
838                   if (pkgSet != null)
839                   {
840                      pkgSet.remove(cl);
841                      if (pkgSet.isEmpty())
842                         packagesMap.remove(pkgName);
843                   }
844                }
845             }
846          }
847          else
848          {
849             // A dynamic classloader could end up in any package set
850
loaderToPackagesMap.remove(cl);
851             for (Iterator JavaDoc i = packagesMap.entrySet().iterator(); i.hasNext();)
852             {
853                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
854                Set JavaDoc pkgSet = (Set JavaDoc) entry.getValue();
855                pkgSet.remove(cl);
856                if (pkgSet.isEmpty())
857                   i.remove();
858             }
859          }
860       }
861
862       // Send the class removal notfications outside of the synchronized block
863
for (int n = 0; n < removeNotifications.size(); n++)
864       {
865          Notification JavaDoc msg = (Notification JavaDoc) removeNotifications.get(n);
866          broadcaster.sendNotification(msg);
867       }
868
869       Notification JavaDoc msg = new Notification JavaDoc(CLASSLOADER_REMOVED, this, getNextSequenceNumber());
870       msg.setUserData(cl);
871       broadcaster.sendNotification(msg);
872    }
873
874    /**
875     * This method provides an mbean-accessible way to add a
876     * UnifiedClassloader, and sends a notification when it is added.
877     *
878     * @param ucl an <code>UnifiedClassLoader</code> value
879     * @return a <code>LoaderRepository</code> value
880     *
881     * @jmx.managed-operation
882     */

883    public LoaderRepositoryDomain registerClassLoader(DomainClassLoader ucl)
884    {
885       addClassLoader(ucl);
886       Notification JavaDoc msg = new Notification JavaDoc(CLASSLOADER_ADDED, this, getNextSequenceNumber());
887       msg.setUserData(ucl);
888       broadcaster.sendNotification(msg);
889
890       return this;
891    }
892
893    /**
894     * @jmx.managed-operation
895     */

896    public LoaderRepositoryDomain getInstance()
897    {
898       return this;
899    }
900
901    // implementation of javax.management.NotificationBroadcaster interface
902

903    /**
904     * addNotificationListener delegates to the broadcaster object we hold.
905     *
906     * @param listener a <code>NotificationListener</code> value
907     * @param filter a <code>NotificationFilter</code> value
908     * @param handback an <code>Object</code> value
909     * @exception IllegalArgumentException if an error occurs
910     */

911    public void addNotificationListener(NotificationListener JavaDoc listener,
912                                        NotificationFilter JavaDoc filter, Object JavaDoc handback) throws IllegalArgumentException JavaDoc
913    {
914       broadcaster.addNotificationListener(listener, filter, handback);
915    }
916
917    /**
918     *
919     * @return <description>
920     */

921    public MBeanNotificationInfo JavaDoc[] getNotificationInfo()
922    {
923       if (info == null)
924       {
925          info = new MBeanNotificationInfo JavaDoc[]{
926             new MBeanNotificationInfo JavaDoc(new String JavaDoc[]{"CLASSLOADER_ADDED"},
927                     "javax.management.Notification",
928                     "Notification that a classloader has been added to the extensible classloader"),
929             new MBeanNotificationInfo JavaDoc(new String JavaDoc[]{"CLASS_REMOVED"},
930                     "javax.management.Notification",
931                     "Notification that a class has been removed from the extensible classloader")
932
933          };
934       }
935       return info;
936    }
937
938    /**
939     * removeNotificationListener delegates to our broadcaster object
940     *
941     * @param listener a <code>NotificationListener</code> value
942     * @exception ListenerNotFoundException if an error occurs
943     */

944    public void removeNotificationListener(NotificationListener JavaDoc listener) throws ListenerNotFoundException JavaDoc
945    {
946       broadcaster.removeNotificationListener(listener);
947    }
948
949    // MBeanRegistration
950
public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server, ObjectName JavaDoc name)
951       throws Exception JavaDoc
952    {
953       return name;
954    }
955
956    public void postRegister(Boolean JavaDoc registrationDone)
957    {
958    }
959
960    public void preDeregister()
961       throws Exception JavaDoc
962    {
963    }
964
965    public void postDeregister()
966    {
967       log.debug("postDeregister, clearing all references");
968       classLoaders.clear();
969       dynamicClassLoaders.clear();
970       nonUCLClassLoader.clear();
971       classLoaderURLs.clear();
972       classes.clear();
973       loaderToClassesMap.clear();
974       loaderToResourcesMap.clear();
975       globalResources.clear();
976       packagesMap.clear();
977       loaderToPackagesMap.clear();
978    }
979
980    /**
981     * lookup the package names for a given class loader
982     * @param loader
983     * @return
984     */

985    String JavaDoc[] getPackageNames(DomainClassLoaderUCLImpl loader)
986    {
987       List JavaDoc<String JavaDoc> pkgNames = loaderToPackagesMap.get(loader);
988       String JavaDoc[] tmp = {};
989       if( pkgNames != null )
990       {
991          tmp = new String JavaDoc[pkgNames.size()];
992          pkgNames.toArray(tmp);
993       }
994       return tmp;
995    }
996
997    private synchronized long getNextSequenceNumber()
998    {
999       return sequenceNumber++;
1000   }
1001
1002   /**
1003    * A comparator for comparing repository classloaders
1004    */

1005   private static class DomainClassLoaderUCLImplComparator implements Comparator JavaDoc
1006   {
1007      /**
1008       * Compares two repository classloaders, they are ordered by:
1009       * 1) parent->child delegation rules in the loader repository
1010       * 2) added order inside the loader repository
1011       */

1012      public int compare(Object JavaDoc o1, Object JavaDoc o2)
1013      {
1014         if (o1 instanceof PkgClassLoader)
1015         {
1016            PkgClassLoader pkg1 = (PkgClassLoader) o1;
1017            PkgClassLoader pkg2 = (PkgClassLoader) o2;
1018            RepositoryClassLoader rcl1 = pkg1.ucl;
1019            RepositoryClassLoader rcl2 = pkg2.ucl;
1020            // We use the package classloader ordering before the repository order
1021
int test = (pkg1.order - pkg2.order);
1022            if (test != 0)
1023               return test;
1024            else
1025               return rcl1.getAddedOrder() - rcl2.getAddedOrder();
1026         }
1027         else
1028         {
1029            DomainClassLoaderUCLImpl rcl1 = (DomainClassLoaderUCLImpl) o1;
1030            DomainClassLoaderUCLImpl rcl2 = (DomainClassLoaderUCLImpl) o2;
1031            return rcl1.getAddedOrder() - rcl2.getAddedOrder();
1032            
1033            // REVIEW: Alternative to using the pkgClassLoader is
1034
// ordering based on the loader repository
1035

1036            //LoaderRepository lr1 = rcl1.getLoaderRepository();
1037
//LoaderRepository lr2 = rcl2.getLoaderRepository();
1038

1039            // Are the loader repositories ordered?
1040
//int test = lr1.compare(lr2);
1041
//if (test != 0)
1042
// return test;
1043
//else
1044
// return rcl1.getAddedOrder() - rcl2.getAddedOrder();
1045
}
1046      }
1047   }
1048
1049   class PackageMapper implements ClassLoaderUtils.PkgNameListener
1050   {
1051      private DomainClassLoader loader;
1052      PackageMapper(DomainClassLoader loader)
1053      {
1054         this.loader = loader;
1055      }
1056      public void addPackage(String JavaDoc pkgName)
1057      {
1058         // Skip the standard J2EE archive directories
1059
if( pkgName.startsWith("META-INF") || pkgName.startsWith("WEB-INF") )
1060            return;
1061
1062         Set JavaDoc<DomainClassLoader> pkgSet = (Set JavaDoc<DomainClassLoader>) packagesMap.get(pkgName);
1063         if( pkgSet == null )
1064         {
1065            pkgSet = new TreeSet JavaDoc<DomainClassLoader>(new DomainClassLoaderUCLImplComparator());
1066            packagesMap.put(pkgName, pkgSet);
1067         }
1068         if( pkgSet.contains(loader) == false )
1069         {
1070            pkgSet.add((DomainClassLoader)loader);
1071            List JavaDoc<String JavaDoc> loaderPkgNames = loaderToPackagesMap.get(loader);
1072            if( loaderPkgNames == null )
1073            {
1074               loaderPkgNames = new ArrayList JavaDoc<String JavaDoc>();
1075               loaderToPackagesMap.put((DomainClassLoader)loader, loaderPkgNames);
1076            }
1077            loaderPkgNames.add(pkgName);
1078
1079            // Anytime more than one class loader exists this may indicate a problem
1080
if( pkgSet.size() > 1 )
1081            {
1082               log.debug("Multiple class loaders found for pkg: "+pkgName);
1083            }
1084            if( log.isTraceEnabled() )
1085               log.trace("Indexed pkg: "+pkgName+", UCL: "+loader);
1086         }
1087      }
1088   }
1089}
1090
Popular Tags