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: lu4242 $)
45 * @version $Revision: 1151650 $ $Date: 2011-07-27 17:14:17 -0500 (Wed, 27 Jul 2011) $
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 }