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 static org.junit.Assert.*;
22
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.PrintStream;
28 import java.lang.reflect.Method;
29 import java.net.URL;
30 import java.net.URLClassLoader;
31 import java.util.*;
32 import java.util.concurrent.atomic.AtomicLong;
33 import java.util.jar.*;
34 import javax.tools.*;
35
36 import org.apache.hadoop.hbase.SmallTests;
37
38 import org.junit.experimental.categories.Category;
39 import org.junit.AfterClass;
40 import org.junit.BeforeClass;
41 import org.junit.Test;
42
43 import org.apache.commons.io.FileUtils;
44
45 @Category(SmallTests.class)
46 public class TestClassFinder {
47 private static final HBaseTestingUtility testUtil = new HBaseTestingUtility();
48 private static final String BASEPKG = "tfcpkg";
49
50
51
52 private static AtomicLong testCounter = new AtomicLong(0);
53 private static AtomicLong jarCounter = new AtomicLong(0);
54
55 private static String basePath = null;
56
57
58 private static final ClassFinder.FileNameFilter trueNameFilter =
59 new ClassFinder.FileNameFilter() {
60 @Override
61 public boolean isCandidateFile(String fileName, String absFilePath) {
62 return true;
63 }
64 };
65 private static final ClassFinder.ClassFilter trueClassFilter =
66 new ClassFinder.ClassFilter() {
67 @Override
68 public boolean isCandidateClass(Class<?> c) {
69 return true;
70 }
71 };
72
73 @BeforeClass
74 public static void createTestDir() throws IOException {
75 basePath = testUtil.getDataTestDir(TestClassFinder.class.getSimpleName()).toString();
76 if (!basePath.endsWith("/")) {
77 basePath += "/";
78 }
79
80 File testDir = new File(basePath);
81 if (testDir.exists()) {
82 deleteTestDir();
83 }
84 assertTrue(testDir.mkdirs());
85 }
86
87 @AfterClass
88 public static void deleteTestDir() throws IOException {
89 testUtil.cleanupTestDir(TestClassFinder.class.getSimpleName());
90 }
91
92 @Test
93 public void testClassFinderCanFindClassesInJars() throws Exception {
94 long counter = testCounter.incrementAndGet();
95 FileAndPath c1 = compileTestClass(counter, "", "c1");
96 FileAndPath c2 = compileTestClass(counter, ".nested", "c2");
97 FileAndPath c3 = compileTestClass(counter, "", "c3");
98 packageAndLoadJar(c1, c3);
99 packageAndLoadJar(c2);
100
101 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
102 Set<Class<?>> allClasses = allClassesFinder.findClasses(
103 makePackageName("", counter), false);
104 assertEquals(3, allClasses.size());
105 }
106
107 @Test
108 public void testClassFinderHandlesConflicts() throws Exception {
109 long counter = testCounter.incrementAndGet();
110 FileAndPath c1 = compileTestClass(counter, "", "c1");
111 FileAndPath c2 = compileTestClass(counter, "", "c2");
112 packageAndLoadJar(c1, c2);
113 packageAndLoadJar(c1);
114
115 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
116 Set<Class<?>> allClasses = allClassesFinder.findClasses(
117 makePackageName("", counter), false);
118 assertEquals(2, allClasses.size());
119 }
120
121 @Test
122 public void testClassFinderHandlesNestedPackages() throws Exception {
123 final String NESTED = ".nested";
124 final String CLASSNAME1 = "c2";
125 final String CLASSNAME2 = "c3";
126 long counter = testCounter.incrementAndGet();
127 FileAndPath c1 = compileTestClass(counter, "", "c1");
128 FileAndPath c2 = compileTestClass(counter, NESTED, CLASSNAME1);
129 FileAndPath c3 = compileTestClass(counter, NESTED, CLASSNAME2);
130 packageAndLoadJar(c1, c2);
131 packageAndLoadJar(c3);
132
133 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
134 Set<Class<?>> nestedClasses = allClassesFinder.findClasses(
135 makePackageName(NESTED, counter), false);
136 assertEquals(2, nestedClasses.size());
137 Class<?> nestedClass1 = makeClass(NESTED, CLASSNAME1, counter);
138 assertTrue(nestedClasses.contains(nestedClass1));
139 Class<?> nestedClass2 = makeClass(NESTED, CLASSNAME2, counter);
140 assertTrue(nestedClasses.contains(nestedClass2));
141 }
142
143 @Test
144 public void testClassFinderFiltersByNameInJar() throws Exception {
145 final String CLASSNAME = "c1";
146 final String CLASSNAMEEXCPREFIX = "c2";
147 long counter = testCounter.incrementAndGet();
148 FileAndPath c1 = compileTestClass(counter, "", CLASSNAME);
149 FileAndPath c2 = compileTestClass(counter, "", CLASSNAMEEXCPREFIX + "1");
150 FileAndPath c3 = compileTestClass(counter, "", CLASSNAMEEXCPREFIX + "2");
151 packageAndLoadJar(c1, c2, c3);
152
153 ClassFinder.FileNameFilter notExcNameFilter = new ClassFinder.FileNameFilter() {
154 @Override
155 public boolean isCandidateFile(String fileName, String absFilePath) {
156 return !fileName.startsWith(CLASSNAMEEXCPREFIX);
157 }
158 };
159 ClassFinder incClassesFinder = new ClassFinder(notExcNameFilter, trueClassFilter);
160 Set<Class<?>> incClasses = incClassesFinder.findClasses(
161 makePackageName("", counter), false);
162 assertEquals(1, incClasses.size());
163 Class<?> incClass = makeClass("", CLASSNAME, counter);
164 assertTrue(incClasses.contains(incClass));
165 }
166
167 @Test
168 public void testClassFinderFiltersByClassInJar() throws Exception {
169 final String CLASSNAME = "c1";
170 final String CLASSNAMEEXCPREFIX = "c2";
171 long counter = testCounter.incrementAndGet();
172 FileAndPath c1 = compileTestClass(counter, "", CLASSNAME);
173 FileAndPath c2 = compileTestClass(counter, "", CLASSNAMEEXCPREFIX + "1");
174 FileAndPath c3 = compileTestClass(counter, "", CLASSNAMEEXCPREFIX + "2");
175 packageAndLoadJar(c1, c2, c3);
176
177 final ClassFinder.ClassFilter notExcClassFilter = new ClassFinder.ClassFilter() {
178 @Override
179 public boolean isCandidateClass(Class<?> c) {
180 return !c.getSimpleName().startsWith(CLASSNAMEEXCPREFIX);
181 }
182 };
183 ClassFinder incClassesFinder = new ClassFinder(trueNameFilter, notExcClassFilter);
184 Set<Class<?>> incClasses = incClassesFinder.findClasses(
185 makePackageName("", counter), false);
186 assertEquals(1, incClasses.size());
187 Class<?> incClass = makeClass("", CLASSNAME, counter);
188 assertTrue(incClasses.contains(incClass));
189 }
190
191 @Test
192 public void testClassFinderCanFindClassesInDirs() throws Exception {
193
194
195 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
196 Set<Class<?>> allClasses = allClassesFinder.findClasses(
197 this.getClass().getPackage().getName(), false);
198 assertTrue(allClasses.contains(this.getClass()));
199 assertTrue(allClasses.contains(ClassFinder.class));
200 }
201
202 @Test
203 public void testClassFinderFiltersByNameInDirs() throws Exception {
204 final String thisName = this.getClass().getSimpleName();
205 ClassFinder.FileNameFilter notThisFilter = new ClassFinder.FileNameFilter() {
206 @Override
207 public boolean isCandidateFile(String fileName, String absFilePath) {
208 return !fileName.equals(thisName + ".class");
209 }
210 };
211 String thisPackage = this.getClass().getPackage().getName();
212 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
213 Set<Class<?>> allClasses = allClassesFinder.findClasses(thisPackage, false);
214 ClassFinder notThisClassFinder = new ClassFinder(notThisFilter, trueClassFilter);
215 Set<Class<?>> notAllClasses = notThisClassFinder.findClasses(thisPackage, false);
216 assertFalse(notAllClasses.contains(this.getClass()));
217 assertEquals(allClasses.size() - 1, notAllClasses.size());
218 }
219
220 @Test
221 public void testClassFinderFiltersByClassInDirs() throws Exception {
222 ClassFinder.ClassFilter notThisFilter = new ClassFinder.ClassFilter() {
223 @Override
224 public boolean isCandidateClass(Class<?> c) {
225 return c != TestClassFinder.class;
226 }
227 };
228 String thisPackage = this.getClass().getPackage().getName();
229 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
230 Set<Class<?>> allClasses = allClassesFinder.findClasses(thisPackage, false);
231 ClassFinder notThisClassFinder = new ClassFinder(trueNameFilter, notThisFilter);
232 Set<Class<?>> notAllClasses = notThisClassFinder.findClasses(thisPackage, false);
233 assertFalse(notAllClasses.contains(this.getClass()));
234 assertEquals(allClasses.size() - 1, notAllClasses.size());
235 }
236
237 @Test
238 public void testClassFinderDefaultsToOwnPackage() throws Exception {
239
240
241 ClassFinder allClassesFinder = new ClassFinder(trueNameFilter, trueClassFilter);
242 Set<Class<?>> pkgClasses = allClassesFinder.findClasses(
243 ClassFinder.class.getPackage().getName(), false);
244 Set<Class<?>> defaultClasses = allClassesFinder.findClasses(false);
245 assertArrayEquals(pkgClasses.toArray(), defaultClasses.toArray());
246 }
247
248 private static class FileAndPath {
249 String path;
250 File file;
251 public FileAndPath(String path, File file) {
252 this.file = file;
253 this.path = path;
254 }
255 }
256
257 private static Class<?> makeClass(String nestedPkgSuffix,
258 String className, long counter) throws ClassNotFoundException {
259 return Class.forName(
260 makePackageName(nestedPkgSuffix, counter) + "." + className + counter);
261 }
262
263 private static String makePackageName(String nestedSuffix, long counter) {
264 return BASEPKG + counter + nestedSuffix;
265 }
266
267
268
269
270
271
272
273
274 private static FileAndPath compileTestClass(long counter,
275 String packageNameSuffix, String classNamePrefix) throws Exception {
276 classNamePrefix = classNamePrefix + counter;
277 String packageName = makePackageName(packageNameSuffix, counter);
278 String javaPath = basePath + classNamePrefix + ".java";
279 String classPath = basePath + classNamePrefix + ".class";
280 PrintStream source = new PrintStream(javaPath);
281 source.println("package " + packageName + ";");
282 source.println("public class " + classNamePrefix
283 + " { public static void main(String[] args) { } };");
284 source.close();
285 JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
286 int result = jc.run(null, null, null, javaPath);
287 assertEquals(0, result);
288 File classFile = new File(classPath);
289 assertTrue(classFile.exists());
290 return new FileAndPath(packageName.replace('.', '/') + '/', classFile);
291 }
292
293
294
295
296
297 private static void packageAndLoadJar(FileAndPath... filesInJar) throws Exception {
298
299 String path = basePath + "jar" + jarCounter.incrementAndGet() + ".jar";
300 Manifest manifest = new Manifest();
301 manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
302 FileOutputStream fos = new FileOutputStream(path);
303 JarOutputStream jarOutputStream = new JarOutputStream(fos, manifest);
304
305
306
307 Set<String> pathsInJar = new HashSet<String>();
308 for (FileAndPath fileAndPath : filesInJar) {
309 String pathToAdd = fileAndPath.path;
310 while (pathsInJar.add(pathToAdd)) {
311 int ix = pathToAdd.lastIndexOf('/', pathToAdd.length() - 2);
312 if (ix < 0) {
313 break;
314 }
315 pathToAdd = pathToAdd.substring(0, ix);
316 }
317 }
318 for (String pathInJar : pathsInJar) {
319 jarOutputStream.putNextEntry(new JarEntry(pathInJar));
320 jarOutputStream.closeEntry();
321 }
322 for (FileAndPath fileAndPath : filesInJar) {
323 File file = fileAndPath.file;
324 jarOutputStream.putNextEntry(
325 new JarEntry(fileAndPath.path + file.getName()));
326 byte[] allBytes = new byte[(int)file.length()];
327 FileInputStream fis = new FileInputStream(file);
328 fis.read(allBytes);
329 fis.close();
330 jarOutputStream.write(allBytes);
331 jarOutputStream.closeEntry();
332 }
333 jarOutputStream.close();
334 fos.close();
335
336
337 File jarFile = new File(path);
338 assertTrue(jarFile.exists());
339 URLClassLoader urlClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
340 Method method = URLClassLoader.class
341 .getDeclaredMethod("addURL", new Class[] { URL.class });
342 method.setAccessible(true);
343 method.invoke(urlClassLoader, new Object[] { jarFile.toURI().toURL() });
344 }
345 };