1 /* 2 * Copyright (C) 2012 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.reflect; 18 19 import static com.google.common.base.Charsets.US_ASCII; 20 import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; 21 import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; 22 import static com.google.common.truth.Truth.assertThat; 23 24 import com.google.common.base.Joiner; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.io.Closer; 27 import com.google.common.io.Files; 28 import com.google.common.io.Resources; 29 import com.google.common.reflect.ClassPath.ClassInfo; 30 import com.google.common.reflect.ClassPath.ResourceInfo; 31 import com.google.common.testing.EqualsTester; 32 import com.google.common.testing.NullPointerTester; 33 import java.io.ByteArrayInputStream; 34 import java.io.File; 35 import java.io.FileOutputStream; 36 import java.io.FilePermission; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.net.MalformedURLException; 40 import java.net.URISyntaxException; 41 import java.net.URL; 42 import java.net.URLClassLoader; 43 import java.security.Permission; 44 import java.security.PermissionCollection; 45 import java.util.HashSet; 46 import java.util.Set; 47 import java.util.jar.Attributes; 48 import java.util.jar.JarFile; 49 import java.util.jar.JarOutputStream; 50 import java.util.jar.Manifest; 51 import java.util.logging.Logger; 52 import java.util.zip.ZipEntry; 53 import junit.framework.TestCase; 54 import org.junit.Test; 55 56 /** Functional tests of {@link ClassPath}. */ 57 public class ClassPathTest extends TestCase { 58 private static final Logger log = Logger.getLogger(ClassPathTest.class.getName()); 59 private static final File FILE = new File("."); 60 testEquals()61 public void testEquals() { 62 new EqualsTester() 63 .addEqualityGroup(classInfo(ClassPathTest.class), classInfo(ClassPathTest.class)) 64 .addEqualityGroup(classInfo(Test.class), classInfo(Test.class, getClass().getClassLoader())) 65 .addEqualityGroup( 66 new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader()), 67 new ResourceInfo(FILE, "a/b/c.txt", getClass().getClassLoader())) 68 .addEqualityGroup(new ResourceInfo(FILE, "x.txt", getClass().getClassLoader())) 69 .testEquals(); 70 } 71 72 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_emptyURLClassLoader_noParent()73 public void testClassPathEntries_emptyURLClassLoader_noParent() { 74 assertThat(ClassPath.Scanner.getClassPathEntries(new URLClassLoader(new URL[0], null)).keySet()) 75 .isEmpty(); 76 } 77 78 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_URLClassLoader_noParent()79 public void testClassPathEntries_URLClassLoader_noParent() throws Exception { 80 URL url1 = new URL("file:/a"); 81 URL url2 = new URL("file:/b"); 82 URLClassLoader classloader = new URLClassLoader(new URL[] {url1, url2}, null); 83 assertThat(ClassPath.Scanner.getClassPathEntries(classloader)) 84 .containsExactly(new File("/a"), classloader, new File("/b"), classloader); 85 } 86 87 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_URLClassLoader_withParent()88 public void testClassPathEntries_URLClassLoader_withParent() throws Exception { 89 URL url1 = new URL("file:/a"); 90 URL url2 = new URL("file:/b"); 91 URLClassLoader parent = new URLClassLoader(new URL[] {url1}, null); 92 URLClassLoader child = new URLClassLoader(new URL[] {url2}, parent) {}; 93 assertThat(ClassPath.Scanner.getClassPathEntries(child)) 94 .containsExactly(new File("/a"), parent, new File("/b"), child) 95 .inOrder(); 96 } 97 98 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_duplicateUri_parentWins()99 public void testClassPathEntries_duplicateUri_parentWins() throws Exception { 100 URL url = new URL("file:/a"); 101 URLClassLoader parent = new URLClassLoader(new URL[] {url}, null); 102 URLClassLoader child = new URLClassLoader(new URL[] {url}, parent) {}; 103 assertThat(ClassPath.Scanner.getClassPathEntries(child)) 104 .containsExactly(new File("/a"), parent); 105 } 106 107 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_notURLClassLoader_noParent()108 public void testClassPathEntries_notURLClassLoader_noParent() { 109 assertThat(ClassPath.Scanner.getClassPathEntries(new ClassLoader(null) {})).isEmpty(); 110 } 111 112 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_notURLClassLoader_withParent()113 public void testClassPathEntries_notURLClassLoader_withParent() throws Exception { 114 URL url = new URL("file:/a"); 115 URLClassLoader parent = new URLClassLoader(new URL[] {url}, null); 116 assertThat(ClassPath.Scanner.getClassPathEntries(new ClassLoader(parent) {})) 117 .containsExactly(new File("/a"), parent); 118 } 119 120 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_notURLClassLoader_withParentAndGrandParent()121 public void testClassPathEntries_notURLClassLoader_withParentAndGrandParent() throws Exception { 122 URL url1 = new URL("file:/a"); 123 URL url2 = new URL("file:/b"); 124 URLClassLoader grandParent = new URLClassLoader(new URL[] {url1}, null); 125 URLClassLoader parent = new URLClassLoader(new URL[] {url2}, grandParent); 126 assertThat(ClassPath.Scanner.getClassPathEntries(new ClassLoader(parent) {})) 127 .containsExactly(new File("/a"), grandParent, new File("/b"), parent); 128 } 129 130 @AndroidIncompatible // Android forbids null parent ClassLoader testClassPathEntries_notURLClassLoader_withGrandParent()131 public void testClassPathEntries_notURLClassLoader_withGrandParent() throws Exception { 132 URL url = new URL("file:/a"); 133 URLClassLoader grandParent = new URLClassLoader(new URL[] {url}, null); 134 ClassLoader parent = new ClassLoader(grandParent) {}; 135 assertThat(ClassPath.Scanner.getClassPathEntries(new ClassLoader(parent) {})) 136 .containsExactly(new File("/a"), grandParent); 137 } 138 139 @AndroidIncompatible // Android forbids null parent ClassLoader 140 // https://github.com/google/guava/issues/2152 testClassPathEntries_URLClassLoader_pathWithSpace()141 public void testClassPathEntries_URLClassLoader_pathWithSpace() throws Exception { 142 URL url = new URL("file:///c:/Documents and Settings/"); 143 URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null); 144 assertThat(ClassPath.Scanner.getClassPathEntries(classloader)) 145 .containsExactly(new File("/c:/Documents and Settings/"), classloader); 146 } 147 148 @AndroidIncompatible // Android forbids null parent ClassLoader 149 // https://github.com/google/guava/issues/2152 testClassPathEntries_URLClassLoader_pathWithEscapedSpace()150 public void testClassPathEntries_URLClassLoader_pathWithEscapedSpace() throws Exception { 151 URL url = new URL("file:///c:/Documents%20and%20Settings/"); 152 URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null); 153 assertThat(ClassPath.Scanner.getClassPathEntries(classloader)) 154 .containsExactly(new File("/c:/Documents and Settings/"), classloader); 155 } 156 157 // https://github.com/google/guava/issues/2152 testToFile()158 public void testToFile() throws Exception { 159 assertThat(ClassPath.toFile(new URL("file:///c:/Documents%20and%20Settings/"))) 160 .isEqualTo(new File("/c:/Documents and Settings/")); 161 assertThat(ClassPath.toFile(new URL("file:///c:/Documents ~ Settings, or not/11-12 12:05"))) 162 .isEqualTo(new File("/c:/Documents ~ Settings, or not/11-12 12:05")); 163 } 164 165 // https://github.com/google/guava/issues/2152 166 @AndroidIncompatible // works in newer Android versions but fails at the version we test with testToFile_AndroidIncompatible()167 public void testToFile_AndroidIncompatible() throws Exception { 168 assertThat(ClassPath.toFile(new URL("file:///c:\\Documents ~ Settings, or not\\11-12 12:05"))) 169 .isEqualTo(new File("/c:\\Documents ~ Settings, or not\\11-12 12:05")); 170 assertThat(ClassPath.toFile(new URL("file:///C:\\Program Files\\Apache Software Foundation"))) 171 .isEqualTo(new File("/C:\\Program Files\\Apache Software Foundation/")); 172 assertThat(ClassPath.toFile(new URL("file:///C:\\\u20320 \u22909"))) // Chinese Ni Hao 173 .isEqualTo(new File("/C:\\\u20320 \u22909")); 174 } 175 176 @AndroidIncompatible // Android forbids null parent ClassLoader 177 // https://github.com/google/guava/issues/2152 testJarFileWithSpaces()178 public void testJarFileWithSpaces() throws Exception { 179 URL url = makeJarUrlWithName("To test unescaped spaces in jar file name.jar"); 180 URLClassLoader classloader = new URLClassLoader(new URL[] {url}, null); 181 assertThat(ClassPath.from(classloader).getTopLevelClasses()).isNotEmpty(); 182 } 183 testScan_classPathCycle()184 public void testScan_classPathCycle() throws IOException { 185 File jarFile = File.createTempFile("with_circular_class_path", ".jar"); 186 try { 187 writeSelfReferencingJarFile(jarFile, "test.txt"); 188 ClassPath.DefaultScanner scanner = new ClassPath.DefaultScanner(); 189 scanner.scan(jarFile, ClassPathTest.class.getClassLoader()); 190 assertThat(scanner.getResources()).hasSize(1); 191 } finally { 192 jarFile.delete(); 193 } 194 } 195 testScanFromFile_fileNotExists()196 public void testScanFromFile_fileNotExists() throws IOException { 197 ClassLoader classLoader = ClassPathTest.class.getClassLoader(); 198 ClassPath.DefaultScanner scanner = new ClassPath.DefaultScanner(); 199 scanner.scan(new File("no/such/file/anywhere"), classLoader); 200 assertThat(scanner.getResources()).isEmpty(); 201 } 202 testScanFromFile_notJarFile()203 public void testScanFromFile_notJarFile() throws IOException { 204 ClassLoader classLoader = ClassPathTest.class.getClassLoader(); 205 File notJar = File.createTempFile("not_a_jar", "txt"); 206 ClassPath.DefaultScanner scanner = new ClassPath.DefaultScanner(); 207 try { 208 scanner.scan(notJar, classLoader); 209 } finally { 210 notJar.delete(); 211 } 212 assertThat(scanner.getResources()).isEmpty(); 213 } 214 testGetClassPathEntry()215 public void testGetClassPathEntry() throws MalformedURLException, URISyntaxException { 216 assertEquals( 217 new File("/usr/test/dep.jar").toURI(), 218 ClassPath.Scanner.getClassPathEntry( 219 new File("/home/build/outer.jar"), "file:/usr/test/dep.jar") 220 .toURI()); 221 assertEquals( 222 new File("/home/build/a.jar").toURI(), 223 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "a.jar").toURI()); 224 assertEquals( 225 new File("/home/build/x/y/z").toURI(), 226 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z").toURI()); 227 assertEquals( 228 new File("/home/build/x/y/z.jar").toURI(), 229 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z.jar") 230 .toURI()); 231 assertEquals( 232 "/home/build/x y.jar", 233 ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x y.jar") 234 .getFile()); 235 } 236 testGetClassPathFromManifest_nullManifest()237 public void testGetClassPathFromManifest_nullManifest() { 238 assertThat(ClassPath.Scanner.getClassPathFromManifest(new File("some.jar"), null)).isEmpty(); 239 } 240 testGetClassPathFromManifest_noClassPath()241 public void testGetClassPathFromManifest_noClassPath() throws IOException { 242 File jarFile = new File("base.jar"); 243 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest(""))).isEmpty(); 244 } 245 testGetClassPathFromManifest_emptyClassPath()246 public void testGetClassPathFromManifest_emptyClassPath() throws IOException { 247 File jarFile = new File("base.jar"); 248 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifestClasspath(""))) 249 .isEmpty(); 250 } 251 testGetClassPathFromManifest_badClassPath()252 public void testGetClassPathFromManifest_badClassPath() throws IOException { 253 File jarFile = new File("base.jar"); 254 Manifest manifest = manifestClasspath("nosuchscheme:an_invalid^path"); 255 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)).isEmpty(); 256 } 257 testGetClassPathFromManifest_pathWithStrangeCharacter()258 public void testGetClassPathFromManifest_pathWithStrangeCharacter() throws IOException { 259 File jarFile = new File("base/some.jar"); 260 Manifest manifest = manifestClasspath("file:the^file.jar"); 261 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 262 .containsExactly(fullpath("base/the^file.jar")); 263 } 264 testGetClassPathFromManifest_relativeDirectory()265 public void testGetClassPathFromManifest_relativeDirectory() throws IOException { 266 File jarFile = new File("base/some.jar"); 267 // with/relative/directory is the Class-Path value in the mf file. 268 Manifest manifest = manifestClasspath("with/relative/dir"); 269 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 270 .containsExactly(fullpath("base/with/relative/dir")); 271 } 272 testGetClassPathFromManifest_relativeJar()273 public void testGetClassPathFromManifest_relativeJar() throws IOException { 274 File jarFile = new File("base/some.jar"); 275 // with/relative/directory is the Class-Path value in the mf file. 276 Manifest manifest = manifestClasspath("with/relative.jar"); 277 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 278 .containsExactly(fullpath("base/with/relative.jar")); 279 } 280 testGetClassPathFromManifest_jarInCurrentDirectory()281 public void testGetClassPathFromManifest_jarInCurrentDirectory() throws IOException { 282 File jarFile = new File("base/some.jar"); 283 // with/relative/directory is the Class-Path value in the mf file. 284 Manifest manifest = manifestClasspath("current.jar"); 285 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 286 .containsExactly(fullpath("base/current.jar")); 287 } 288 testGetClassPathFromManifest_absoluteDirectory()289 public void testGetClassPathFromManifest_absoluteDirectory() throws IOException { 290 File jarFile = new File("base/some.jar"); 291 Manifest manifest = manifestClasspath("file:/with/absolute/dir"); 292 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 293 .containsExactly(fullpath("/with/absolute/dir")); 294 } 295 testGetClassPathFromManifest_absoluteJar()296 public void testGetClassPathFromManifest_absoluteJar() throws IOException { 297 File jarFile = new File("base/some.jar"); 298 Manifest manifest = manifestClasspath("file:/with/absolute.jar"); 299 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 300 .containsExactly(fullpath("/with/absolute.jar")); 301 } 302 testGetClassPathFromManifest_multiplePaths()303 public void testGetClassPathFromManifest_multiplePaths() throws IOException { 304 File jarFile = new File("base/some.jar"); 305 Manifest manifest = manifestClasspath("file:/with/absolute.jar relative.jar relative/dir"); 306 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 307 .containsExactly( 308 fullpath("/with/absolute.jar"), 309 fullpath("base/relative.jar"), 310 fullpath("base/relative/dir")) 311 .inOrder(); 312 } 313 testGetClassPathFromManifest_leadingBlanks()314 public void testGetClassPathFromManifest_leadingBlanks() throws IOException { 315 File jarFile = new File("base/some.jar"); 316 Manifest manifest = manifestClasspath(" relative.jar"); 317 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 318 .containsExactly(fullpath("base/relative.jar")); 319 } 320 testGetClassPathFromManifest_trailingBlanks()321 public void testGetClassPathFromManifest_trailingBlanks() throws IOException { 322 File jarFile = new File("base/some.jar"); 323 Manifest manifest = manifestClasspath("relative.jar "); 324 assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest)) 325 .containsExactly(fullpath("base/relative.jar")); 326 } 327 testGetClassName()328 public void testGetClassName() { 329 assertEquals("abc.d.Abc", ClassPath.getClassName("abc/d/Abc.class")); 330 } 331 testResourceInfo_of()332 public void testResourceInfo_of() { 333 assertEquals(ClassInfo.class, resourceInfo(ClassPathTest.class).getClass()); 334 assertEquals(ClassInfo.class, resourceInfo(ClassPath.class).getClass()); 335 assertEquals(ClassInfo.class, resourceInfo(Nested.class).getClass()); 336 } 337 testGetSimpleName()338 public void testGetSimpleName() { 339 ClassLoader classLoader = getClass().getClassLoader(); 340 assertEquals("Foo", new ClassInfo(FILE, "Foo.class", classLoader).getSimpleName()); 341 assertEquals("Foo", new ClassInfo(FILE, "a/b/Foo.class", classLoader).getSimpleName()); 342 assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName()); 343 assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName()); 344 assertEquals("Foo", new ClassInfo(FILE, "a/b/Bar$Foo.class", classLoader).getSimpleName()); 345 assertEquals("", new ClassInfo(FILE, "a/b/Bar$1.class", classLoader).getSimpleName()); 346 assertEquals("Local", new ClassInfo(FILE, "a/b/Bar$1Local.class", classLoader).getSimpleName()); 347 } 348 testGetPackageName()349 public void testGetPackageName() { 350 assertEquals( 351 "", new ClassInfo(FILE, "Foo.class", getClass().getClassLoader()).getPackageName()); 352 assertEquals( 353 "a.b", new ClassInfo(FILE, "a/b/Foo.class", getClass().getClassLoader()).getPackageName()); 354 } 355 356 // Test that ResourceInfo.urls() returns identical content to ClassLoader.getResources() 357 testGetClassPathUrls()358 public void testGetClassPathUrls() throws Exception { 359 String oldPathSeparator = PATH_SEPARATOR.value(); 360 String oldClassPath = JAVA_CLASS_PATH.value(); 361 System.setProperty(PATH_SEPARATOR.key(), ":"); 362 System.setProperty( 363 JAVA_CLASS_PATH.key(), 364 Joiner.on(":") 365 .join( 366 "relative/path/to/some.jar", 367 "/absolute/path/to/some.jar", 368 "relative/path/to/class/root", 369 "/absolute/path/to/class/root")); 370 try { 371 ImmutableList<URL> urls = ClassPath.Scanner.parseJavaClassPath(); 372 373 assertThat(urls.get(0).getProtocol()).isEqualTo("file"); 374 assertThat(urls.get(0).getAuthority()).isNull(); 375 assertThat(urls.get(0).getPath()).endsWith("/relative/path/to/some.jar"); 376 377 assertThat(urls.get(1)).isEqualTo(new URL("file:///absolute/path/to/some.jar")); 378 379 assertThat(urls.get(2).getProtocol()).isEqualTo("file"); 380 assertThat(urls.get(2).getAuthority()).isNull(); 381 assertThat(urls.get(2).getPath()).endsWith("/relative/path/to/class/root"); 382 383 assertThat(urls.get(3)).isEqualTo(new URL("file:///absolute/path/to/class/root")); 384 385 assertThat(urls).hasSize(4); 386 } finally { 387 System.setProperty(PATH_SEPARATOR.key(), oldPathSeparator); 388 System.setProperty(JAVA_CLASS_PATH.key(), oldClassPath); 389 } 390 } 391 contentEquals(URL left, URL right)392 private static boolean contentEquals(URL left, URL right) throws IOException { 393 return Resources.asByteSource(left).contentEquals(Resources.asByteSource(right)); 394 } 395 396 private static class Nested {} 397 testNulls()398 public void testNulls() throws IOException { 399 new NullPointerTester().testAllPublicStaticMethods(ClassPath.class); 400 new NullPointerTester() 401 .testAllPublicInstanceMethods(ClassPath.from(getClass().getClassLoader())); 402 } 403 testResourceScanner()404 public void testResourceScanner() throws IOException { 405 ResourceScanner scanner = new ResourceScanner(); 406 scanner.scan(ClassLoader.getSystemClassLoader()); 407 assertThat(scanner.resources).contains("com/google/common/reflect/ClassPathTest.class"); 408 } 409 testExistsThrowsSecurityException()410 public void testExistsThrowsSecurityException() throws IOException, URISyntaxException { 411 SecurityManager oldSecurityManager = System.getSecurityManager(); 412 try { 413 doTestExistsThrowsSecurityException(); 414 } finally { 415 System.setSecurityManager(oldSecurityManager); 416 } 417 } 418 doTestExistsThrowsSecurityException()419 private void doTestExistsThrowsSecurityException() throws IOException, URISyntaxException { 420 File file = null; 421 // In Java 9, Logger may read the TZ database. Only disallow reading the class path URLs. 422 final PermissionCollection readClassPathFiles = 423 new FilePermission("", "read").newPermissionCollection(); 424 for (URL url : ClassPath.Scanner.parseJavaClassPath()) { 425 if (url.getProtocol().equalsIgnoreCase("file")) { 426 file = new File(url.toURI()); 427 readClassPathFiles.add(new FilePermission(file.getAbsolutePath(), "read")); 428 } 429 } 430 assertThat(file).isNotNull(); 431 SecurityManager disallowFilesSecurityManager = 432 new SecurityManager() { 433 @Override 434 public void checkPermission(Permission p) { 435 if (readClassPathFiles.implies(p)) { 436 throw new SecurityException("Disallowed: " + p); 437 } 438 } 439 }; 440 System.setSecurityManager(disallowFilesSecurityManager); 441 try { 442 file.exists(); 443 fail("Did not get expected SecurityException"); 444 } catch (SecurityException expected) { 445 } 446 ClassPath classPath = ClassPath.from(getClass().getClassLoader()); 447 // ClassPath may contain resources from the boot class loader; just not from the class path. 448 for (ResourceInfo resource : classPath.getResources()) { 449 assertThat(resource.getResourceName()).doesNotContain("com/google/common/reflect/"); 450 } 451 } 452 findClass( Iterable<ClassPath.ClassInfo> classes, Class<?> cls)453 private static ClassPath.ClassInfo findClass( 454 Iterable<ClassPath.ClassInfo> classes, Class<?> cls) { 455 for (ClassPath.ClassInfo classInfo : classes) { 456 if (classInfo.getName().equals(cls.getName())) { 457 return classInfo; 458 } 459 } 460 throw new AssertionError("failed to find " + cls); 461 } 462 resourceInfo(Class<?> cls)463 private static ResourceInfo resourceInfo(Class<?> cls) { 464 String resource = cls.getName().replace('.', '/') + ".class"; 465 ClassLoader loader = cls.getClassLoader(); 466 return ResourceInfo.of(FILE, resource, loader); 467 } 468 classInfo(Class<?> cls)469 private static ClassInfo classInfo(Class<?> cls) { 470 return classInfo(cls, cls.getClassLoader()); 471 } 472 classInfo(Class<?> cls, ClassLoader classLoader)473 private static ClassInfo classInfo(Class<?> cls, ClassLoader classLoader) { 474 String resource = cls.getName().replace('.', '/') + ".class"; 475 return new ClassInfo(FILE, resource, classLoader); 476 } 477 manifestClasspath(String classpath)478 private static Manifest manifestClasspath(String classpath) throws IOException { 479 return manifest("Class-Path: " + classpath + "\n"); 480 } 481 writeSelfReferencingJarFile(File jarFile, String... entries)482 private static void writeSelfReferencingJarFile(File jarFile, String... entries) 483 throws IOException { 484 Manifest manifest = new Manifest(); 485 // Without version, the manifest is silently ignored. Ugh! 486 manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); 487 manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, jarFile.getName()); 488 489 Closer closer = Closer.create(); 490 try { 491 FileOutputStream fileOut = closer.register(new FileOutputStream(jarFile)); 492 JarOutputStream jarOut = closer.register(new JarOutputStream(fileOut)); 493 for (String entry : entries) { 494 jarOut.putNextEntry(new ZipEntry(entry)); 495 Resources.copy(ClassPathTest.class.getResource(entry), jarOut); 496 jarOut.closeEntry(); 497 } 498 } catch (Throwable e) { 499 throw closer.rethrow(e); 500 } finally { 501 closer.close(); 502 } 503 } 504 manifest(String content)505 private static Manifest manifest(String content) throws IOException { 506 InputStream in = new ByteArrayInputStream(content.getBytes(US_ASCII)); 507 Manifest manifest = new Manifest(); 508 manifest.read(in); 509 return manifest; 510 } 511 fullpath(String path)512 private static File fullpath(String path) { 513 return new File(new File(path).toURI()); 514 } 515 516 private static class ResourceScanner extends ClassPath.Scanner { 517 final Set<String> resources = new HashSet<>(); 518 519 @Override scanResource(ResourceInfo resource)520 protected void scanResource(ResourceInfo resource) throws IOException { 521 resources.add(resource.getResourceName()); 522 } 523 } 524 makeJarUrlWithName(String name)525 private static URL makeJarUrlWithName(String name) throws IOException { 526 File fullPath = new File(Files.createTempDir(), name); 527 File jarFile = JarFileFinder.pickAnyJarFile(); 528 Files.copy(jarFile, fullPath); 529 return fullPath.toURI().toURL(); 530 } 531 532 private static final class JarFileFinder extends ClassPath.Scanner { 533 534 private File found; 535 pickAnyJarFile()536 static File pickAnyJarFile() throws IOException { 537 JarFileFinder finder = new JarFileFinder(); 538 try { 539 finder.scan(JarFileFinder.class.getClassLoader()); 540 throw new IllegalStateException("No jar file found!"); 541 } catch (StopScanningException expected) { 542 return finder.found; 543 } 544 } 545 546 @Override scanJarFile(ClassLoader classloader, JarFile file)547 void scanJarFile(ClassLoader classloader, JarFile file) throws IOException { 548 this.found = new File(file.getName()); 549 throw new StopScanningException(); 550 } 551 552 @Override scanResource(ResourceInfo resource)553 protected void scanResource(ResourceInfo resource) {} 554 555 // Special exception just to terminate the scanning when we get any jar file to use. 556 private static final class StopScanningException extends RuntimeException {} 557 } 558 } 559