KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.BufferedReader JavaDoc;
25 import java.io.File JavaDoc;
26 import java.io.FileFilter JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.InputStreamReader JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.net.URLClassLoader JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Comparator JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.LinkedList JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.TreeSet JavaDoc;
41 import java.util.zip.ZipEntry JavaDoc;
42 import java.util.zip.ZipInputStream JavaDoc;
43 import java.security.CodeSource JavaDoc;
44 import java.security.ProtectionDomain JavaDoc;
45 import java.lang.reflect.Method JavaDoc;
46
47 import org.jboss.logging.Logger;
48 import org.jboss.mx.loading.LoadMgr3.PkgClassLoader;
49
50 /** Utility methods for class loader to package names, etc.
51  *
52  * @author Scott.Stark@jboss.org
53  * @version $Revision: 57108 $
54  */

55 public class ClassLoaderUtils
56 {
57    private static Logger log = Logger.getLogger(ClassLoaderUtils.class);
58
59    /** The singleton instance of repository classloader comparator */
60    private static final Comparator JavaDoc repositoryClassLoaderComparator = new RepositoryClassLoaderComparator();
61
62    /** Format a string buffer containing the Class, Interfaces, CodeSource,
63     and ClassLoader information for the given object clazz.
64
65     @param clazz the Class
66     @param results - the buffer to write the info to
67     */

68    public static void displayClassInfo(Class JavaDoc clazz, StringBuffer JavaDoc results)
69    {
70       // Print out some codebase info for the ProbeHome
71
ClassLoader JavaDoc cl = clazz.getClassLoader();
72       results.append("\n"+clazz.getName()+"("+Integer.toHexString(clazz.hashCode())+").ClassLoader="+cl);
73       ClassLoader JavaDoc parent = cl;
74       while( parent != null )
75       {
76          results.append("\n.."+parent);
77          URL JavaDoc[] urls = getClassLoaderURLs(parent);
78          int length = urls != null ? urls.length : 0;
79          for(int u = 0; u < length; u ++)
80          {
81             results.append("\n...."+urls[u]);
82          }
83          if( parent != null )
84             parent = parent.getParent();
85       }
86       CodeSource JavaDoc clazzCS = clazz.getProtectionDomain().getCodeSource();
87       if( clazzCS != null )
88          results.append("\n++++CodeSource: "+clazzCS);
89       else
90          results.append("\n++++Null CodeSource");
91
92       results.append("\nImplemented Interfaces:");
93       Class JavaDoc[] ifaces = clazz.getInterfaces();
94       for(int i = 0; i < ifaces.length; i ++)
95       {
96          Class JavaDoc iface = ifaces[i];
97          results.append("\n++"+iface+"("+Integer.toHexString(iface.hashCode())+")");
98          ClassLoader JavaDoc loader = ifaces[i].getClassLoader();
99          results.append("\n++++ClassLoader: "+loader);
100          ProtectionDomain JavaDoc pd = ifaces[i].getProtectionDomain();
101          CodeSource JavaDoc cs = pd.getCodeSource();
102          if( cs != null )
103             results.append("\n++++CodeSource: "+cs);
104          else
105             results.append("\n++++Null CodeSource");
106       }
107    }
108
109    /** Use reflection to access a URL[] getURLs or URL[] getClasspath method so
110     that non-URLClassLoader class loaders, or class loaders that override
111     getURLs to return null or empty, can provide the true classpath info.
112     */

113    public static URL JavaDoc[] getClassLoaderURLs(ClassLoader JavaDoc cl)
114    {
115       URL JavaDoc[] urls = {};
116       try
117       {
118          Class JavaDoc returnType = urls.getClass();
119          Class JavaDoc[] parameterTypes = {};
120          Class JavaDoc clClass = cl.getClass();
121          Method JavaDoc getURLs = clClass.getMethod("getURLs", parameterTypes);
122          if( returnType.isAssignableFrom(getURLs.getReturnType()) )
123          {
124             Object JavaDoc[] args = {};
125             urls = (URL JavaDoc[]) getURLs.invoke(cl, args);
126          }
127          if( urls == null || urls.length == 0 )
128          {
129             Method JavaDoc getCp = clClass.getMethod("getClasspath", parameterTypes);
130             if( returnType.isAssignableFrom(getCp.getReturnType()) )
131             {
132                Object JavaDoc[] args = {};
133                urls = (URL JavaDoc[]) getCp.invoke(cl, args);
134             }
135          }
136       }
137       catch(Exception JavaDoc ignore)
138       {
139       }
140       return urls;
141    }
142
143
144    /** Get all of the URLClassLoaders from cl on up the hierarchy
145     *
146     * @param cl the class loader to start from
147     * @return The possibly empty array of URLClassLoaders from cl through
148     * its parent class loaders
149     */

150    public static URLClassLoader JavaDoc[] getClassLoaderStack(ClassLoader JavaDoc cl)
151    {
152       ArrayList JavaDoc stack = new ArrayList JavaDoc();
153       while( cl != null )
154       {
155          if( cl instanceof URLClassLoader JavaDoc )
156          {
157             stack.add(cl);
158          }
159          cl = cl.getParent();
160       }
161       URLClassLoader JavaDoc[] ucls = new URLClassLoader JavaDoc[stack.size()];
162       stack.toArray(ucls);
163       return ucls;
164    }
165
166    /** Translates a dot class name (java.lang.String) into a path form
167     * suitable for a jar entry (java/lang/String.class)
168     *
169     * @param className java.lang.String
170     * @return java/lang/String.class
171     */

172    public static String JavaDoc getJarClassName(String JavaDoc className)
173    {
174       String JavaDoc jarClassName = className.replace('.', '/');
175       return jarClassName + ".class";
176    }
177
178    /** Parse a class name into its package prefix. This has to handle
179       array classes whose name is prefixed with [L.
180     */

181    public static String JavaDoc getPackageName(String JavaDoc className)
182    {
183       int startIndex = 0;
184       // Strip any leading "[+L" found in array class names
185
if( className.length() > 0 && className.charAt(0) == '[' )
186       {
187          // Move beyond the [...[L prefix
188
startIndex = className.indexOf('L') + 1;
189       }
190        // Now extract the package name
191
String JavaDoc pkgName = "";
192       int endIndex = className.lastIndexOf('.');
193       if( endIndex > 0 )
194          pkgName = className.substring(startIndex, endIndex);
195       return pkgName;
196    }
197
198    /** Parse a class name into its resource form. This has to handle
199       array classes whose name is prefixed with [L.
200     */

201    public static String JavaDoc getResourceName(String JavaDoc className)
202    {
203       int startIndex = 0;
204       // Strip any leading "[+L" found in array class names
205
if( className.length() > 0 && className.charAt(0) == '[' )
206       {
207          // Move beyond the [...[L prefix
208
startIndex = className.indexOf('L') + 1;
209       }
210        // Now extract the package name
211
String JavaDoc resName = "";
212       int endIndex = className.lastIndexOf('.');
213       if( endIndex > 0 )
214          resName = className.substring(startIndex, endIndex);
215       return resName.replace('.', '/');
216    }
217
218    /**
219     * Create a new package set
220     *
221     * @return the new package set
222     */

223    public static Set JavaDoc newPackageSet()
224    {
225       return new TreeSet JavaDoc(repositoryClassLoaderComparator);
226    }
227
228    /**
229     * Clone a package set
230     *
231     * @param toClone set to clone
232     * @return the cloned package set
233     */

234    public static Set JavaDoc clonePackageSet(Object JavaDoc toClone)
235    {
236       TreeSet JavaDoc original = (TreeSet JavaDoc) toClone;
237       return (Set JavaDoc) original.clone();
238    }
239       
240    /** Augment the package name associated with a UCL.
241     * @param cl the UCL that loads from url
242     * @param packagesMap the Map<cl, String[]> to update
243     * @param url the URL to parse for package names
244     * @param prevPkgNames the set of pckage names already associated with cl
245     * @return the updated unique set of package names
246     * @throws Exception
247     */

248    public static void updatePackageMap(URL JavaDoc url, PkgNameListener listener)
249       throws Exception JavaDoc
250    {
251       ClassPathIterator cpi = new ClassPathIterator(url);
252       updatePackageMap(cpi, listener);
253    }
254
255    /** Augment the class names associated with a UCL.
256     * @param cl the UCL that loads from url
257     * @param classNamesMap the Map<cl, String[]> to update
258     * @param url the URL to parse for class names
259     * @param prevClassNames the set of pckage names already associated with cl
260     * @return the updated list of class names
261     * @throws Exception
262     */

263    public static String JavaDoc[] updateClassNamesMap(Object JavaDoc cl,
264       Map JavaDoc classNamesMap, URL JavaDoc url, String JavaDoc[] prevClassNames)
265       throws Exception JavaDoc
266    {
267       ClassPathIterator cpi = new ClassPathIterator(url);
268       HashSet JavaDoc classNameSet = null;
269       if (prevClassNames == null)
270          classNameSet = new HashSet JavaDoc();
271       else
272          classNameSet = new HashSet JavaDoc(Arrays.asList(prevClassNames));
273       return updateClassNamesMap(cl, classNamesMap, cpi, classNameSet);
274    }
275
276    static void updatePackageMap(ClassPathIterator cpi, PkgNameListener listener)
277       throws Exception JavaDoc
278    {
279       ClassPathEntry entry;
280       while( (entry = cpi.getNextEntry()) != null )
281       {
282          String JavaDoc name = entry.getName();
283          // First look for a META-INF/INDEX.LIST entry
284
if( name.equals("META-INF/INDEX.LIST") )
285          {
286             readJarIndex(cpi, listener);
287             // We are done
288
break;
289          }
290
291          // Skip empty directory entries
292
if( entry.isDirectory() == true )
293             continue;
294
295          String JavaDoc pkgName = entry.toPackageName();
296          listener.addPackage(pkgName);
297       }
298       cpi.close();
299    }
300
301    static String JavaDoc[] updateClassNamesMap(Object JavaDoc cl, Map JavaDoc classNamesMap,
302       ClassPathIterator cpi, HashSet JavaDoc classNameSet)
303       throws Exception JavaDoc
304    {
305       boolean trace = log.isTraceEnabled();
306       ClassPathEntry entry;
307       while( (entry = cpi.getNextEntry()) != null )
308       {
309          String JavaDoc name = entry.getName();
310          // Skip empty directory entries
311
if( entry.isDirectory() == true )
312             continue;
313          // Skip non .class files
314
if( name.endsWith(".class") == false )
315             continue;
316
317          addClass(name, classNamesMap, cl, trace);
318          classNameSet.add(name);
319       }
320       cpi.close();
321
322       // Return an array of the package names
323
String JavaDoc[] classNames = new String JavaDoc[classNameSet.size()];
324       classNameSet.toArray(classNames);
325       return classNames;
326    }
327
328    /** Read the JDK 1.3+ META-INF/INDEX.LIST entry to obtain the package
329     names without having to iterate through all entries in the jar.
330     */

331    private static void readJarIndex(ClassPathIterator cpi, PkgNameListener listener)
332       throws Exception JavaDoc
333    {
334       boolean trace = log.isTraceEnabled();
335       InputStream JavaDoc zis = cpi.getInputStream();
336       BufferedReader JavaDoc br = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(zis));
337       String JavaDoc line;
338       // Skip the jar index header
339
while( (line = br.readLine()) != null )
340       {
341          if( line.length() == 0 )
342             break;
343       }
344
345       // Read the main jar section
346
String JavaDoc jarName = br.readLine();
347       if( trace )
348          log.trace("Reading INDEX.LIST for jar: "+jarName);
349       while( (line = br.readLine()) != null )
350       {
351          if( line.length() == 0 )
352             break;
353          String JavaDoc pkgName = line.replace('/', '.');
354          listener.addPackage(pkgName);
355       }
356       br.close();
357    }
358
359    /** Add a class name to the UCL map.
360     * @param jarClassName the class name in the jar (java/lang/String.class)
361     * @param classNamesMap the UCL class name mappings
362     * @param cl the UCL
363     * @param trace the logging trace level flag
364     */

365    private static void addClass(String JavaDoc jarClassName, Map JavaDoc classNamesMap,
366       Object JavaDoc cl, boolean trace)
367    {
368       LinkedList JavaDoc ucls = (LinkedList JavaDoc) classNamesMap.get(jarClassName);
369       if( ucls == null )
370       {
371          ucls = new LinkedList JavaDoc();
372          ucls.add(cl);
373          classNamesMap.put(jarClassName, ucls);
374       }
375       else
376       {
377          boolean uclIsMapped = ucls.contains(cl);
378          if( uclIsMapped == false )
379          {
380             log.debug("Multiple class loaders found for class: "+jarClassName
381                + ", duplicate UCL: "+cl);
382             ucls.add(cl);
383          }
384       }
385       if( trace )
386          log.trace("Indexed class: "+jarClassName+", UCL: "+ucls.get(0));
387    }
388
389    public static interface PkgNameListener
390    {
391       public void addPackage(String JavaDoc name);
392    }
393
394    /**
395    */

396    static class FileIterator
397    {
398       LinkedList JavaDoc subDirectories = new LinkedList JavaDoc();
399       FileFilter JavaDoc filter;
400       File JavaDoc[] currentListing;
401       int index = 0;
402
403       FileIterator(File JavaDoc start)
404       {
405          String JavaDoc name = start.getName();
406          // Don't recurse into wars
407
boolean isWar = name.endsWith(".war");
408          if( isWar )
409             currentListing = new File JavaDoc[0];
410          else
411             currentListing = start.listFiles();
412       }
413       FileIterator(File JavaDoc start, FileFilter JavaDoc filter)
414       {
415          String JavaDoc name = start.getName();
416          // Don't recurse into wars
417
boolean isWar = name.endsWith(".war");
418          if( isWar )
419             currentListing = new File JavaDoc[0];
420          else
421             currentListing = start.listFiles(filter);
422          this.filter = filter;
423       }
424
425       File JavaDoc getNextEntry()
426       {
427          File JavaDoc next = null;
428          if( index >= currentListing.length && subDirectories.size() > 0 )
429          {
430             do
431             {
432                File JavaDoc nextDir = (File JavaDoc) subDirectories.removeFirst();
433                currentListing = nextDir.listFiles(filter);
434             } while( currentListing.length == 0 && subDirectories.size() > 0 );
435             index = 0;
436          }
437          if( index < currentListing.length )
438          {
439             next = currentListing[index ++];
440             if( next.isDirectory() )
441                subDirectories.addLast(next);
442          }
443          return next;
444       }
445    }
446
447    /**
448     */

449    static class ClassPathEntry
450    {
451       String JavaDoc name;
452       ZipEntry JavaDoc zipEntry;
453       File JavaDoc fileEntry;
454
455       ClassPathEntry(ZipEntry JavaDoc zipEntry)
456       {
457          this.zipEntry = zipEntry;
458          this.name = zipEntry.getName();
459       }
460       ClassPathEntry(File JavaDoc fileEntry, int rootLength)
461       {
462          this.fileEntry = fileEntry;
463          this.name = fileEntry.getPath().substring(rootLength);
464       }
465
466       String JavaDoc getName()
467       {
468          return name;
469       }
470       /** Convert the entry path to a package name
471        */

472       String JavaDoc toPackageName()
473       {
474          String JavaDoc pkgName = name;
475          char separatorChar = zipEntry != null ? '/' : File.separatorChar;
476          int index = name.lastIndexOf(separatorChar);
477          if( index > 0 )
478          {
479             pkgName = name.substring(0, index);
480             pkgName = pkgName.replace(separatorChar, '.');
481          }
482          else
483          {
484             // This must be an entry in the default package (e.g., X.class)
485
pkgName = "";
486          }
487          return pkgName;
488       }
489
490       boolean isDirectory()
491       {
492          boolean isDirectory = false;
493          if( zipEntry != null )
494             isDirectory = zipEntry.isDirectory();
495          else
496             isDirectory = fileEntry.isDirectory();
497          return isDirectory;
498       }
499    }
500
501    /** An iterator for jar entries or directory structures.
502    */

503    static class ClassPathIterator
504    {
505       ZipInputStream JavaDoc zis;
506       FileIterator fileIter;
507       File JavaDoc file;
508       int rootLength;
509
510       ClassPathIterator(URL JavaDoc url) throws IOException JavaDoc
511       {
512          String JavaDoc protocol = url != null ? url.getProtocol() : null;
513          if( protocol == null )
514          {
515          }
516          else if( protocol.equals("file") )
517          {
518             File JavaDoc tmp = new File JavaDoc(url.getFile());
519             if( tmp.isDirectory() )
520             {
521                rootLength = tmp.getPath().length() + 1;
522                fileIter = new FileIterator(tmp);
523             }
524             else
525             {
526                // Assume this is a jar archive
527
InputStream JavaDoc is = new FileInputStream JavaDoc(tmp);
528                zis = new ZipInputStream JavaDoc(is);
529             }
530          }
531          else
532          {
533             // Assume this points to a jar
534
InputStream JavaDoc is = url.openStream();
535             zis = new ZipInputStream JavaDoc(is);
536          }
537       }
538
539       ClassPathEntry getNextEntry() throws IOException JavaDoc
540       {
541          ClassPathEntry entry = null;
542          if( zis != null )
543          {
544             ZipEntry JavaDoc zentry = zis.getNextEntry();
545             if( zentry != null )
546                entry = new ClassPathEntry(zentry);
547          }
548          else if( fileIter != null )
549          {
550             File JavaDoc fentry = fileIter.getNextEntry();
551             if( fentry != null )
552                entry = new ClassPathEntry(fentry, rootLength);
553             file = fentry;
554          }
555
556          return entry;
557       }
558
559       InputStream JavaDoc getInputStream() throws IOException JavaDoc
560       {
561          InputStream JavaDoc is = zis;
562          if( zis == null )
563          {
564             is = new FileInputStream JavaDoc(file);
565          }
566          return is;
567       }
568
569       void close() throws IOException JavaDoc
570       {
571          if( zis != null )
572             zis.close();
573       }
574
575    }
576    
577    /**
578     * A comparator for comparing repository classloaders
579     */

580    private static class RepositoryClassLoaderComparator implements Comparator JavaDoc
581    {
582       /**
583        * Compares two repository classloaders, they are ordered by:
584        * 1) parent->child delegation rules in the loader repository
585        * 2) added order inside the loader repository
586        */

587       public int compare(Object JavaDoc o1, Object JavaDoc o2)
588       {
589          if (o1 instanceof PkgClassLoader)
590          {
591             PkgClassLoader pkg1 = (PkgClassLoader) o1;
592             PkgClassLoader pkg2 = (PkgClassLoader) o2;
593             RepositoryClassLoader rcl1 = pkg1.ucl;
594             RepositoryClassLoader rcl2 = pkg2.ucl;
595             // We use the package classloader ordering before the repository order
596
int test = (pkg1.order - pkg2.order);
597             if (test != 0)
598                return test;
599             else
600                return rcl1.getAddedOrder() - rcl2.getAddedOrder();
601          }
602          else
603          {
604             RepositoryClassLoader rcl1 = (RepositoryClassLoader) o1;
605             RepositoryClassLoader rcl2 = (RepositoryClassLoader) o2;
606             return rcl1.getAddedOrder() - rcl2.getAddedOrder();
607             
608             // REVIEW: Alternative to using the pkgClassLoader is
609
// ordering based on the loader repository
610

611             //LoaderRepository lr1 = rcl1.getLoaderRepository();
612
//LoaderRepository lr2 = rcl2.getLoaderRepository();
613

614             // Are the loader repositories ordered?
615
//int test = lr1.compare(lr2);
616
//if (test != 0)
617
// return test;
618
//else
619
// return rcl1.getAddedOrder() - rcl2.getAddedOrder();
620
}
621       }
622    }
623 }
624
Popular Tags