XBEAN-312 ensure we dont list mjar jars twice

git-svn-id: https://svn.apache.org/repos/asf/geronimo/xbean/trunk@1842433 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/xbean-finder/src/main/java/org/apache/xbean/finder/ClassLoaders.java b/xbean-finder/src/main/java/org/apache/xbean/finder/ClassLoaders.java
index 4468b8e..3e181d6 100644
--- a/xbean-finder/src/main/java/org/apache/xbean/finder/ClassLoaders.java
+++ b/xbean-finder/src/main/java/org/apache/xbean/finder/ClassLoaders.java
@@ -16,8 +16,6 @@
  */
 package org.apache.xbean.finder;
 
-import org.apache.xbean.finder.util.Files;
-
 import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -28,12 +26,16 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.xbean.finder.util.Files;
 
 public final class ClassLoaders {
     private static final boolean DONT_USE_GET_URLS = Boolean.getBoolean("xbean.finder.use.get-resources");
     private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader();
 
     private static final boolean UNIX = !System.getProperty("os.name").toLowerCase().contains("win");
+    private static final Pattern MJAR_PATTERN = Pattern.compile(".*/META-INF/versions/[0-9]+/$");
 
     public static Set<URL> findUrls(final ClassLoader classLoader) throws IOException {
         if (classLoader == null || (SYSTEM.getParent() != null && classLoader == SYSTEM.getParent())) {
@@ -115,7 +117,14 @@
             final String externalForm = url.toExternalForm();
             set.add(new URL(externalForm.substring(0, externalForm.lastIndexOf("META-INF"))));
         }
-        set.addAll(Collections.list(classLoader.getResources("")));
+        for (final URL url : Collections.list(classLoader.getResources(""))) {
+            final String externalForm = url.toExternalForm();
+            if (MJAR_PATTERN.matcher(externalForm).matches()) {
+                set.add(new URL(externalForm.substring(0, externalForm.lastIndexOf("META-INF"))));
+            } else {
+                set.add(url);
+            }
+        }
         return set;
     }
 
diff --git a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MJarJarArchiveTest.java b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MJarJarArchiveTest.java
index 2863dc3..a806400 100644
--- a/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MJarJarArchiveTest.java
+++ b/xbean-finder/src/test/java/org/apache/xbean/finder/archive/MJarJarArchiveTest.java
@@ -18,8 +18,9 @@
 
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
 import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
 import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
 import static org.objectweb.asm.Opcodes.ALOAD;
@@ -34,16 +35,20 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
 
 import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.ClassLoaders;
 import org.apache.xbean.finder.util.IOUtil;
-import org.junit.Assume;
 import org.junit.ClassRule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -95,29 +100,29 @@
                     jarOS.closeEntry();
                 }
                 {
+                    jarOS.putNextEntry(new JarEntry("META-INF/"));
+                    jarOS.closeEntry();
+                }
+                {
+                    jarOS.putNextEntry(new JarEntry("META-INF/versions/"));
+                    jarOS.closeEntry();
+                }
+                {
+                    jarOS.putNextEntry(new JarEntry("META-INF/versions/" + version + "/"));
+                    jarOS.closeEntry();
+                }
+                {
+                    jarOS.putNextEntry(new JarEntry("META-INF/versions/9/"));
+                    jarOS.closeEntry();
+                }
+                {
                     jarOS.putNextEntry(new JarEntry("META-INF/versions/" + version + "/org/test/Foo.class"));
-
-                    final ClassWriter writer = new ClassWriter(COMPUTE_FRAMES);
-                    writer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "org/test/Foo", null, Type.getInternalName(Object.class), new String[0]);
-
-                    final MethodVisitor constructor = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
-                    constructor.visitCode();
-                    constructor.visitVarInsn(ALOAD, 0);
-                    constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
-                    constructor.visitInsn(RETURN);
-                    constructor.visitMaxs(1, 1);
-                    constructor.visitEnd();
-
-                    final MethodVisitor run = writer.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
-                    run.visitAnnotation(Type.getDescriptor(Marker.class), true).visitEnd();
-                    run.visitCode();
-                    run.visitInsn(RETURN);
-                    run.visitMaxs(-1, -1);
-                    run.visitEnd();
-
-                    writer.visitEnd();
-
-                    jarOS.write(writer.toByteArray());
+                    jarOS.write(createFooClazz().toByteArray());
+                    jarOS.closeEntry();
+                }
+                {
+                    jarOS.putNextEntry(new JarEntry("META-INF/versions/9/org/test/Foo.class"));
+                    jarOS.write(createFooClazz().toByteArray());
                     jarOS.closeEntry();
                 }
             } finally {
@@ -139,8 +144,63 @@
 
     @Test
     public void testGetBytecode() throws Exception {
-        Assume.assumeFalse(System.getProperty("java.version", "1").startsWith("1"));
-        final URLClassLoader loader = new URLClassLoader(new URL[]{jar.toURI().toURL()}, Thread.currentThread().getContextClassLoader()) {
+        ensureJava9OrLater();
+        final URLClassLoader loader = newMJarClassLoader();
+        final JarArchive archive = new JarArchive(loader, jar.toURI().toURL());
+        final AnnotationFinder finder = new AnnotationFinder(archive, true);
+        assertEquals(1, finder.findAnnotatedMethods(Marker.class).size());
+        if (Closeable.class.isInstance(loader)) {
+            Closeable.class.cast(loader).close();
+        }
+    }
+
+    @Test
+    public void classLoaderScanningOneUrl() throws Exception {
+        ensureJava9OrLater();
+        final URLClassLoader loader = newMJarClassLoader();
+        final Set<URL> urls = ClassLoaders.findUrlFromResources(loader);
+        final Collection<String> testUrls = new ArrayList<String>();
+        for (final URL u : urls) {
+            final String str = u.toExternalForm();
+            if (str.contains("org.apache.xbean.finder.archive.MJarJarArchiveTest")) {
+                testUrls.add(str);
+            }
+        }
+        assertEquals(1, testUrls.size());
+        if (Closeable.class.isInstance(loader)) {
+            Closeable.class.cast(loader).close();
+        }
+    }
+
+    private void ensureJava9OrLater() {
+        assumeTrue(!System.getProperty("java.version", "1").startsWith("1."));
+    }
+
+    private static ClassWriter createFooClazz() {
+        final ClassWriter writer = new ClassWriter(COMPUTE_FRAMES);
+        writer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "org/test/Foo", null, Type.getInternalName(Object.class), new String[0]);
+
+        final MethodVisitor constructor = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        constructor.visitCode();
+        constructor.visitVarInsn(ALOAD, 0);
+        constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+        constructor.visitInsn(RETURN);
+        constructor.visitMaxs(1, 1);
+        constructor.visitEnd();
+
+        final MethodVisitor run = writer.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
+        run.visitAnnotation(Type.getDescriptor(Marker.class), true).visitEnd();
+        run.visitCode();
+        run.visitInsn(RETURN);
+        run.visitMaxs(-1, -1);
+        run.visitEnd();
+
+        writer.visitEnd();
+        return writer;
+    }
+
+    private URLClassLoader newMJarClassLoader() throws MalformedURLException {
+        return new URLClassLoader(new URL[]{jar.toURI().toURL()}, Thread.currentThread().getContextClassLoader()) {
 
             @Override
             protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
@@ -169,12 +229,6 @@
                 return super.loadClass(name, resolve);
             }
         };
-        final JarArchive archive = new JarArchive(loader, jar.toURI().toURL());
-        final AnnotationFinder finder = new AnnotationFinder(archive, true);
-        assertEquals(1, finder.findAnnotatedMethods(Marker.class).size());
-        if (Closeable.class.isInstance(loader)) {
-            Closeable.class.cast(loader).close();
-        }
     }
 
     @Retention(RUNTIME)