1 package com.github.smokestack.ejb.internal;
2
3
4
5
6
7 import java.io.*;
8 import java.net.URL;
9 import java.net.JarURLConnection;
10 import java.net.MalformedURLException;
11 import java.util.*;
12 import java.util.jar.*;
13
14
15
16
17
18
19
20
21
22
23 public class ClassFinder {
24 private Class<?> searchClass = null;
25 private Map<URL, String> classpathLocations = new HashMap<URL, String>();
26 private Map<Class<?>, URL> results = new HashMap<Class<?>, URL>();
27 private List<Throwable> errors = new ArrayList<Throwable>();
28 private boolean working = false;
29 private boolean debug=false;
30
31 public ClassFinder() {
32 refreshLocations();
33 }
34
35 public ClassFinder(boolean debug) {
36 this();
37 this.debug=debug;
38 }
39
40
41
42
43 public final void refreshLocations() {
44 synchronized (classpathLocations) {
45 classpathLocations = getClasspathLocations();
46 }
47 }
48
49
50
51
52 public final Vector<Class<?>> findSubclasses(String fqcn) {
53 synchronized (classpathLocations) {
54 synchronized (results) {
55 try {
56 working = true;
57 searchClass = null;
58 errors = new ArrayList<Throwable>();
59 results = new TreeMap<Class<?>, URL>(CLASS_COMPARATOR);
60
61
62
63
64 if (fqcn.startsWith(".") || fqcn.endsWith(".")) {
65 return new Vector<Class<?>>();
66 }
67
68
69
70
71 try {
72 searchClass = Class.forName(fqcn);
73 } catch (ClassNotFoundException ex) {
74
75 errors.add(ex);
76 return new Vector<Class<?>>();
77 }
78
79 return findSubclasses(searchClass, classpathLocations);
80 } finally {
81 working = false;
82 }
83 }
84 }
85 }
86
87 public final List<Throwable> getErrors() {
88 return new ArrayList<Throwable>(errors);
89 }
90
91
92
93
94
95
96
97
98 public final URL getLocationOf(Class<?> cls) {
99 if (results != null)
100 return results.get(cls);
101 else
102 return null;
103 }
104
105
106
107
108
109 public final Map<URL, String> getClasspathLocations() {
110 Map<URL, String> map = new TreeMap<URL, String>(URL_COMPARATOR);
111 File file = null;
112
113 String pathSep = System.getProperty("path.separator");
114 String classpath = System.getProperty("java.class.path");
115
116
117 StringTokenizer st = new StringTokenizer(classpath, pathSep);
118 while (st.hasMoreTokens()) {
119 String path = st.nextToken();
120 file = new File(path);
121 include(null, file, map);
122 }
123
124 Iterator<URL> it = map.keySet().iterator();
125 while (it.hasNext()) {
126 URL url = it.next();
127
128 }
129
130 return map;
131 }
132
133 private final static FileFilter DIRECTORIES_ONLY = new FileFilter() {
134 public boolean accept(File f) {
135 if (f.exists() && f.isDirectory())
136 return true;
137 else
138 return false;
139 }
140 };
141
142 private final static Comparator<URL> URL_COMPARATOR = new Comparator<URL>() {
143 public int compare(URL u1, URL u2) {
144 return String.valueOf(u1).compareTo(String.valueOf(u2));
145 }
146 };
147
148 private final static Comparator<Class<?>> CLASS_COMPARATOR = new Comparator<Class<?>>() {
149 public int compare(Class<?> c1, Class<?> c2) {
150 return String.valueOf(c1).compareTo(String.valueOf(c2));
151 }
152 };
153
154 private final void include(String name, File file, Map<URL, String> map) {
155 if (!file.exists())
156 return;
157 if (!file.isDirectory()) {
158
159 includeJar(file, map);
160 return;
161 }
162
163 if (name == null)
164 name = "";
165 else
166 name += ".";
167
168
169 File[] dirs = file.listFiles(DIRECTORIES_ONLY);
170 for (int i = 0; i < dirs.length; i++) {
171 try {
172
173 map.put(new URL("file://" + dirs[i].getCanonicalPath()), name
174 + dirs[i].getName());
175 } catch (IOException ioe) {
176 return;
177 }
178
179 include(name + dirs[i].getName(), dirs[i], map);
180 }
181 }
182
183 private void includeJar(File file, Map<URL, String> map) {
184 if (file.isDirectory())
185 return;
186
187 URL jarURL = null;
188 JarFile jar = null;
189 try {
190 jarURL = new URL("file://" + file.getCanonicalPath());
191 jarURL = new URL("jar:" + jarURL.toExternalForm() + "!/");
192 JarURLConnection conn = (JarURLConnection) jarURL.openConnection();
193 jar = conn.getJarFile();
194 } catch (Exception e) {
195
196
197 return;
198 }
199
200 if (jar == null || jarURL == null)
201 return;
202
203
204 map.put(jarURL, "");
205
206 Enumeration<JarEntry> e = jar.entries();
207 while (e.hasMoreElements()) {
208 JarEntry entry = e.nextElement();
209
210 if (entry.isDirectory()) {
211 if (entry.getName().toUpperCase().equals("META-INF/"))
212 continue;
213
214 try {
215 map.put(new URL(jarURL.toExternalForm() + entry.getName()),
216 packageNameFor(entry));
217 } catch (MalformedURLException murl) {
218
219 continue;
220 }
221 }
222 }
223 }
224
225 private static String packageNameFor(JarEntry entry) {
226 if (entry == null)
227 return "";
228 String s = entry.getName();
229 if (s == null)
230 return "";
231 if (s.length() == 0)
232 return s;
233 if (s.startsWith("/"))
234 s = s.substring(1, s.length());
235 if (s.endsWith("/"))
236 s = s.substring(0, s.length() - 1);
237 return s.replace('/', '.');
238 }
239
240 private final void includeResourceLocations(String packageName, Map<URL, String> map) {
241 try {
242 Enumeration<URL> resourceLocations = ClassFinder.class
243 .getClassLoader().getResources(getPackagePath(packageName));
244
245 while (resourceLocations.hasMoreElements()) {
246 map.put(resourceLocations.nextElement(), packageName);
247 }
248 } catch (Exception e) {
249
250 if (debug){
251 errors.add(e);
252 }
253 return;
254 }
255 }
256
257 private final Vector<Class<?>> findSubclasses(Class<?> superClass,
258 Map<URL, String> locations) {
259 Vector<Class<?>> v = new Vector<Class<?>>();
260
261 Vector<Class<?>> w = null;
262
263
264
265
266
267
268
269 Iterator<URL> it = locations.keySet().iterator();
270 while (it.hasNext()) {
271 URL url = it.next();
272
273
274 w = findSubclasses(url, locations.get(url), superClass);
275 if (w != null && (w.size() > 0))
276 v.addAll(w);
277 }
278
279 return v;
280 }
281
282 private final Vector<Class<?>> findSubclasses(URL location,
283 String packageName, Class<?> superClass) {
284
285
286
287 synchronized (results) {
288
289
290 Map<Class<?>, URL> thisResult = new TreeMap<Class<?>, URL>(
291 CLASS_COMPARATOR);
292 Vector<Class<?>> v = new Vector<Class<?>>();
293
294
295
296 String fqcn = searchClass.getName();
297
298 List<URL> knownLocations = new ArrayList<URL>();
299 knownLocations.add(location);
300
301
302
303 for (int loc = 0; loc < knownLocations.size(); loc++) {
304 URL url = knownLocations.get(loc);
305
306
307 File directory = new File(url.getFile());
308
309
310
311 if (directory.exists()) {
312
313 String[] files = directory.list();
314 for (int i = 0; i < files.length; i++) {
315
316 if (files[i].endsWith(".class")) {
317
318 String classname = files[i].substring(0, files[i]
319 .length() - 6);
320
321
322
323
324 try {
325 Class<?> c = Class.forName(packageName + "."
326 + classname);
327 if (superClass.isAssignableFrom(c)
328 && !fqcn.equals(packageName + "."
329 + classname)) {
330 thisResult.put(c, url);
331 }
332 } catch (ClassNotFoundException cnfex) {
333 if (debug){
334 errors.add(cnfex);
335 }
336
337 } catch (Exception ex) {
338 if (debug){
339 errors.add(ex);
340 }
341
342 } catch (NoClassDefFoundError ncdfe){
343 if (debug){
344 errors.add(ncdfe);
345 }
346 }
347 }
348 }
349 } else {
350 try {
351
352
353 JarURLConnection conn = (JarURLConnection) url
354 .openConnection();
355
356 JarFile jarFile = conn.getJarFile();
357
358
359
360
361 Enumeration<JarEntry> e = jarFile.entries();
362 while (e.hasMoreElements()) {
363 JarEntry entry = e.nextElement();
364 String entryname = entry.getName();
365
366
367
368
369 if (!entry.isDirectory()
370 && entryname.endsWith(".class")) {
371 String classname = entryname.substring(0,
372 entryname.length() - 6);
373 if (classname.startsWith("/"))
374 classname = classname.substring(1);
375 classname = classname.replace('/', '.');
376
377
378
379
380 try {
381
382 Class c = Class.forName(classname);
383
384 if (superClass.isAssignableFrom(c)
385 && !fqcn.equals(classname)) {
386 thisResult.put(c, url);
387 }
388 } catch (ClassNotFoundException cnfex) {
389
390
391
392 errors.add(cnfex);
393 } catch (NoClassDefFoundError ncdfe) {
394
395
396 errors.add(ncdfe);
397 } catch (UnsatisfiedLinkError ule) {
398
399
400 errors.add(ule);
401 } catch (Exception exception) {
402
403
404 errors.add(exception);
405 } catch (Error error) {
406
407
408
409 errors.add(error);
410 }
411 }
412 }
413 } catch (IOException ioex) {
414
415 errors.add(ioex);
416 }
417 }
418 }
419
420
421
422 results.putAll(thisResult);
423
424 Iterator<Class<?>> it = thisResult.keySet().iterator();
425 while (it.hasNext()) {
426 v.add(it.next());
427 }
428 return v;
429
430 }
431 }
432
433 private final static String getPackagePath(String packageName) {
434
435 String path = new String(packageName);
436 if (!path.startsWith("/")) {
437 path = "/" + path;
438 }
439 path = path.replace('.', '/');
440
441
442 if (!path.endsWith("/"))
443 path += "/";
444
445
446
447
448 if (path.startsWith("/"))
449 path = path.substring(1, path.length());
450
451
452
453 return path;
454 }
455
456 public Vector<Class<?>> findSubclasses(Class<?> clazz) {
457 return findSubclasses(clazz.getCanonicalName());
458 }
459 }