KickJava   Java API By Example, From Geeks To Geeks.

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


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

68 public class UnifiedLoaderRepository3 extends LoaderRepository
69    implements MBeanRegistration JavaDoc, NotificationBroadcaster JavaDoc,
70       UnifiedLoaderRepository3MBean
71 {
72    // Static --------------------------------------------------------
73
private static final Logger log = Logger.getLogger(UnifiedLoaderRepository3.class);
74    /** Used to provide a relative ordering of UCLs based on the order in
75     * which they are added to the repository */

76    private static int addedCount;
77
78    // Attributes ----------------------------------------------------
79

80    /** HashSet<UCL> of classloaders in the repository.
81     * Access synchronized via this.classLoaders monitor.
82     */

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

88    private HashSet JavaDoc dynamicClassLoaders = new HashSet JavaDoc();
89    /** A HashMap<ClassLoader, UCL> of foreign (non-UCL) classloaders that
90     * have been added to the repository as the key and the value the UCL
91     * actually used by the ULR.
92     * Access synchronized via this.classLoaders monitor.
93     */

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

101    private HashSet JavaDoc classLoaderURLs = new HashSet JavaDoc();
102
103    /** The loaded classes cache, HashMap<String, Class>.
104     * Access synchronized via this.classes monitor.
105     */

106    private ConcurrentReaderHashMap classes = new ConcurrentReaderHashMap();
107
108    /** HashMap<UCL, HashSet<String>> class loaders to the set of class names
109     * loaded via the UCL.
110     * Access synchronized via this.classes monitor.
111     */

112    private HashMap JavaDoc loaderToClassesMap = new HashMap JavaDoc();
113
114    /** HashMap<UCL, HashMap<String, URL>> class loaders to the set of
115     * resource names they looked up.
116     * Access synchronized via this.loaderToResourcesMap monitor.
117     */

118    private HashMap JavaDoc loaderToResourcesMap = new HashMap JavaDoc();
119
120    /** HashMap<String, ResourceInfo(URL, UCL)> of global resources not unique
121     * to a UCL
122     * Access synchronized via this.loaderToResourcesMap monitor.
123     */

124    private HashMap JavaDoc globalResources = new HashMap JavaDoc();
125
126    /** A HashMap<String, Set<UCL>> of package names to the set of
127     * ClassLoaders which have classes in the package.
128     * Access synchronized via this.packagesMap monitor.
129     */

130    private ConcurrentReaderHashMap packagesMap = new ConcurrentReaderHashMap();
131
132    /** A HashMap<UCL, String[]> of class loaders to the array of pckages names
133     * they serve
134     * Access synchronized via this.packagesMap monitor.
135     */

136    private HashMap JavaDoc<RepositoryClassLoader, List JavaDoc<String JavaDoc>> loaderToPackagesMap = new HashMap JavaDoc<RepositoryClassLoader, List JavaDoc<String JavaDoc>>();
137
138    /**
139     * The sequenceNumber used to number notifications.
140     */

141    private long sequenceNumber = 0;
142
143    /**
144     * We delegate our notification sending to a support object.
145     */

146    private final JBossNotificationBroadcasterSupport broadcaster = new JBossNotificationBroadcasterSupport();
147
148    /**
149     * The NotificationInfo we emit.
150     */

151    private MBeanNotificationInfo JavaDoc[] info;
152
153
154    // Public --------------------------------------------------------
155

156    public RepositoryClassLoader newClassLoader(final URL JavaDoc url, boolean addToRepository)
157            throws Exception JavaDoc
158    {
159       UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, null, this);
160       if (addToRepository)
161          this.registerClassLoader(ucl);
162       return ucl;
163    }
164
165    public RepositoryClassLoader newClassLoader(final URL JavaDoc url, final URL JavaDoc origURL, boolean addToRepository)
166            throws Exception JavaDoc
167    {
168       UnifiedClassLoader3 ucl = new UnifiedClassLoader3(url, origURL, this);
169       if (addToRepository)
170          this.registerClassLoader(ucl);
171       return ucl;
172    }
173
174    public int getCacheSize()
175    {
176       return classes.size();
177    }
178
179    public int getClassLoadersSize()
180    {
181       return classLoaders.size();
182    }
183
184    public void flush()
185    {
186       synchronized (classes)
187       {
188          classes.clear();
189       }
190    }
191
192    public Class JavaDoc getCachedClass(String JavaDoc classname)
193    {
194       return (Class JavaDoc) classes.get(classname);
195    }
196
197    /** Unlike other implementations of LoaderRepository, this method does
198     * nothing but ask the UnifiedClassLoader3 to load the class as UCL3s
199     * do not use this method.
200     */

201    public Class JavaDoc loadClass(String JavaDoc name, boolean resolve, ClassLoader JavaDoc cl) throws ClassNotFoundException JavaDoc
202    {
203       RepositoryClassLoader rcl = getRepositoryClassLoader(cl, name);
204       return rcl.loadClass(name, resolve);
205    }
206
207    /** Called by LoadMgr to obtain all class loaders for the given className
208     * @return Set<UnifiedClassLoader3>, may be null
209     */

210    public Set JavaDoc getPackageClassLoaders(String JavaDoc className)
211    {
212       String JavaDoc pkgName = ClassLoaderUtils.getPackageName(className);
213       Set JavaDoc pkgSet = (Set JavaDoc) packagesMap.get(pkgName);
214       if (dynamicClassLoaders.size() > 0)
215       {
216          if (pkgSet == null)
217             pkgSet = ClassLoaderUtils.newPackageSet();
218          pkgSet.addAll(dynamicClassLoaders);
219       }
220       return pkgSet;
221    }
222
223    private String JavaDoc getResourcePackageName(String JavaDoc rsrcName)
224    {
225       int index = rsrcName.lastIndexOf('/');
226       String JavaDoc pkgName = rsrcName;
227       if (index > 0)
228          pkgName = rsrcName.substring(0, index);
229       return pkgName.replace('/', '.');
230    }
231
232    /** Lookup a Class from the repository cache.
233     * @param name the fully qualified class name
234     * @return the cached Class if found, null otherwise
235     */

236    public Class JavaDoc loadClassFromCache(String JavaDoc name)
237    {
238       Class JavaDoc cls = null;
239       synchronized (classes)
240       {
241          cls = (Class JavaDoc) classes.get(name);
242       }
243       return cls;
244    }
245
246    /** Add a Class to the repository cache.
247     * @param name the fully qualified class name
248     * @param cls the Class instance
249     * @param cl the repository UCL
250     */

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

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

335    public void getResources(String JavaDoc name, ClassLoader JavaDoc cl, List JavaDoc urls)
336    {
337       // Go through all class loaders
338
Iterator JavaDoc iter = classLoaders.iterator();
339       while (iter.hasNext() == true)
340       {
341          ClassLoader JavaDoc nextCL = (ClassLoader JavaDoc) iter.next();
342          if (nextCL instanceof RepositoryClassLoader)
343          {
344             RepositoryClassLoader ucl = (RepositoryClassLoader) nextCL;
345             try
346             {
347                Enumeration JavaDoc resURLs = ucl.findResourcesLocally(name);
348                while (resURLs.hasMoreElements())
349                {
350                   Object JavaDoc res = resURLs.nextElement();
351                   urls.add(res);
352                }
353             }
354             catch (IOException JavaDoc ignore)
355             {
356             }
357          }
358       }
359    }
360
361    /** As opposed to classes, resource are not looked up in a global cache,
362     * since it is possible that 2 classloaders have the same resource name
363     * (ejb-jar.xml), a global cache will overwrite. Instead we look in the
364     * classloader's cache that we mantain to cycle the classloaders
365     * @param name the resource name
366     * @param cl the repository classloader
367     * @return the resource URL if found, null otherwise
368     */

369    private URL JavaDoc getResourceFromCache(String JavaDoc name, ClassLoader JavaDoc cl)
370    {
371       URL JavaDoc resource = null;
372       synchronized (loaderToResourcesMap)
373       {
374          if (loaderToResourcesMap.containsKey(cl))
375          {
376             HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.get(cl);
377             resource = (URL JavaDoc) resources.get(name);
378          }
379       }
380       return resource;
381    }
382
383    private URL JavaDoc getResourceFromClassLoader(String JavaDoc name, ClassLoader JavaDoc cl)
384    {
385       URL JavaDoc resource = null;
386       if (cl instanceof RepositoryClassLoader)
387       {
388          RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
389          resource = ucl.getResourceLocally(name);
390          cacheLoadedResource(name, resource, cl);
391       }
392       return resource;
393    }
394
395    /** Check for a resource in the global cache
396     * Synchronizes access to globalResources using the loaderToResourcesMap monitor
397     * @param name
398     * @return
399     */

400    protected URL JavaDoc getResourceFromGlobalCache(String JavaDoc name)
401    {
402       ResourceInfo ri = null;
403       synchronized (loaderToResourcesMap)
404       {
405          ri = (ResourceInfo) globalResources.get(name);
406       }
407       URL JavaDoc resource = null;
408       if (ri != null)
409          resource = ri.url;
410       return resource;
411    }
412
413    protected URL JavaDoc getResourceFromRepository(String JavaDoc name, ClassLoader JavaDoc cl)
414    {
415       // Get the set of class loaders from the packages map
416
String JavaDoc pkgName = getResourcePackageName(name);
417       Iterator JavaDoc i = null;
418       Set JavaDoc pkgSet = (Set JavaDoc) this.packagesMap.get(pkgName);
419       if (pkgSet != null)
420       {
421          i = pkgSet.iterator();
422       }
423       if (i == null)
424       {
425          // If no pkg match was found just go through all class loaders
426
i = classLoaders.iterator();
427       }
428
429       URL JavaDoc url = null;
430       while (i.hasNext() == true)
431       {
432          ClassLoader JavaDoc classloader = (ClassLoader JavaDoc) i.next();
433          if (classloader.equals(cl))
434          {
435             continue;
436          }
437
438          if (classloader instanceof RepositoryClassLoader)
439          {
440             url = ((RepositoryClassLoader) classloader).getResourceLocally(name);
441             if (url != null)
442             {
443                cacheLoadedResource(name, url, classloader);
444                cacheGlobalResource(name, url, classloader);
445                break;
446             }
447             else
448             {
449                // Do nothing, go on with next classloader
450
}
451          }
452       }
453       return url;
454    }
455
456    /** Update the loaderToResourcesMap
457     * @param name the resource name
458     * @param url the resource URL
459     * @param cl the UCL
460     */

461    private void cacheLoadedResource(String JavaDoc name, URL JavaDoc url, ClassLoader JavaDoc cl)
462    {
463       // Update the cache for this classloader only
464
// This is used for cycling classloaders
465
synchronized (loaderToResourcesMap)
466       {
467          HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.get(cl);
468          if (resources == null)
469          {
470             resources = new HashMap JavaDoc();
471             loaderToResourcesMap.put(cl, resources);
472          }
473          resources.put(name, url);
474       }
475    }
476
477    /** Update cache of resources looked up via one UCL, buf found in another UCL
478     * @param name the resource name
479     * @param url the resource URL
480     * @param cl the UCL
481     */

482    private void cacheGlobalResource(String JavaDoc name, URL JavaDoc url, ClassLoader JavaDoc cl)
483    {
484       synchronized (loaderToResourcesMap)
485       {
486          globalResources.put(name, new ResourceInfo(url, cl));
487       }
488    }
489
490    /** This is a utility method a listing of the URL for all UnifiedClassLoaders
491     * associated with the repository. It is never called in response to
492     * class or resource loading.
493     */

494    public URL JavaDoc[] getURLs()
495    {
496       HashSet JavaDoc classpath = new HashSet JavaDoc();
497       Set JavaDoc tmp = classLoaders;
498       for (Iterator JavaDoc iter = tmp.iterator(); iter.hasNext();)
499       {
500          Object JavaDoc obj = iter.next();
501          if (obj instanceof RepositoryClassLoader)
502          {
503             RepositoryClassLoader cl = (RepositoryClassLoader) obj;
504             URL JavaDoc[] urls = cl.getClasspath();
505             int length = urls != null ? urls.length : 0;
506             for (int u = 0; u < length; u++)
507             {
508                URL JavaDoc path = urls[u];
509                classpath.add(path);
510             }
511          }
512       } // for all ClassLoaders
513

514       URL JavaDoc[] cp = new URL JavaDoc[classpath.size()];
515       classpath.toArray(cp);
516       return cp;
517    }
518
519    /** A utility method that iterates over all repository class loaders and
520     * display the class information for every UCL that contains the given
521     * className
522     */

523    public String JavaDoc displayClassInfo(String JavaDoc className)
524    {
525       /* We have to find the class as a resource as we don't want to invoke
526       loadClass(name) and cause the side-effect of loading new classes.
527       */

528       String JavaDoc classRsrcName = className.replace('.', '/') + ".class";
529
530       int count = 0;
531       Class JavaDoc loadedClass = this.loadClassFromCache(className);
532       StringBuffer JavaDoc results = new StringBuffer JavaDoc(className + " Information\n");
533       if (loadedClass != null)
534       {
535          results.append("Repository cache version:");
536          Classes.displayClassInfo(loadedClass, results);
537       }
538       else
539       {
540          results.append("Not loaded in repository cache\n");
541       }
542       Set JavaDoc tmp = classLoaders;
543       for (Iterator JavaDoc iter = tmp.iterator(); iter.hasNext();)
544       {
545          URLClassLoader JavaDoc cl = (URLClassLoader JavaDoc) iter.next();
546          URL JavaDoc classURL = cl.findResource(classRsrcName);
547          if (classURL != null)
548          {
549             results.append("\n\n### Instance" + count + " found in UCL: " + cl + "\n");
550             count++;
551          }
552       }
553
554       // Also look to the parent class loaders of the TCL
555
ClassLoader JavaDoc tcl = Thread.currentThread().getContextClassLoader();
556       URLClassLoader JavaDoc[] stack = ClassLoaderUtils.getClassLoaderStack(tcl);
557       for (int s = 0; s < stack.length; s++)
558       {
559          URLClassLoader JavaDoc cl = stack[s];
560          URL JavaDoc classURL = cl.findResource(classRsrcName);
561          if (classURL != null)
562          {
563             results.append("\n\n### Instance" + count + " via UCL: " + cl + "\n");
564             count++;
565          }
566       }
567
568       return results.toString();
569    }
570
571    // LoaderRepository overrides ------------------------------------
572

573    /** First tries to load from any UCL in the ULR, and if the
574     * class is not found, next tries the current thread context
575     * class loader.
576     * @param className - the class to load
577     */

578    public Class JavaDoc loadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc
579    {
580       // Try to load from a UCL in the ULR first
581
ClassLoader JavaDoc scl = Thread.currentThread().getContextClassLoader();
582       ClassLoader JavaDoc ucl = null;
583       if (classLoaders.size() > 0)
584          ucl = (ClassLoader JavaDoc) this.classLoaders.iterator().next();
585       try
586       {
587          if (ucl != null)
588             return loadClass(className, false, ucl);
589       }
590       catch (ClassNotFoundException JavaDoc ignore)
591       {
592          // go on and try the next loader
593
}
594
595       try
596       {
597          // If there is no class try the TCL
598
return scl.loadClass(className);
599       }
600       catch (ClassNotFoundException JavaDoc e)
601       {
602          // go on and try the next loader
603
}
604
605       // at last try a native
606
Class JavaDoc clazz = getNativeClassForName(className);
607       if (clazz != null) return clazz;
608
609       throw new ClassNotFoundException JavaDoc(className);
610    }
611
612    /**
613     * Loads a class from the repository, excluding the given
614     * classloader.
615     *
616     * @param loader the classloader to exclude
617     * @param className the class to load
618     * @return the found class
619     * @exception ClassNotFoundException when there is no such class
620     */

621    public Class JavaDoc loadClassWithout(ClassLoader JavaDoc loader, String JavaDoc className)
622            throws ClassNotFoundException JavaDoc
623    {
624       throw new ClassNotFoundException JavaDoc("NYI");
625    }
626
627    /**
628     * Loads a class from the repository, using the classloaders that were
629     * registered before the given classloader.
630     *
631     * @param stop consult all the classloaders registered before this one
632     * in an attempt to load a class
633     * @param className name of the class to load
634     * @return loaded class instance
635     * @throws ClassNotFoundException if none of the consulted classloaders were
636     * able to load the requested class
637     */

638    public Class JavaDoc loadClassBefore(ClassLoader JavaDoc stop, String JavaDoc className) throws ClassNotFoundException JavaDoc
639    {
640       RepositoryClassLoader stopAt = getRepositoryClassLoader(stop, className);
641       return stopAt.loadClassBefore(className);
642    }
643
644    /**
645     * Get any wrapping classloader for the passed classloader
646     *
647     * @param cl the wrapped classloader
648     * @return the wrapping classloader or null if not wrapped
649     */

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

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

719    private void addRepositoryClassLoader(RepositoryClassLoader cl)
720    {
721       cl.setRepository(this);
722       // See if this URL already exists
723
URL JavaDoc url = cl.getURL();
724       boolean added = false;
725       synchronized (classLoaders)
726       {
727          boolean exists = false;
728          if (cl instanceof UnifiedClassLoader)
729             exists = classLoaderURLs.contains(url);
730          // If already present will not be added
731
if (!exists)
732          {
733             if (url != null)
734                classLoaderURLs.add(url);
735             added = classLoaders.add(cl);
736          }
737          if (added)
738          {
739             log.debug("Adding " + cl);
740             addedCount++;
741             cl.setAddedOrder(addedCount);
742             updatePackageMap(cl);
743          }
744          else
745          {
746             log.debug("Skipping duplicate " + cl);
747          }
748       }
749    }
750
751    private void addMLetClassLoader(MLet JavaDoc loader)
752    {
753       MLetRepositoryClassLoader rcl = new MLetRepositoryClassLoader(loader);
754       synchronized (classLoaders)
755       {
756          nonUCLClassLoader.put(loader, rcl);
757       }
758       addRepositoryClassLoader(rcl);
759    }
760
761    private void addURLClassLoader(URLClassLoader JavaDoc loader)
762    {
763       URL JavaDoc[] urls = loader.getURLs();
764       int count = urls != null && urls.length > 0 ? urls.length : 0;
765       URL JavaDoc origURL = count > 0 ? urls[0] : null;
766       UnifiedClassLoader3 ucl3 = new UnifiedClassLoader3(origURL, origURL, this);
767       addRepositoryClassLoader(ucl3);
768       synchronized (classLoaders)
769       {
770          nonUCLClassLoader.put(loader, ucl3);
771       }
772       for (int i = 1; i < count; i++)
773       {
774          this.addClassLoaderURL(ucl3, urls[i]);
775       }
776    }
777    
778    /** Walk through the class loader URL to see what packages it is capable
779     of handling
780     */

781    private void updatePackageMap(RepositoryClassLoader cl)
782    {
783       try
784       {
785          URL JavaDoc url = cl.getURL();
786          PackageMapper listener = new PackageMapper(cl);
787          ClassLoaderUtils.updatePackageMap(url, listener);
788       }
789       catch (Exception JavaDoc e)
790       {
791          if (log.isTraceEnabled())
792             log.trace("Failed to update pkgs for cl=" + cl, e);
793          else
794             log.debug("Failed to update pkgs for cl=" + cl, e);
795       }
796    }
797
798    /** Walk through the new URL to update the packages the ClassLoader is
799     * capable of handling
800     */

801    private void updatePackageMap(RepositoryClassLoader cl, URL JavaDoc url)
802    {
803       try
804       {
805          PackageMapper listener = new PackageMapper(cl);
806          ClassLoaderUtils.updatePackageMap(url, listener);
807       }
808       catch (Exception JavaDoc e)
809       {
810          if (log.isTraceEnabled())
811             log.trace("Failed to update pkgs for cl=" + cl, e);
812          else
813             log.debug("Failed to update pkgs for cl=" + cl, e);
814       }
815    }
816
817    /** Remove the class loader from the repository. This synchronizes on the
818     * this.classLoaders
819     */

820    public void removeClassLoader(ClassLoader JavaDoc loader)
821    {
822       ArrayList JavaDoc removeNotifications = new ArrayList JavaDoc();
823       ClassLoader JavaDoc cl = loader;
824       synchronized (classLoaders)
825       {
826          if ((loader instanceof RepositoryClassLoader) == false)
827          {
828             cl = (ClassLoader JavaDoc) nonUCLClassLoader.remove(loader);
829          }
830          if (cl instanceof RepositoryClassLoader)
831          {
832             RepositoryClassLoader ucl = (RepositoryClassLoader) cl;
833             if (getTranslator() != null)
834                getTranslator().unregisterClassLoader(ucl);
835             URL JavaDoc[] urls = ucl.getClasspath();
836             for (int u = 0; u < urls.length; u++)
837                classLoaderURLs.remove(urls[u]);
838          }
839          boolean dynamic = dynamicClassLoaders.remove(cl);
840          boolean removed = classLoaders.remove(cl);
841          log.debug("UnifiedLoaderRepository removed(" + removed + ") " + cl);
842
843          // Take care also of the cycling mapping for classes
844
HashSet JavaDoc loadedClasses = null;
845          boolean hasLoadedClasses = false;
846          synchronized (classes)
847          {
848             hasLoadedClasses = loaderToClassesMap.containsKey(cl);
849             if (hasLoadedClasses)
850                loadedClasses = (HashSet JavaDoc) loaderToClassesMap.remove(cl);
851             // This classloader has loaded at least one class
852
if (loadedClasses != null)
853             {
854                // Notify that classes are about to be removed
855
for (Iterator JavaDoc iter = loadedClasses.iterator(); iter.hasNext();)
856                {
857                   String JavaDoc className = (String JavaDoc) iter.next();
858                   Notification JavaDoc n = new Notification JavaDoc(CLASS_REMOVED, this,
859                           getNextSequenceNumber(), className);
860                   removeNotifications.add(n);
861                }
862
863                // Remove the classes from the global cache
864
for (Iterator JavaDoc i = loadedClasses.iterator(); i.hasNext();)
865                {
866                   String JavaDoc cls = (String JavaDoc) i.next();
867                   this.classes.remove(cls);
868                }
869             }
870          }
871
872          // Take care also of the cycling mapping for resources
873
synchronized (loaderToResourcesMap)
874          {
875             if (loaderToResourcesMap.containsKey(cl))
876             {
877                HashMap JavaDoc resources = (HashMap JavaDoc) loaderToResourcesMap.remove(cl);
878
879                // Remove the resources from the global cache that are from this classloader
880
if (resources != null)
881                {
882                   for (Iterator JavaDoc i = resources.keySet().iterator(); i.hasNext();)
883                   {
884                      String JavaDoc name = (String JavaDoc) i.next();
885                      ResourceInfo ri = (ResourceInfo) globalResources.get(name);
886                      if (ri != null && ri.cl == cl)
887                         globalResources.remove(name);
888                   }
889                }
890             }
891          }
892
893          // Clean up the package name to class loader mapping
894
if (dynamic == false)
895          {
896             List JavaDoc<String JavaDoc> pkgNames = loaderToPackagesMap.remove(cl);
897             if( pkgNames != null )
898             {
899                for(String JavaDoc pkgName : pkgNames)
900                {
901                   Set JavaDoc pkgSet = (Set JavaDoc) packagesMap.get(pkgName);
902                   if (pkgSet != null)
903                   {
904                      pkgSet.remove(cl);
905                      if (pkgSet.isEmpty())
906                         packagesMap.remove(pkgName);
907                   }
908                }
909             }
910          }
911          else
912          {
913             // A dynamic classloader could end up in any package set
914
loaderToPackagesMap.remove(cl);
915             for (Iterator JavaDoc i = packagesMap.entrySet().iterator(); i.hasNext();)
916             {
917                Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
918                Set JavaDoc pkgSet = (Set JavaDoc) entry.getValue();
919                pkgSet.remove(cl);
920                if (pkgSet.isEmpty())
921                   i.remove();
922             }
923          }
924       }
925
926       // Send the class removal notfications outside of the synchronized block
927
for (int n = 0; n < removeNotifications.size(); n++)
928       {
929          Notification JavaDoc msg = (Notification JavaDoc) removeNotifications.get(n);
930          broadcaster.sendNotification(msg);
931       }
932
933       Notification JavaDoc msg = new Notification JavaDoc(CLASSLOADER_REMOVED, this, getNextSequenceNumber());
934       msg.setUserData(cl);
935       broadcaster.sendNotification(msg);
936    }
937
938    /**
939     * This method provides an mbean-accessible way to add a
940     * UnifiedClassloader, and sends a notification when it is added.
941     *
942     * @param ucl an <code>UnifiedClassLoader</code> value
943     * @return a <code>LoaderRepository</code> value
944     *
945     * @jmx.managed-operation
946     */

947    public LoaderRepository registerClassLoader(RepositoryClassLoader ucl)
948    {
949       addClassLoader(ucl);
950       Notification JavaDoc msg = new Notification JavaDoc(CLASSLOADER_ADDED, this, getNextSequenceNumber());
951       msg.setUserData(ucl);
952       broadcaster.sendNotification(msg);
953
954       return this;
955    }
956
957    /**
958     * @jmx.managed-operation
959     */

960    public LoaderRepository getInstance()
961    {
962       return this;
963    }
964
965    // implementation of javax.management.NotificationBroadcaster interface
966

967    /**
968     * addNotificationListener delegates to the broadcaster object we hold.
969     *
970     * @param listener a <code>NotificationListener</code> value
971     * @param filter a <code>NotificationFilter</code> value
972     * @param handback an <code>Object</code> value
973     * @exception IllegalArgumentException if an error occurs
974     */

975    public void addNotificationListener(NotificationListener JavaDoc listener,
976                                        NotificationFilter JavaDoc filter, Object JavaDoc handback) throws IllegalArgumentException JavaDoc
977    {
978       broadcaster.addNotificationListener(listener, filter, handback);
979    }
980
981    /**
982     *
983     * @return <description>
984     */

985    public MBeanNotificationInfo JavaDoc[] getNotificationInfo()
986    {
987       if (info == null)
988       {
989          info = new MBeanNotificationInfo JavaDoc[]{
990             new MBeanNotificationInfo JavaDoc(new String JavaDoc[]{"CLASSLOADER_ADDED"},
991                     "javax.management.Notification",
992                     "Notification that a classloader has been added to the extensible classloader"),
993             new MBeanNotificationInfo JavaDoc(new String JavaDoc[]{"CLASS_REMOVED"},
994                     "javax.management.Notification",
995                     "Notification that a class has been removed from the extensible classloader")
996
997          };
998       }
999       return info;
1000   }
1001
1002   /**
1003    * removeNotificationListener delegates to our broadcaster object
1004    *
1005    * @param listener a <code>NotificationListener</code> value
1006    * @exception ListenerNotFoundException if an error occurs
1007    */

1008   public void removeNotificationListener(NotificationListener JavaDoc listener) throws ListenerNotFoundException JavaDoc
1009   {
1010      broadcaster.removeNotificationListener(listener);
1011   }
1012
1013   // MBeanRegistration
1014
public ObjectName JavaDoc preRegister(MBeanServer JavaDoc server, ObjectName JavaDoc name)
1015      throws Exception JavaDoc
1016   {
1017      return name;
1018   }
1019
1020   public void postRegister(Boolean JavaDoc registrationDone)
1021   {
1022   }
1023
1024   public void preDeregister()
1025      throws Exception JavaDoc
1026   {
1027   }
1028
1029   public void postDeregister()
1030   {
1031      log.debug("postDeregister, clearing all references");
1032      classLoaders.clear();
1033      dynamicClassLoaders.clear();
1034      nonUCLClassLoader.clear();
1035      classLoaderURLs.clear();
1036      classes.clear();
1037      loaderToClassesMap.clear();
1038      loaderToResourcesMap.clear();
1039      globalResources.clear();
1040      packagesMap.clear();
1041      loaderToPackagesMap.clear();
1042   }
1043
1044   private synchronized long getNextSequenceNumber()
1045   {
1046      return sequenceNumber++;
1047   }
1048
1049
1050   private RepositoryClassLoader getRepositoryClassLoader(ClassLoader JavaDoc cl, String JavaDoc name) throws ClassNotFoundException JavaDoc
1051   {
1052      if (cl instanceof RepositoryClassLoader)
1053         return (RepositoryClassLoader) cl;
1054      else
1055      {
1056         RepositoryClassLoader rcl = getWrappingClassLoader(cl);
1057         if (rcl == null)
1058            throw new ClassNotFoundException JavaDoc("Class not found " + name + " (Unknown classloader " + cl + ")");
1059         return rcl;
1060      }
1061   }
1062
1063   private class PackageMapper implements ClassLoaderUtils.PkgNameListener
1064   {
1065      private RepositoryClassLoader loader;
1066      PackageMapper(RepositoryClassLoader loader)
1067      {
1068         this.loader = loader;
1069      }
1070      public void addPackage(String JavaDoc pkgName)
1071      {
1072         // Skip the standard J2EE archive directories
1073
if( pkgName.startsWith("META-INF") || pkgName.startsWith("WEB-INF") )
1074            return;
1075
1076         Set JavaDoc<RepositoryClassLoader> pkgSet = (Set JavaDoc<RepositoryClassLoader>) packagesMap.get(pkgName);
1077         if( pkgSet == null )
1078         {
1079            pkgSet = ClassLoaderUtils.newPackageSet();
1080            packagesMap.put(pkgName, pkgSet);
1081         }
1082         if( pkgSet.contains(loader) == false )
1083         {
1084            pkgSet.add((RepositoryClassLoader)loader);
1085            List JavaDoc<String JavaDoc> loaderPkgNames = loaderToPackagesMap.get(loader);
1086            if( loaderPkgNames == null )
1087            {
1088               loaderPkgNames = new ArrayList JavaDoc<String JavaDoc>();
1089               loaderToPackagesMap.put((RepositoryClassLoader)loader, loaderPkgNames);
1090            }
1091            loaderPkgNames.add(pkgName);
1092
1093            // Anytime more than one class loader exists this may indicate a problem
1094
if( pkgSet.size() > 1 )
1095            {
1096               log.debug("Multiple class loaders found for pkg: "+pkgName);
1097            }
1098            if( log.isTraceEnabled() )
1099               log.trace("Indexed pkg: "+pkgName+", UCL: "+loader);
1100         }
1101      }
1102   }
1103}
1104
Popular Tags