Skip to content

Commit

Permalink
use Map<File, ResourceInfo> instead of Map<URI, ResourceInfo> to work…
Browse files Browse the repository at this point in the history
… around the problem mentioned in #1899

-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=81339582
  • Loading branch information
benyu authored and cpovirk committed Dec 5, 2014
1 parent e152cfd commit 81b23cd
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 65 deletions.
75 changes: 45 additions & 30 deletions guava-tests/test/com/google/common/reflect/ClassPathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
Expand Down Expand Up @@ -177,7 +177,7 @@ public void testClassPathEntries_URLClassLoader_noParent() throws Exception {
URL url2 = new URL("file:/b");
URLClassLoader classloader = new URLClassLoader(new URL[] {url1, url2}, null);
assertEquals(
ImmutableMap.of(url1.toURI(), classloader, url2.toURI(), classloader),
ImmutableMap.of(new File("/a"), classloader, new File("/b"), classloader),
ClassPath.getClassPathEntries(classloader));
}

Expand All @@ -186,16 +186,16 @@ public void testClassPathEntries_URLClassLoader_withParent() throws Exception {
URL url2 = new URL("file:/b");
URLClassLoader parent = new URLClassLoader(new URL[] {url1}, null);
URLClassLoader child = new URLClassLoader(new URL[] {url2}, parent) {};
ImmutableMap<URI, ClassLoader> classPathEntries = ClassPath.getClassPathEntries(child);
assertEquals(ImmutableMap.of(url1.toURI(), parent, url2.toURI(), child), classPathEntries);
assertThat(classPathEntries.keySet()).containsExactly(url1.toURI(), url2.toURI()).inOrder();
ImmutableMap<File, ClassLoader> classPathEntries = ClassPath.getClassPathEntries(child);
assertEquals(ImmutableMap.of(new File("/a"), parent, new File("/b"), child), classPathEntries);
assertThat(classPathEntries.keySet()).containsExactly(new File("/a"), new File("/b")).inOrder();
}

public void testClassPathEntries_duplicateUri_parentWins() throws Exception {
URL url = new URL("file:/a");
URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
URLClassLoader child = new URLClassLoader(new URL[] {url}, parent) {};
assertEquals(ImmutableMap.of(url.toURI(), parent), ClassPath.getClassPathEntries(child));
assertEquals(ImmutableMap.of(new File("/a"), parent), ClassPath.getClassPathEntries(child));
}

public void testClassPathEntries_notURLClassLoader_noParent() {
Expand All @@ -206,7 +206,7 @@ public void testClassPathEntries_notURLClassLoader_withParent() throws Exception
URL url = new URL("file:/a");
URLClassLoader parent = new URLClassLoader(new URL[] {url}, null);
assertEquals(
ImmutableMap.of(url.toURI(), parent),
ImmutableMap.of(new File("/a"), parent),
ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
}

Expand All @@ -216,7 +216,7 @@ public void testClassPathEntries_notURLClassLoader_withParentAndGrandParent() th
URLClassLoader grandParent = new URLClassLoader(new URL[] {url1}, null);
URLClassLoader parent = new URLClassLoader(new URL[] {url2}, grandParent);
assertEquals(
ImmutableMap.of(url1.toURI(), grandParent, url2.toURI(), parent),
ImmutableMap.of(new File("/a"), grandParent, new File("/b"), parent),
ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
}

Expand All @@ -225,7 +225,7 @@ public void testClassPathEntries_notURLClassLoader_withGrandParent() throws Exce
URLClassLoader grandParent = new URLClassLoader(new URL[] {url}, null);
ClassLoader parent = new ClassLoader(grandParent) {};
assertEquals(
ImmutableMap.of(url.toURI(), grandParent),
ImmutableMap.of(new File("/a"), grandParent),
ClassPath.getClassPathEntries(new ClassLoader(parent) {}));
}

Expand All @@ -234,7 +234,7 @@ public void testScan_classPathCycle() throws IOException {
try {
writeSelfReferencingJarFile(jarFile, "test.txt");
ClassPath.Scanner scanner = new ClassPath.Scanner();
scanner.scan(jarFile.toURI(), ClassPathTest.class.getClassLoader());
scanner.scan(jarFile, ClassPathTest.class.getClassLoader());
assertEquals(1, scanner.getResources().size());
} finally {
jarFile.delete();
Expand All @@ -260,16 +260,20 @@ public void testScanFromFile_notJarFile() throws IOException {
assertThat(scanner.getResources()).isEmpty();
}

public void testGetClassPathEntry() throws URISyntaxException {
assertEquals(URI.create("file:/usr/test/dep.jar"),
public void testGetClassPathEntry() throws MalformedURLException, URISyntaxException {
assertEquals(new File("/usr/test/dep.jar").toURI(),
ClassPath.Scanner.getClassPathEntry(
new File("/home/build/outer.jar"), "file:/usr/test/dep.jar"));
assertEquals(URI.create("file:/home/build/a.jar"),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "a.jar"));
assertEquals(URI.create("file:/home/build/x/y/z"),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z"));
assertEquals(URI.create("file:/home/build/x/y/z.jar"),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z.jar"));
new File("/home/build/outer.jar"), "file:/usr/test/dep.jar").toURI());
assertEquals(new File("/home/build/a.jar").toURI(),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "a.jar").toURI());
assertEquals(new File("/home/build/x/y/z").toURI(),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z").toURI());
assertEquals(new File("/home/build/x/y/z.jar").toURI(),
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x/y/z.jar")
.toURI());
assertEquals("/home/build/x y.jar",
ClassPath.Scanner.getClassPathEntry(new File("/home/build/outer.jar"), "x y.jar")
.getFile());
}

public void testGetClassPathFromManifest_nullManifest() {
Expand All @@ -290,72 +294,79 @@ public void testGetClassPathFromManifest_emptyClassPath() throws IOException {

public void testGetClassPathFromManifest_badClassPath() throws IOException {
File jarFile = new File("base.jar");
Manifest manifest = manifestClasspath("an_invalid^path");
Manifest manifest = manifestClasspath("nosuchscheme:an_invalid^path");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.isEmpty();
}

public void testGetClassPathFromManifest_pathWithStrangeCharacter() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath("file:the^file.jar");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(fullpath("base/the^file.jar"));
}

public void testGetClassPathFromManifest_relativeDirectory() throws IOException {
File jarFile = new File("base/some.jar");
// with/relative/directory is the Class-Path value in the mf file.
Manifest manifest = manifestClasspath("with/relative/dir");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("base/with/relative/dir").toURI());
.containsExactly(fullpath("base/with/relative/dir"));
}

public void testGetClassPathFromManifest_relativeJar() throws IOException {
File jarFile = new File("base/some.jar");
// with/relative/directory is the Class-Path value in the mf file.
Manifest manifest = manifestClasspath("with/relative.jar");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("base/with/relative.jar").toURI());
.containsExactly(fullpath("base/with/relative.jar"));
}

public void testGetClassPathFromManifest_jarInCurrentDirectory() throws IOException {
File jarFile = new File("base/some.jar");
// with/relative/directory is the Class-Path value in the mf file.
Manifest manifest = manifestClasspath("current.jar");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("base/current.jar").toURI());
.containsExactly(fullpath("base/current.jar"));
}

public void testGetClassPathFromManifest_absoluteDirectory() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath("file:/with/absolute/dir");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("/with/absolute/dir").toURI());
.containsExactly(fullpath("/with/absolute/dir"));
}

public void testGetClassPathFromManifest_absoluteJar() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath("file:/with/absolute.jar");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("/with/absolute.jar").toURI());
.containsExactly(fullpath("/with/absolute.jar"));
}

public void testGetClassPathFromManifest_multiplePaths() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath("file:/with/absolute.jar relative.jar relative/dir");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(
new File("/with/absolute.jar").toURI(),
new File("base/relative.jar").toURI(),
new File("base/relative/dir").toURI())
fullpath("/with/absolute.jar"),
fullpath("base/relative.jar"),
fullpath("base/relative/dir"))
.inOrder();
}

public void testGetClassPathFromManifest_leadingBlanks() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath(" relative.jar");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("base/relative.jar").toURI());
.containsExactly(fullpath("base/relative.jar"));
}

public void testGetClassPathFromManifest_trailingBlanks() throws IOException {
File jarFile = new File("base/some.jar");
Manifest manifest = manifestClasspath("relative.jar ");
assertThat(ClassPath.Scanner.getClassPathFromManifest(jarFile, manifest))
.containsExactly(new File("base/relative.jar").toURI());
.containsExactly(fullpath("base/relative.jar"));
}

public void testGetClassName() {
Expand Down Expand Up @@ -456,4 +467,8 @@ private static Manifest manifest(String content) throws IOException {
manifest.read(in);
return manifest;
}

private static File fullpath(String path) {
return new File(new File(path).toURI());
}
}
64 changes: 29 additions & 35 deletions guava/src/com/google/common/reflect/ClassPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
Expand Down Expand Up @@ -88,7 +87,7 @@ private ClassPath(ImmutableSet<ResourceInfo> resources) {
*/
public static ClassPath from(ClassLoader classloader) throws IOException {
Scanner scanner = new Scanner();
for (Map.Entry<URI, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
for (Map.Entry<File, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) {
scanner.scan(entry.getKey(), entry.getValue());
}
return new ClassPath(scanner.getResources());
Expand Down Expand Up @@ -275,9 +274,9 @@ public Class<?> load() {
}
}

@VisibleForTesting static ImmutableMap<URI, ClassLoader> getClassPathEntries(
@VisibleForTesting static ImmutableMap<File, ClassLoader> getClassPathEntries(
ClassLoader classloader) {
LinkedHashMap<URI, ClassLoader> entries = Maps.newLinkedHashMap();
LinkedHashMap<File, ClassLoader> entries = Maps.newLinkedHashMap();
// Search parent first, since it's the order ClassLoader#loadClass() uses.
ClassLoader parent = classloader.getParent();
if (parent != null) {
Expand All @@ -286,14 +285,11 @@ public Class<?> load() {
if (classloader instanceof URLClassLoader) {
URLClassLoader urlClassLoader = (URLClassLoader) classloader;
for (URL entry : urlClassLoader.getURLs()) {
URI uri;
try {
uri = entry.toURI();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
if (!entries.containsKey(uri)) {
entries.put(uri, classloader);
if (entry.getProtocol().equals("file")) {
File file = new File(entry.getFile());
if (!entries.containsKey(file)) {
entries.put(file, classloader);
}
}
}
}
Expand All @@ -304,15 +300,15 @@ public Class<?> load() {

private final ImmutableSortedSet.Builder<ResourceInfo> resources =
new ImmutableSortedSet.Builder<ResourceInfo>(Ordering.usingToString());
private final Set<URI> scannedUris = Sets.newHashSet();
private final Set<File> scannedUris = Sets.newHashSet();

ImmutableSortedSet<ResourceInfo> getResources() {
return resources.build();
}

void scan(URI uri, ClassLoader classloader) throws IOException {
if (uri.getScheme().equals("file") && scannedUris.add(uri)) {
scanFrom(new File(uri), classloader);
void scan(File file, ClassLoader classloader) throws IOException {
if (scannedUris.add(file)) {
scanFrom(file, classloader);
}
}

Expand Down Expand Up @@ -372,8 +368,8 @@ private void scanJar(File file, ClassLoader classloader) throws IOException {
return;
}
try {
for (URI uri : getClassPathFromManifest(file, jarFile.getManifest())) {
scan(uri, classloader);
for (File path : getClassPathFromManifest(file, jarFile.getManifest())) {
scan(path, classloader);
}
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
Expand All @@ -392,48 +388,46 @@ private void scanJar(File file, ClassLoader classloader) throws IOException {

/**
* Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according
* to <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
* to
* <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">
* JAR File Specification</a>. If {@code manifest} is null, it means the jar file has no
* manifest, and an empty set will be returned.
*/
@VisibleForTesting static ImmutableSet<URI> getClassPathFromManifest(
@VisibleForTesting static ImmutableSet<File> getClassPathFromManifest(
File jarFile, @Nullable Manifest manifest) {
if (manifest == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<URI> builder = ImmutableSet.builder();
ImmutableSet.Builder<File> builder = ImmutableSet.builder();
String classpathAttribute = manifest.getMainAttributes()
.getValue(Attributes.Name.CLASS_PATH.toString());
if (classpathAttribute != null) {
for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) {
URI uri;
URL url;
try {
uri = getClassPathEntry(jarFile, path);
} catch (URISyntaxException e) {
url = getClassPathEntry(jarFile, path);
} catch (MalformedURLException e) {
// Ignore bad entry
logger.warning("Invalid Class-Path entry: " + path);
continue;
}
builder.add(uri);
if (url.getProtocol().equals("file")) {
builder.add(new File(url.getFile()));
}
}
}
return builder.build();
}

/**
* Returns the absolute uri of the Class-Path entry value as specified in
* <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes">
* <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Main_Attributes">
* JAR File Specification</a>. Even though the specification only talks about relative urls,
* absolute urls are actually supported too (for example, in Maven surefire plugin).
*/
@VisibleForTesting static URI getClassPathEntry(File jarFile, String path)
throws URISyntaxException {
URI uri = new URI(path);
if (uri.isAbsolute()) {
return uri;
} else {
return new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI();
}
@VisibleForTesting static URL getClassPathEntry(File jarFile, String path)
throws MalformedURLException {
return new URL(jarFile.toURI().toURL(), path);
}
}

Expand Down

0 comments on commit 81b23cd

Please sign in to comment.