1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase;
20
21 import java.io.File;
22 import java.io.FileFilter;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.net.URL;
26 import java.util.ArrayList;
27 import java.util.Enumeration;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarInputStream;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38
39
40
41
42
43
44 public class ClassFinder {
45 private static final Log LOG = LogFactory.getLog(ClassFinder.class);
46 private static String CLASS_EXT = ".class";
47
48 private ResourcePathFilter resourcePathFilter;
49 private FileNameFilter fileNameFilter;
50 private ClassFilter classFilter;
51 private FileFilter fileFilter;
52
53 public interface ResourcePathFilter {
54 boolean isCandidatePath(String resourcePath, boolean isJar);
55 };
56
57 public interface FileNameFilter {
58 boolean isCandidateFile(String fileName, String absFilePath);
59 };
60
61 public interface ClassFilter {
62 boolean isCandidateClass(Class<?> c);
63 };
64
65 public static class Not implements ResourcePathFilter, FileNameFilter, ClassFilter {
66 private ResourcePathFilter resourcePathFilter;
67 private FileNameFilter fileNameFilter;
68 private ClassFilter classFilter;
69
70 public Not(ResourcePathFilter resourcePathFilter){this.resourcePathFilter = resourcePathFilter;}
71 public Not(FileNameFilter fileNameFilter){this.fileNameFilter = fileNameFilter;}
72 public Not(ClassFilter classFilter){this.classFilter = classFilter;}
73
74 @Override
75 public boolean isCandidatePath(String resourcePath, boolean isJar) {
76 return !resourcePathFilter.isCandidatePath(resourcePath, isJar);
77 }
78 @Override
79 public boolean isCandidateFile(String fileName, String absFilePath) {
80 return !fileNameFilter.isCandidateFile(fileName, absFilePath);
81 }
82 @Override
83 public boolean isCandidateClass(Class<?> c) {
84 return !classFilter.isCandidateClass(c);
85 }
86 }
87
88 public static class And implements ClassFilter {
89 ClassFilter[] classFilters;
90 public And(ClassFilter...classFilters) { this.classFilters = classFilters; }
91 @Override
92 public boolean isCandidateClass(Class<?> c) {
93 for (ClassFilter filter : classFilters) {
94 if (!filter.isCandidateClass(c)) {
95 return false;
96 }
97 }
98 return true;
99 }
100 }
101
102 public ClassFinder() {
103 this(null, null, null);
104 }
105
106 public ClassFinder(ResourcePathFilter resourcePathFilter,
107 FileNameFilter fileNameFilter, ClassFilter classFilter) {
108 this.resourcePathFilter = resourcePathFilter;
109 this.classFilter = classFilter;
110 this.fileNameFilter = fileNameFilter;
111 this.fileFilter = new FileFilterWithName(fileNameFilter);
112 }
113
114
115
116
117
118
119 public Set<Class<?>> findClasses(boolean proceedOnExceptions)
120 throws ClassNotFoundException, IOException, LinkageError {
121 return findClasses(this.getClass().getPackage().getName(), proceedOnExceptions);
122 }
123
124
125
126
127
128
129
130 public Set<Class<?>> findClasses(String packageName, boolean proceedOnExceptions)
131 throws ClassNotFoundException, IOException, LinkageError {
132 final String path = packageName.replace('.', '/');
133 final Pattern jarResourceRe = Pattern.compile("^file:(.+\\.jar)!/" + path + "$");
134
135 Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources(path);
136 List<File> dirs = new ArrayList<File>();
137 List<String> jars = new ArrayList<String>();
138
139 while (resources.hasMoreElements()) {
140 URL resource = resources.nextElement();
141 String resourcePath = resource.getFile();
142 Matcher matcher = jarResourceRe.matcher(resourcePath);
143 boolean isJar = matcher.find();
144 resourcePath = isJar ? matcher.group(1) : resourcePath;
145 if (null == this.resourcePathFilter
146 || this.resourcePathFilter.isCandidatePath(resourcePath, isJar)) {
147 LOG.debug("Will look for classes in " + resourcePath);
148 if (isJar) {
149 jars.add(resourcePath);
150 } else {
151 dirs.add(new File(resourcePath));
152 }
153 }
154 }
155
156 Set<Class<?>> classes = new HashSet<Class<?>>();
157 for (File directory : dirs) {
158 classes.addAll(findClassesFromFiles(directory, packageName, proceedOnExceptions));
159 }
160 for (String jarFileName : jars) {
161 classes.addAll(findClassesFromJar(jarFileName, packageName, proceedOnExceptions));
162 }
163 return classes;
164 }
165
166 private Set<Class<?>> findClassesFromJar(String jarFileName,
167 String packageName, boolean proceedOnExceptions)
168 throws IOException, ClassNotFoundException, LinkageError {
169 JarInputStream jarFile = null;
170 try {
171 jarFile = new JarInputStream(new FileInputStream(jarFileName));
172 } catch (IOException ioEx) {
173 LOG.warn("Failed to look for classes in " + jarFileName + ": " + ioEx);
174 throw ioEx;
175 }
176
177 Set<Class<?>> classes = new HashSet<Class<?>>();
178 JarEntry entry = null;
179 try {
180 while (true) {
181 try {
182 entry = jarFile.getNextJarEntry();
183 } catch (IOException ioEx) {
184 if (!proceedOnExceptions) {
185 throw ioEx;
186 }
187 LOG.warn("Failed to get next entry from " + jarFileName + ": " + ioEx);
188 break;
189 }
190 if (entry == null) {
191 break;
192 }
193
194 String className = entry.getName();
195 if (!className.endsWith(CLASS_EXT)) {
196 continue;
197 }
198 int ix = className.lastIndexOf('/');
199 String fileName = (ix >= 0) ? className.substring(ix + 1) : className;
200 if (null != this.fileNameFilter
201 && !this.fileNameFilter.isCandidateFile(fileName, className)) {
202 continue;
203 }
204 className =
205 className.substring(0, className.length() - CLASS_EXT.length()).replace('/', '.');
206 if (!className.startsWith(packageName)) {
207 continue;
208 }
209 Class<?> c = makeClass(className, proceedOnExceptions);
210 if (c != null) {
211 if (!classes.add(c)) {
212 LOG.warn("Ignoring duplicate class " + className);
213 }
214 }
215 }
216 return classes;
217 } finally {
218 jarFile.close();
219 }
220 }
221
222 private Set<Class<?>> findClassesFromFiles(File baseDirectory, String packageName,
223 boolean proceedOnExceptions) throws ClassNotFoundException, LinkageError {
224 Set<Class<?>> classes = new HashSet<Class<?>>();
225 if (!baseDirectory.exists()) {
226 LOG.warn("Failed to find " + baseDirectory.getAbsolutePath());
227 return classes;
228 }
229
230 File[] files = baseDirectory.listFiles(this.fileFilter);
231 if (files == null) {
232 LOG.warn("Failed to get files from " + baseDirectory.getAbsolutePath());
233 return classes;
234 }
235
236 for (File file : files) {
237 final String fileName = file.getName();
238 if (file.isDirectory()) {
239 classes.addAll(findClassesFromFiles(file, packageName + "." + fileName,
240 proceedOnExceptions));
241 } else {
242 String className = packageName + '.'
243 + fileName.substring(0, fileName.length() - CLASS_EXT.length());
244 Class<?> c = makeClass(className, proceedOnExceptions);
245 if (c != null) {
246 if (!classes.add(c)) {
247 LOG.warn("Ignoring duplicate class " + className);
248 }
249 }
250 }
251 }
252 return classes;
253 }
254
255 private Class<?> makeClass(String className, boolean proceedOnExceptions)
256 throws ClassNotFoundException, LinkageError {
257 try {
258 Class<?> c = Class.forName(className, false, this.getClass().getClassLoader());
259 boolean isCandidateClass = null == classFilter || classFilter.isCandidateClass(c);
260 return isCandidateClass ? c : null;
261 } catch (ClassNotFoundException classNotFoundEx) {
262 if (!proceedOnExceptions) {
263 throw classNotFoundEx;
264 }
265 LOG.debug("Failed to instantiate or check " + className + ": " + classNotFoundEx);
266 } catch (LinkageError linkageEx) {
267 if (!proceedOnExceptions) {
268 throw linkageEx;
269 }
270 LOG.debug("Failed to instantiate or check " + className + ": " + linkageEx);
271 }
272 return null;
273 }
274
275 private class FileFilterWithName implements FileFilter {
276 private FileNameFilter nameFilter;
277
278 public FileFilterWithName(FileNameFilter nameFilter) {
279 this.nameFilter = nameFilter;
280 }
281
282 @Override
283 public boolean accept(File file) {
284 return file.isDirectory()
285 || (file.getName().endsWith(CLASS_EXT)
286 && (null == nameFilter
287 || nameFilter.isCandidateFile(file.getName(), file.getAbsolutePath())));
288 }
289 };
290 };