8211825: ModuleLayer.defineModulesWithXXX does not setup delegation when module reads automatic module
authoralanb
Tue, 09 Oct 2018 07:06:32 +0100
changeset 52050 a42c378b6f01
parent 52049 a986ec4ff214
child 52051 6ee9500fe653
8211825: ModuleLayer.defineModulesWithXXX does not setup delegation when module reads automatic module Reviewed-by: mchung
src/java.base/share/classes/java/lang/Module.java
src/java.base/share/classes/jdk/internal/loader/Loader.java
src/java.base/share/classes/jdk/internal/loader/LoaderPool.java
test/jdk/java/lang/ModuleLayer/automatic/AutomaticModulesTest.java
test/jdk/java/lang/ModuleLayer/automatic/src/alib/q/Lib.java
test/jdk/java/lang/ModuleLayer/automatic/src/m/module-info.java
test/jdk/java/lang/ModuleLayer/automatic/src/m/p/Main.java
--- a/src/java.base/share/classes/java/lang/Module.java	Mon Oct 08 19:44:44 2018 -0700
+++ b/src/java.base/share/classes/java/lang/Module.java	Tue Oct 09 07:06:32 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -477,7 +477,7 @@
      *
      * @see ModuleDescriptor#opens()
      * @see #addOpens(String,Module)
-     * @see AccessibleObject#setAccessible(boolean)
+     * @see java.lang.reflect.AccessibleObject#setAccessible(boolean)
      * @see java.lang.invoke.MethodHandles#privateLookupIn
      */
     public boolean isOpen(String pn, Module other) {
@@ -747,7 +747,7 @@
      *         package to at least the caller's module
      *
      * @see #isOpen(String,Module)
-     * @see AccessibleObject#setAccessible(boolean)
+     * @see java.lang.reflect.AccessibleObject#setAccessible(boolean)
      * @see java.lang.invoke.MethodHandles#privateLookupIn
      */
     @CallerSensitive
@@ -1061,7 +1061,10 @@
      * @return a map of module name to runtime {@code Module}
      *
      * @throws IllegalArgumentException
-     *         If defining any of the modules to the VM fails
+     *         If the function maps a module to the null or platform class loader
+     * @throws IllegalStateException
+     *         If the module cannot be defined to the VM or its packages overlap
+     *         with another module mapped to the same class loader
      */
     static Map<String, Module> defineModules(Configuration cf,
                                              Function<String, ClassLoader> clf,
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java	Mon Oct 08 19:44:44 2018 -0700
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java	Tue Oct 09 07:06:32 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -198,7 +198,6 @@
         this.acc = AccessController.getContext();
     }
 
-
     /**
      * Completes initialization of this Loader. This method populates
      * remotePackageToLoader with the packages of the remote modules, where
@@ -253,25 +252,25 @@
                 }
 
                 // find the packages that are exported to the target module
-                String target = resolvedModule.name();
                 ModuleDescriptor descriptor = other.reference().descriptor();
-                for (ModuleDescriptor.Exports e : descriptor.exports()) {
-                    boolean delegate;
-                    if (e.isQualified()) {
-                        // qualified export in same configuration
-                        delegate = (other.configuration() == cf)
-                                && e.targets().contains(target);
-                    } else {
-                        // unqualified
-                        delegate = true;
-                    }
+                if (descriptor.isAutomatic()) {
+                    ClassLoader l = loader;
+                    descriptor.packages().forEach(pn -> remotePackage(pn, l));
+                } else {
+                    String target = resolvedModule.name();
+                    for (ModuleDescriptor.Exports e : descriptor.exports()) {
+                        boolean delegate;
+                        if (e.isQualified()) {
+                            // qualified export in same configuration
+                            delegate = (other.configuration() == cf)
+                                    && e.targets().contains(target);
+                        } else {
+                            // unqualified
+                            delegate = true;
+                        }
 
-                    if (delegate) {
-                        String pn = e.source();
-                        ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader);
-                        if (l != null && l != loader) {
-                            throw new IllegalArgumentException("Package "
-                                + pn + " cannot be imported from multiple loaders");
+                        if (delegate) {
+                            remotePackage(e.source(), loader);
                         }
                     }
                 }
@@ -283,6 +282,22 @@
     }
 
     /**
+     * Adds to remotePackageToLoader so that an attempt to load a class in
+     * the package delegates to the given class loader.
+     *
+     * @throws IllegalStateException
+     *         if the package is already mapped to a different class loader
+     */
+    private void remotePackage(String pn, ClassLoader loader) {
+        ClassLoader l = remotePackageToLoader.putIfAbsent(pn, loader);
+        if (l != null && l != loader) {
+            throw new IllegalStateException("Package "
+                + pn + " cannot be imported from multiple loaders");
+        }
+    }
+
+
+    /**
      * Find the layer corresponding to the given configuration in the tree
      * of layers rooted at the given parent.
      */
--- a/src/java.base/share/classes/jdk/internal/loader/LoaderPool.java	Mon Oct 08 19:44:44 2018 -0700
+++ b/src/java.base/share/classes/jdk/internal/loader/LoaderPool.java	Tue Oct 09 07:06:32 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -46,7 +46,7 @@
 
     /**
      * Creates a pool of class loaders. Each module in the given configuration
-     * will be loaded its own class loader in the pool. The class loader is
+     * is mapped to its own class loader in the pool. The class loader is
      * created with the given parent class loader as its parent.
      */
     public LoaderPool(Configuration cf,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/ModuleLayer/automatic/AutomaticModulesTest.java	Tue Oct 09 07:06:32 2018 +0100
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8211825
+ * @modules jdk.compiler
+ * @library /test/lib
+ * @build jdk.test.lib.compiler.CompilerUtils jdk.test.lib.util.JarUtils
+ * @run testng/othervm AutomaticModulesTest
+ * @summary Tests automatic modules in module layers
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.lang.ModuleLayer.Controller;
+import java.lang.module.*;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+import jdk.test.lib.compiler.CompilerUtils;
+import jdk.test.lib.util.JarUtils;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/**
+ * This test uses two modules:
+ *     m requires alib and has an entry point p.Main
+ *     alib is an automatic module
+ */
+
+@Test
+public class AutomaticModulesTest {
+
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    private static final Path CLASSES = Path.of("classes");
+    private static final Path LIB = Path.of("lib");
+    private static final Path MODS = Path.of("mods");
+
+    @BeforeTest
+    public void setup() throws Exception {
+        // javac -d classes src/alib/**
+        // jar cf lib/alib.jar -C classes .
+        Files.createDirectory(CLASSES);
+        assertTrue(CompilerUtils.compile(Path.of(TEST_SRC, "src", "alib"), CLASSES));
+        JarUtils.createJarFile(LIB.resolve("alib.jar"), CLASSES);
+
+        // javac -p lib -d mods/m - src/m/**
+        Path src = Path.of(TEST_SRC, "src", "m");
+        Path output = Files.createDirectories(MODS.resolve("m"));
+        assertTrue(CompilerUtils.compile(src, output, "-p", LIB.toString()));
+    }
+
+    /**
+     * Create a module layer with modules m and alib mapped to the same class
+     * loader.
+     */
+    public void testOneLoader() throws Exception {
+        Configuration cf = ModuleLayer.boot()
+                .configuration()
+                .resolve(ModuleFinder.of(), ModuleFinder.of(MODS, LIB), Set.of("m"));
+        ResolvedModule m = cf.findModule("m").orElseThrow();
+        ResolvedModule alib = cf.findModule("alib").orElseThrow();
+        assertTrue(m.reads().contains(alib));
+        assertTrue(alib.reference().descriptor().isAutomatic());
+        ModuleLayer bootLayer = ModuleLayer.boot();
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Controller controller = ModuleLayer.defineModulesWithOneLoader(cf, List.of(bootLayer), scl);
+        invokeMain(controller, "m/p.Main");
+    }
+
+    /**
+     * Create a module layer with modules m and alib mapped to different class
+     * loaders. This will test that L(m) delegates to L(alib) in the same layer.
+     */
+    public void testManyLoaders() throws Exception {
+        Configuration cf = ModuleLayer.boot()
+                .configuration()
+                .resolve(ModuleFinder.of(), ModuleFinder.of(MODS, LIB), Set.of("m"));
+        ResolvedModule m = cf.findModule("m").orElseThrow();
+        ResolvedModule alib = cf.findModule("alib").orElseThrow();
+        assertTrue(m.reads().contains(alib));
+        assertTrue(alib.reference().descriptor().isAutomatic());
+        ModuleLayer bootLayer = ModuleLayer.boot();
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        Controller controller = ModuleLayer.defineModulesWithManyLoaders(cf, List.of(bootLayer), scl);
+        invokeMain(controller, "m/p.Main");
+    }
+
+    /**
+     * Create a module layer with alib and another module layer with m.
+     * This will test that L(m) delegates to L(alib) in a parent layer.
+     */
+    public void testAutomaticModuleInParent() throws Exception {
+        ModuleLayer bootLayer = ModuleLayer.boot();
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+
+        // configuration/layer containing alib
+        Configuration cf1 = bootLayer
+                .configuration()
+                .resolve(ModuleFinder.of(), ModuleFinder.of(LIB), Set.of("alib"));
+        ModuleLayer layer1 = bootLayer.defineModulesWithOneLoader(cf1, scl);
+
+        // configuration/layer containing m
+        Configuration cf2 = cf1.resolve(ModuleFinder.of(), ModuleFinder.of(MODS), Set.of("m"));
+        Controller controller = ModuleLayer.defineModulesWithOneLoader(cf2, List.of(layer1), scl);
+
+        invokeMain(controller, "m/p.Main");
+    }
+
+    /**
+     * Invokes the main method of the given entry point (module-name/class-name)
+     */
+    private void invokeMain(Controller controller, String entry) throws Exception {
+        String[] s = entry.split("/");
+        String moduleName = s[0];
+        String className = s[1];
+        int pos = className.lastIndexOf('.');
+        String packageName = className.substring(0, pos);
+        ModuleLayer layer = controller.layer();
+        Module module = layer.findModule(moduleName).orElseThrow();
+        controller.addExports(module, packageName, this.getClass().getModule());
+        ClassLoader loader = layer.findLoader(moduleName);
+        Class<?> c = loader.loadClass(className);
+        Method m = c.getMethod("main", String[].class);
+        m.invoke(null, (Object)new String[0]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/ModuleLayer/automatic/src/alib/q/Lib.java	Tue Oct 09 07:06:32 2018 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package q;
+
+public class Lib {
+    private Lib() { }
+
+    public static String generate() {
+        return "something useful";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/ModuleLayer/automatic/src/m/module-info.java	Tue Oct 09 07:06:32 2018 +0100
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+module m {
+    requires alib;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/ModuleLayer/automatic/src/m/p/Main.java	Tue Oct 09 07:06:32 2018 +0100
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package p;
+
+import q.Lib;
+
+public class Main {
+    public static void main(String[] args) {
+        String s = Lib.generate();
+        System.out.println(s);
+    }
+}