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