From 1864180ea75d4760f208c10eb9d15220f1a70878 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 28 Oct 2013 08:08:11 -0500 Subject: [PATCH 1/5] Teach Finder to find more than just the first matching element When the system class path contains more than one .jar, it is quite concievable that, say, 'META-INF/MANIFEST.MF' can be found in multiple class path elements. This commit teaches the working horse of class path inspection, the Finder class, how to continue the search at a given state. Signed-off-by: Johannes Schindelin --- src/avian/finder.h | 1 + src/finder.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/avian/finder.h b/src/avian/finder.h index cf92044b72..c7fdbd547b 100644 --- a/src/avian/finder.h +++ b/src/avian/finder.h @@ -168,6 +168,7 @@ class Finder { unsigned* length, bool tryDirectory = false) = 0; virtual const char* urlPrefix(const char* name) = 0; + virtual const char* nextUrlPrefix(const char* name, void *&finderElementPtr) = 0; virtual const char* sourceUrl(const char* name) = 0; virtual const char* path() = 0; virtual void dispose() = 0; diff --git a/src/finder.cpp b/src/finder.cpp index b170abc6e7..27969321ba 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -908,7 +908,16 @@ class MyFinder: public Finder { } virtual const char* urlPrefix(const char* name) { - for (Element* e = path_; e; e = e->next) { + void *finderElementPtr = NULL; + return nextUrlPrefix(name, finderElementPtr); + } + + virtual const char* nextUrlPrefix(const char* name, + void *&finderElementPtr) + { + Element *&e = reinterpret_cast(finderElementPtr); + e = e ? e->next : path_; + for (; e; e = e->next) { unsigned length; System::FileType type = e->stat(name, &length, true); if (type != System::TypeDoesNotExist) { From 0602d4a44705dbdf8f13c1c2572b9a0beb019dd0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 28 Oct 2013 08:52:58 -0500 Subject: [PATCH 2/5] Consider all class path elements in SystemClassLoader#findResources The findResources method is supposed to enumerate all the class path elements' matching paths' URLs, but we used to stop at the first one. While this is good enough when the system class path contains only a single .jar file, since b88438d2(sketch of JAR support in Finder) supports more than a single .jar file in the class path. Signed-off-by: Johannes Schindelin --- classpath/avian/SystemClassLoader.java | 44 ++++++++++++++++++++++---- src/builtin.cpp | 24 ++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index 71759c7a54..afdee82ed3 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -17,6 +17,7 @@ import java.util.Collection; import java.util.Collections; import java.util.ArrayList; import java.util.Enumeration; +import java.util.NoSuchElementException; public class SystemClassLoader extends ClassLoader { private native VMClass findVMClass(String name) @@ -114,12 +115,43 @@ public class SystemClassLoader extends ClassLoader { return Collections.enumeration(urls); } - protected Enumeration findResources(String name) { - Collection urls = new ArrayList(1); - URL url = findResource(name); - if (url != null) { - urls.add(url); + private class ResourceEnumeration implements Enumeration { + private long[] finderElementPtrPtr; + private String name, urlPrefix; + + public ResourceEnumeration(String name) { + this.name = name; + finderElementPtrPtr = new long[1]; + urlPrefix = nextResourceURLPrefix(); } - return Collections.enumeration(urls); + + private native String nextResourceURLPrefix(SystemClassLoader loader, + String name, long[] finderElementPtrPtr); + + private String nextResourceURLPrefix() { + return nextResourceURLPrefix(SystemClassLoader.this, name, + finderElementPtrPtr); + } + + public boolean hasMoreElements() { + return urlPrefix != null; + } + + public URL nextElement() { + if (urlPrefix == null) throw new NoSuchElementException(); + URL result; + try { + result = new URL(urlPrefix + name); + } catch (MalformedURLException ignored) { + result = null; + } + if (finderElementPtrPtr[0] == 0l) urlPrefix = null; + else urlPrefix = nextResourceURLPrefix(); + return result; + } + } + + protected Enumeration findResources(String name) { + return new ResourceEnumeration(name); } } diff --git a/src/builtin.cpp b/src/builtin.cpp index f7f9a28d53..7fda369dbf 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -152,6 +152,30 @@ Avian_avian_SystemClassLoader_resourceURLPrefix } } +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_00024ResourceEnumeration_nextResourceURLPrefix +(Thread* t, object, uintptr_t* arguments) +{ + object loader = reinterpret_cast(arguments[1]); + object name = reinterpret_cast(arguments[2]); + object finderElementPtrPtr = reinterpret_cast(arguments[3]); + + if (LIKELY(name) && LIKELY(finderElementPtrPtr)) { + THREAD_RUNTIME_ARRAY(t, char, n, stringLength(t, name) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + void *&finderElementPtr = reinterpret_cast(longArrayBody(t, + finderElementPtrPtr, 0)); + const char* name = static_cast + (systemClassLoaderFinder(t, loader))->nextUrlPrefix(RUNTIME_ARRAY_BODY(n), + finderElementPtr); + + return name ? reinterpret_cast(makeString(t, "%s", name)) : 0; + } else { + throwNew(t, Machine::NullPointerExceptionType); + } +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_SystemClassLoader_getClass (Thread* t, object, uintptr_t* arguments) From 167688eabdd10ad91143cb0baf113e2288b2b408 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 28 Oct 2013 08:53:22 -0500 Subject: [PATCH 3/5] Teach SystemClassLoader#getResources to not stop at the first match The getResources method can be used to find all matches in the class path for a given path, e.g. to seek out all the META-INF/MANIFEST.MF files contained in all of the .jar files in the class path. We just taught the findResources() method to return all matches (rather than only the first), so let's use that method to get all the matches from the current class loader's class path elements. Signed-off-by: Johannes Schindelin --- classpath/avian/SystemClassLoader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index afdee82ed3..ce9cdcd7a3 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -107,9 +107,9 @@ public class SystemClassLoader extends ClassLoader { } } - URL url = findResource(name); - if (url != null) { - urls.add(url); + Enumeration urls2 = findResources(name); + while (urls2.hasMoreElements()) { + urls.add(urls2.nextElement()); } return Collections.enumeration(urls); From a2feec0babedda2d2482c3caaee5ab5867691d8b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 4 Nov 2013 15:28:19 -0600 Subject: [PATCH 4/5] Add a pseudo-integration test for getResources() This adds an extra class path element to the VM running the unit tests, writes files with identical file names into both directories and then verifies that SystemClassLoader#getResources can find them. Signed-off-by: Johannes Schindelin --- makefile | 11 +++++++++-- test/Misc.java | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index ae3da55383..4eaeafa092 100755 --- a/makefile +++ b/makefile @@ -1474,13 +1474,20 @@ ifeq ($(continuations),true) $(build)/compile-x86-asm.o: $(src)/continuations-x86.$(asm-format) endif -$(build)/run-tests.sh: $(test-classes) makefile +$(build)/run-tests.sh: $(test-classes) makefile $(build)/extra-dir/multi-classpath-test.txt $(build)/test/multi-classpath-test.txt echo 'cd $$(dirname $$0)' > $(@) echo "sh ./test.sh 2>/dev/null \\" >> $(@) - echo "$(shell echo $(library-path) | sed 's|$(build)|\.|g') ./$(name)-unittest${exe-suffix} ./$(notdir $(test-executable)) $(mode) \"-Djava.library.path=. -cp test\" \\" >> $(@) + echo "$(shell echo $(library-path) | sed 's|$(build)|\.|g') ./$(name)-unittest${exe-suffix} ./$(notdir $(test-executable)) $(mode) \"-Djava.library.path=. -cp test:extra-dir\" \\" >> $(@) echo "$(call class-names,$(test-build),$(filter-out $(test-support-classes), $(test-classes))) \\" >> $(@) echo "$(continuation-tests) $(tail-tests)" >> $(@) +$(build)/extra-dir/multi-classpath-test.txt: + mkdir -p $(build)/extra-dir + echo "$@" > $@ + +$(build)/test/multi-classpath-test.txt: + echo "$@" > $@ + $(build)/test.sh: $(test)/test.sh cp $(<) $(@) diff --git a/test/Misc.java b/test/Misc.java index 91195d5f13..b0fde2d44b 100644 --- a/test/Misc.java +++ b/test/Misc.java @@ -1,3 +1,7 @@ +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; + public class Misc { private static class μClass { public int μField; @@ -262,6 +266,27 @@ public class Misc { expect((Protected.class.getModifiers() & java.lang.reflect.Modifier.PUBLIC) == 0); + + try { + int count = 0; + boolean test = false, extraDir = false; + ClassLoader loader = Misc.class.getClassLoader(); + Enumeration resources = loader.getResources("multi-classpath-test.txt"); + while (resources.hasMoreElements()) { + ++count; + String url = resources.nextElement().toString(); + if (url.contains("extra-dir")) { + extraDir = true; + } else if (url.contains("test")) { + test = true; + } + } + expect(count == 2); + expect(test); + expect(extraDir); + } catch (IOException e) { + throw new RuntimeException(e); + } } protected class Protected { } From 002b0db43a61a5cda48d6cab51cc1740935fb917 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 26 Oct 2013 15:47:51 -0500 Subject: [PATCH 5/5] Do not report jar: URLs as jar://file:... Signed-off-by: Johannes Schindelin --- classpath/avian/jar/Handler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classpath/avian/jar/Handler.java b/classpath/avian/jar/Handler.java index 3b5f64fd5f..726f12e413 100644 --- a/classpath/avian/jar/Handler.java +++ b/classpath/avian/jar/Handler.java @@ -42,7 +42,7 @@ public class Handler extends URLStreamHandler { ("protocol " + file.getProtocol() + " not yet supported"); } - url.set("jar", "", -1, s, null); + url.set("jar", null, -1, s, null); } private static class MyJarURLConnection extends JarURLConnection {