8182742: ClassLoader.getResourceXXX throws NPE when ClassLoader created by defineModulesWithXXX
authoralanb
Wed, 06 Dec 2017 08:36:09 +0000
changeset 48076 794cbfa7a309
parent 48075 c51f9eea6d2b
child 48077 2a749b997a0d
8182742: ClassLoader.getResourceXXX throws NPE when ClassLoader created by defineModulesWithXXX Reviewed-by: redestad, mchung
src/java.base/share/classes/jdk/internal/loader/Loader.java
test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java	Wed Dec 06 08:33:04 2017 +0000
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java	Wed Dec 06 08:36:09 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@
 import java.io.File;
 import java.io.FilePermission;
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleReader;
@@ -58,12 +57,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Spliterator;
-import java.util.Spliterators;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Supplier;
 import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.Resources;
@@ -403,12 +398,15 @@
 
         // this loader
         URL url = findResource(name);
-        if (url != null) {
-            return url;
-        } else {
+        if (url == null) {
             // parent loader
-            return parent.getResource(name);
+            if (parent != null) {
+                url = parent.getResource(name);
+            } else {
+                url = BootLoader.findResource(name);
+            }
         }
+        return url;
     }
 
     @Override
@@ -419,7 +417,12 @@
         List<URL> urls = findResourcesAsList(name);
 
         // parent loader
-        Enumeration<URL> e = parent.getResources(name);
+        Enumeration<URL> e;
+        if (parent != null) {
+            e = parent.getResources(name);
+        } else {
+            e = BootLoader.findResources(name);
+        }
 
         // concat the URLs with the URLs returned by the parent
         return new Enumeration<>() {
@@ -439,25 +442,6 @@
         };
     }
 
-    @Override
-    public Stream<URL> resources(String name) {
-        Objects.requireNonNull(name);
-        // ordering not specified
-        int characteristics = (Spliterator.NONNULL | Spliterator.IMMUTABLE |
-                               Spliterator.SIZED | Spliterator.SUBSIZED);
-        Supplier<Spliterator<URL>> supplier = () -> {
-            try {
-                List<URL> urls = findResourcesAsList(name);
-                return Spliterators.spliterator(urls, characteristics);
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-        };
-        Stream<URL> s1 = StreamSupport.stream(supplier, characteristics, false);
-        Stream<URL> s2 = parent.resources(name);
-        return Stream.concat(s1, s2);
-    }
-
     /**
      * Finds the resources with the given name in this class loader.
      */
--- a/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java	Wed Dec 06 08:33:04 2017 +0000
+++ b/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java	Wed Dec 06 08:36:09 2017 +0000
@@ -52,6 +52,7 @@
 import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.stream.Collectors;
+
 import jdk.test.lib.compiler.CompilerUtils;
 
 import org.testng.annotations.BeforeTest;
@@ -78,7 +79,7 @@
      * Basic test of ModuleLayer.defineModulesWithOneLoader
      *
      * Test scenario:
-     *   m1 requires m2 and m3
+     * m1 requires m2 and m3
      */
     public void testWithOneLoader() throws Exception {
         Configuration cf = resolve("m1");
@@ -105,7 +106,7 @@
      * Basic test of ModuleLayer.defineModulesWithManyLoaders
      *
      * Test scenario:
-     *   m1 requires m2 and m3
+     * m1 requires m2 and m3
      */
     public void testWithManyLoaders() throws Exception {
         Configuration cf = resolve("m1");
@@ -136,9 +137,9 @@
      * modules is a service provider module.
      *
      * Test scenario:
-     *    m1 requires m2 and m3
-     *    m1 uses S
-     *    m4 provides S with ...
+     * m1 requires m2 and m3
+     * m1 uses S
+     * m4 provides S with ...
      */
     public void testServicesWithOneLoader() throws Exception {
         Configuration cf = resolveAndBind("m1");
@@ -175,9 +176,9 @@
      * modules is a service provider module.
      *
      * Test scenario:
-     *    m1 requires m2 and m3
-     *    m1 uses S
-     *    m4 provides S with ...
+     * m1 requires m2 and m3
+     * m1 uses S
+     * m4 provides S with ...
      */
     public void testServicesWithManyLoaders() throws Exception {
         Configuration cf = resolveAndBind("m1");
@@ -234,7 +235,7 @@
         ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent);
         testLoad(layer, cn);
 
-         // one loader with boot loader as parent
+        // one loader with boot loader as parent
         layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, null);
         testLoadFail(layer, cn);
 
@@ -252,21 +253,21 @@
      * Test defineModulesWithXXX when modules that have overlapping packages.
      *
      * Test scenario:
-     *   m1 exports p
-     *   m2 exports p
+     * m1 exports p
+     * m2 exports p
      */
     public void testOverlappingPackages() {
         ModuleDescriptor descriptor1
-            = ModuleDescriptor.newModule("m1").exports("p").build();
+                = ModuleDescriptor.newModule("m1").exports("p").build();
 
         ModuleDescriptor descriptor2
-            = ModuleDescriptor.newModule("m2").exports("p").build();
+                = ModuleDescriptor.newModule("m2").exports("p").build();
 
         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
 
         Configuration cf = ModuleLayer.boot()
-            .configuration()
-            .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2"));
+                .configuration()
+                .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2"));
 
         // cannot define both module m1 and m2 to the same class loader
         try {
@@ -284,35 +285,35 @@
      * Test ModuleLayer.defineModulesWithXXX with split delegation.
      *
      * Test scenario:
-     *   layer1: m1 exports p, m2 exports p
-     *   layer2: m3 reads m1, m4 reads m2
+     * layer1: m1 exports p, m2 exports p
+     * layer2: m3 reads m1, m4 reads m2
      */
     public void testSplitDelegation() {
         ModuleDescriptor descriptor1
-            = ModuleDescriptor.newModule("m1").exports("p").build();
+                = ModuleDescriptor.newModule("m1").exports("p").build();
 
         ModuleDescriptor descriptor2
-            = ModuleDescriptor.newModule("m2").exports("p").build();
+                = ModuleDescriptor.newModule("m2").exports("p").build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 
         Configuration cf1 = ModuleLayer.boot()
-            .configuration()
-            .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2"));
+                .configuration()
+                .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2"));
 
         ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null);
         checkLayer(layer1, "m1", "m2");
 
         ModuleDescriptor descriptor3
-            = ModuleDescriptor.newModule("m3").requires("m1").build();
+                = ModuleDescriptor.newModule("m3").requires("m1").build();
 
         ModuleDescriptor descriptor4
-            = ModuleDescriptor.newModule("m4").requires("m2").build();
+                = ModuleDescriptor.newModule("m4").requires("m2").build();
 
         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4);
 
         Configuration cf2 = cf1.resolve(finder2, ModuleFinder.of(),
-                                                Set.of("m3", "m4"));
+                Set.of("m3", "m4"));
 
         // package p cannot be supplied by two class loaders
         try {
@@ -331,8 +332,8 @@
      * named modules in the parent layer.
      *
      * Test scenario:
-     *   layer1: m1, m2, m3 => same loader
-     *   layer2: m1, m2, m4 => same loader
+     * layer1: m1, m2, m3 => same loader
+     * layer2: m1, m2, m4 => same loader
      */
     public void testOverriding1() throws Exception {
         Configuration cf1 = resolve("m1");
@@ -342,7 +343,7 @@
 
         ModuleFinder finder = ModuleFinder.of(MODS_DIR);
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null);
         checkLayer(layer2, "m1", "m2", "m3");
@@ -378,8 +379,8 @@
      * named modules in the parent layer.
      *
      * Test scenario:
-     *   layer1: m1, m2, m3 => loader pool
-     *   layer2: m1, m2, m3 => loader pool
+     * layer1: m1, m2, m3 => loader pool
+     * layer2: m1, m2, m3 => loader pool
      */
     public void testOverriding2() throws Exception {
         Configuration cf1 = resolve("m1");
@@ -389,7 +390,7 @@
 
         ModuleFinder finder = ModuleFinder.of(MODS_DIR);
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null);
         checkLayer(layer2, "m1", "m2", "m3");
@@ -482,7 +483,7 @@
         ModuleFinder finder = finderFor("m1", "m3");
 
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null);
         checkLayer(layer2, "m1", "m3");
@@ -517,7 +518,7 @@
         ModuleFinder finder = finderFor("m1", "m3");
 
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null);
         checkLayer(layer2, "m1", "m3");
@@ -550,18 +551,27 @@
         assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6);
     }
 
-
     /**
      * Basic test for locating resources with a class loader created by
      * defineModulesWithOneLoader.
      */
     public void testResourcesWithOneLoader() throws Exception {
+        testResourcesWithOneLoader(ClassLoader.getSystemClassLoader());
+        testResourcesWithOneLoader(null);
+    }
+
+    /**
+     * Test locating resources with the class loader created by
+     * defineModulesWithOneLoader. The class loader has the given class
+     * loader as its parent.
+     */
+    void testResourcesWithOneLoader(ClassLoader parent) throws Exception {
         Configuration cf = resolve("m1");
-        ClassLoader scl = ClassLoader.getSystemClassLoader();
-        ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl);
+        ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent);
 
         ClassLoader loader = layer.findLoader("m1");
         assertNotNull(loader);
+        assertTrue(loader.getParent() == parent);
 
         // check that getResource and getResources are consistent
         URL url1 = loader.getResource("module-info.class");
@@ -607,14 +617,24 @@
      * defineModulesWithManyLoaders.
      */
     public void testResourcesWithManyLoaders() throws Exception {
+        testResourcesWithManyLoaders(ClassLoader.getSystemClassLoader());
+        testResourcesWithManyLoaders(null);
+    }
+
+    /**
+     * Test locating resources with class loaders created by
+     * defineModulesWithManyLoaders. The class loaders have the given class
+     * loader as their parent.
+     */
+    void testResourcesWithManyLoaders(ClassLoader parent) throws Exception {
         Configuration cf = resolve("m1");
-        ClassLoader scl = ClassLoader.getSystemClassLoader();
-        ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl);
+        ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, parent);
 
         for (Module m : layer.modules()) {
             String name = m.getName();
             ClassLoader loader = m.getClassLoader();
             assertNotNull(loader);
+            assertTrue(loader.getParent() == parent);
 
             // getResource should find the module-info.class for the module
             URL url = loader.getResource("module-info.class");