View Javadoc

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