2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.shale.tiger.view.faces;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.IOException;
25  import java.net.JarURLConnection;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.net.URLConnection;
29  import java.util.Enumeration;
30  import java.util.List;
31  import java.util.jar.JarEntry;
32  import java.util.jar.JarFile;
33  
34  /***
35   * <p>Utility class with methods that support getting a recursive list of
36   * classes starting with a specific package name.</p>
37   */
38  public final class PackageInfo {
39  
40      /***
41       * <p>The <code>Log</code> instance we will be using.</p>
42       */
43      private transient Log log = null;
44  
45  
46      /***
47       * the singleton for this class
48       */
49      private final static PackageInfo INSTANCE = new PackageInfo();
50  
51  
52      /***
53       * <p>Get the singleton instance of this class.</p>
54       */
55      public final static PackageInfo getInstance() {
56  
57          return INSTANCE;
58  
59      }
60  
61  
62      /***
63       * <p>Return an array of all classes, visible to our application class loader,
64       * in the specified Java package.</p>
65       *
66       * @param classes List of matching classes being accumulated
67       * @param pckgname Package name used to select matching classes
68       *
69       * @throws ClassNotFoundException
70       */
71      public Class[] getClasses(final List<Class> classes, final String pckgname)
72        throws ClassNotFoundException {
73  
74          Enumeration resources;
75          ClassLoader cld;
76          String path;
77          try {
78  
79              // convert the package name to a path
80              path = pckgname.replace('.', '/');
81  
82              cld = Thread.currentThread().getContextClassLoader();
83              if (cld == null) {
84                  throw new ClassNotFoundException("Can't get class loader.");
85              }
86  
87              // find the entry points to the classpath
88              resources = cld.getResources(path);
89              if (resources == null || !resources.hasMoreElements()) {
90                  throw new ClassNotFoundException("No resource for " + path);
91              }
92  
93          } catch (NullPointerException e) {
94              throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
95                      + ") does not appear to be a valid package", e);
96          } catch (IOException e) {
97              throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
98                      + ") does not appear to be a valid package", e);
99          }
100 
101         // iterate through all resources containing the package in question
102         while (resources.hasMoreElements()) {
103             URL resource = (URL) resources.nextElement();
104             URLConnection connection = null;
105             try {
106                 connection = resource.openConnection();
107             } catch (IOException e) {
108                 throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
109                         + ") does not appear to be a valid package", e);
110             }
111 
112             if (connection instanceof JarURLConnection) {
113                 // iterate trhough all the entries in the jar
114                 JarURLConnection juc = (JarURLConnection) connection;
115                 JarFile jarFile = null;
116                 try {
117                     jarFile = juc.getJarFile();
118                 } catch (IOException e) {
119                     throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
120                             + ") does not appear to be a valid package", e);
121                 }
122                 Enumeration<JarEntry> entries = jarFile.entries();
123                 while (entries.hasMoreElements()) {
124                     JarEntry jarEntry = entries.nextElement();
125                     String entryName = jarEntry.getName();
126                     if (!entryName.startsWith(path)) {
127                         continue;
128                     }
129                     if (!entryName.toLowerCase().endsWith(".class")) {
130                         continue;
131                     }
132                     String className = filenameToClassname(entryName);
133                     loadClass(classes, cld, className);
134                 }
135             } else {
136                 // iterate trhough all the children starting with the package name
137                 File file;
138                 try {
139                     file = new File(connection.getURL().toURI());
140                 } catch (URISyntaxException e) {
141                     log().warn("error loading directory " + connection, e);
142                     continue;
143                 }
144 
145                 listFilesRecursive(classes, file, cld, pckgname);
146             }
147         }
148 
149         if (classes.size() < 1) {
150             throw new ClassNotFoundException(pckgname
151                     + " does not appear to be a valid package");
152         }
153 
154         Class[] resolvedClasses = new Class[classes.size()];
155         classes.toArray(resolvedClasses);
156         return resolvedClasses;
157 
158     }
159 
160 
161     /***
162      * <p>Convert a filename to a classname.</p>
163      *
164      * @param entryName Filename to be converted
165      */
166     protected String filenameToClassname(String entryName) {
167 
168         return entryName.substring(0, entryName.length() - 6).replace('/', '.');
169 
170     }
171 
172 
173     /***
174      * <p>Load the class <code>className</code> using the classloader
175      * <code>cld</code>, and add it to the list.</p>
176      *
177      * @param classes List of matching classes being accumulated
178      * @param cld ClassLoader from which to load the specified class
179      * @param className Name of the class to be loaded
180      */
181     protected void loadClass(List<Class> classes, ClassLoader cld, String className) {
182 
183         try {
184             classes.add(cld.loadClass(className));
185         }
186         catch (NoClassDefFoundError e) {
187             log().warn("error loading class " + className, e);
188         } catch (ClassNotFoundException e) {
189             log().warn("error loading class " + className, e);
190         }
191 
192     }
193 
194 
195     /***
196      * <p>Traverse a directory structure starting at <code>base</code>, adding
197      * matching files to the specified list.</p>
198      *
199      * @param classes List of matching classes being accumulated
200      * @param base Base file from which to recurse
201      * @param cld ClassLoader being searched for matching classes
202      * @param pckgname Package name used to select matching classes
203      */
204     protected void listFilesRecursive(final List<Class> classes, final File base,
205             final ClassLoader cld, final String pckgname) {
206 
207         base.listFiles(new FileFilter() {
208 
209             public boolean accept(File file) {
210                 if (file.isDirectory()) {
211                     listFilesRecursive(classes, file, cld, pckgname + "." + file.getName());
212                     return false;
213                 }
214                 if (!file.getName().toLowerCase().endsWith(".class")) {
215                     return false;
216                 }
217 
218                 String className = filenameToClassname(pckgname + "." + file.getName());
219                 loadClass(classes, cld, className);
220 
221                 return false;
222             }
223 
224         });
225 
226     }
227 
228 
229     /***
230      * <p>Return the <code>Log</code> instance to be used for this class,
231      * instantiating a new one if necessary.</p>
232      */
233     private Log log() {
234 
235         if (log == null) {
236             log = LogFactory.getLog(PackageInfo.class);
237         }
238         return log;
239 
240     }
241 
242 
243 }