8211825: ModuleLayer.defineModulesWithXXX does not setup delegation when module reads automatic module
Reviewed-by: mchung
--- 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);
+ }
+}