1
2
3
4
5
6
7
8
9
10 package org.mule.util.scan;
11
12 import org.mule.config.ExceptionHelper;
13 import org.mule.util.ClassUtils;
14 import org.mule.util.FileUtils;
15 import org.mule.util.scan.annotations.AnnotationFilter;
16 import org.mule.util.scan.annotations.AnnotationTypeFilter;
17 import org.mule.util.scan.annotations.AnnotationsScanner;
18 import org.mule.util.scan.annotations.ClosableClassReader;
19 import org.mule.util.scan.annotations.MetaAnnotationTypeFilter;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.lang.annotation.Annotation;
26 import java.lang.annotation.ElementType;
27 import java.lang.annotation.Target;
28 import java.lang.reflect.Modifier;
29 import java.net.URL;
30 import java.util.Collection;
31 import java.util.Enumeration;
32 import java.util.HashSet;
33 import java.util.Set;
34 import java.util.jar.JarEntry;
35 import java.util.jar.JarFile;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.objectweb.asm.ClassReader;
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class ClasspathScanner
54 {
55 public static final int INCLUDE_ABSTRACT = 0x01;
56 public static final int INCLUDE_INTERFACE = 0x02;
57 public static final int INCLUDE_INNER = 0x04;
58 public static final int INCLUDE_ANONYMOUS = 0x08;
59
60 public static final int DEFAULT_FLAGS = 0x0;
61
62
63
64 protected transient final Log logger = LogFactory.getLog(ClasspathScanner.class);
65
66 private ClassLoader classLoader;
67
68 private String[] basepaths;
69
70 public ClasspathScanner(String... basepaths)
71 {
72 this.classLoader = Thread.currentThread().getContextClassLoader();
73 this.basepaths = basepaths;
74 }
75
76 public ClasspathScanner(ClassLoader classLoader, String... basepaths)
77 {
78 this.classLoader = classLoader;
79 this.basepaths = basepaths;
80 }
81
82 public Set<Class> scanFor(Class clazz) throws IOException
83 {
84 return scanFor(clazz, DEFAULT_FLAGS);
85 }
86
87 public Set<Class> scanFor(Class clazz, int flags) throws IOException
88 {
89 Set<Class> classes = new HashSet<Class>();
90
91 for (int i = 0; i < basepaths.length; i++)
92 {
93 String basepath = basepaths[i];
94
95 Enumeration<URL> urls = classLoader.getResources(basepath.trim());
96 while (urls.hasMoreElements())
97 {
98 URL url = urls.nextElement();
99 if (url.getProtocol().equalsIgnoreCase("file"))
100 {
101 classes.addAll(processFileUrl(url, basepath, clazz, flags));
102 }
103 else if (url.getProtocol().equalsIgnoreCase("jar"))
104 {
105 classes.addAll(processJarUrl(url, basepath, clazz, flags));
106 }
107 else
108 {
109 throw new IllegalArgumentException("Do not understand how to handle protocol: " + url.getProtocol());
110 }
111 }
112 }
113 return classes;
114 }
115
116 protected Set<Class> processJarUrl(URL url, String basepath, Class clazz, int flags) throws IOException
117 {
118 Set<Class> set = new HashSet<Class>();
119 String path = url.getFile().substring(5, url.getFile().indexOf("!"));
120
121 path = path.replaceAll("%20", " ");
122 JarFile jar = new JarFile(path);
123
124 for (Enumeration entries = jar.entries(); entries.hasMoreElements();)
125 {
126 JarEntry entry = (JarEntry) entries.nextElement();
127 if (entry.getName().startsWith(basepath) && entry.getName().endsWith(".class"))
128 {
129 try
130 {
131 String name = entry.getName();
132
133 if (name.contains("$") && !hasFlag(flags, INCLUDE_INNER))
134 {
135 continue;
136 }
137
138 URL classURL = classLoader.getResource(name);
139 InputStream classStream = classURL.openStream();
140 ClassReader reader = new ClosableClassReader(classStream);
141
142 ClassScanner visitor = getScanner(clazz);
143 reader.accept(visitor, 0);
144 if (visitor.isMatch())
145 {
146 addClassToSet(loadClass(visitor.getClassName()), set, flags);
147 }
148 }
149 catch (Exception e)
150 {
151 if (logger.isDebugEnabled())
152 {
153 Throwable t = ExceptionHelper.getRootException(e);
154 logger.debug(String.format("%s: caused by: %s", e.toString(), t.toString()));
155 }
156 }
157 }
158 }
159 jar.close();
160
161 return set;
162 }
163
164 protected boolean hasFlag(int flags, int flag)
165 {
166 return (flags & flag) != 0;
167 }
168
169 protected Set<Class> processFileUrl(URL url, String basepath, Class clazz, int flags) throws IOException
170 {
171 Set<Class> set = new HashSet<Class>();
172 String urlBase = url.getFile();
173
174 urlBase = urlBase.replaceAll("%20", " ");
175 File dir = new File(urlBase);
176 if(!dir.isDirectory())
177 {
178 logger.warn("Cannot process File URL: " + url + ". Path is not a directory");
179 return set;
180 }
181
182 Collection<File> files = FileUtils.listFiles(new File(urlBase), new String[]{"class"}, true);
183 for (File file : files)
184 {
185 try
186 {
187
188 if (file.getName().contains("$") && !hasFlag(flags, INCLUDE_INNER))
189 {
190 continue;
191 }
192 InputStream classStream = new FileInputStream(file);
193 ClassReader reader = new ClosableClassReader(classStream);
194
195 ClassScanner visitor = getScanner(clazz);
196 reader.accept(visitor, 0);
197 if (visitor.isMatch())
198 {
199 addClassToSet(loadClass(visitor.getClassName()), set, flags);
200 }
201 }
202 catch (IOException e)
203 {
204 if (logger.isDebugEnabled())
205 {
206 Throwable t = ExceptionHelper.getRootException(e);
207 logger.debug(String.format("%s: caused by: %s", e.toString(), t.toString()));
208 }
209 }
210 }
211 return set;
212 }
213
214 protected void addClassToSet(Class c, Set<Class> set, int flags)
215 {
216 if (c != null)
217 {
218 synchronized (set)
219 {
220 if(c.isInterface())
221 {
222 if(hasFlag(flags, INCLUDE_INTERFACE)) set.add(c);
223 }
224 else if(Modifier.isAbstract(c.getModifiers()))
225 {
226 if(hasFlag(flags, INCLUDE_ABSTRACT)) set.add(c);
227 }
228 else
229 {
230 set.add(c);
231 }
232 }
233 }
234 }
235
236 protected Class loadClass(String name)
237 {
238 String c = name.replace("/", ".");
239 try
240 {
241 return ClassUtils.loadClass(c, classLoader);
242 }
243 catch (ClassNotFoundException e)
244 {
245 if (logger.isWarnEnabled())
246 {
247 logger.warn(String.format("%s : %s", c, e.toString()));
248 }
249 return null;
250 }
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264
265 protected ClassScanner getScanner(Class clazz)
266 {
267 if (clazz.isInterface())
268 {
269 if (clazz.isAnnotation())
270 {
271 AnnotationFilter filter = null;
272 Annotation[] annos = clazz.getDeclaredAnnotations();
273 for (int i = 0; i < annos.length; i++)
274 {
275 Annotation anno = annos[i];
276 if (anno instanceof Target)
277 {
278 if (((Target) anno).value()[0] == ElementType.ANNOTATION_TYPE)
279 {
280 filter = new MetaAnnotationTypeFilter(clazz, classLoader);
281 }
282 }
283 }
284 if (filter == null)
285 {
286 filter = new AnnotationTypeFilter(clazz);
287 }
288 return new AnnotationsScanner(filter);
289 }
290 else
291 {
292 return new InterfaceClassScanner(clazz);
293 }
294 }
295 else
296 {
297 return new ImplementationClassScanner(clazz);
298 }
299 }
300 }