# HG changeset patch # User alanb # Date 1574163329 0 # Node ID 580fb715b29d82439ab7174d9277b5eeb9d27514 # Parent 189f47d990b57074b4fdd1bd7328d301663ddea3 8233922: Service binding augments module graph with observable incubator modules Reviewed-by: mchung diff -r 189f47d990b5 -r 580fb715b29d src/java.base/share/classes/java/lang/module/Configuration.java --- a/src/java.base/share/classes/java/lang/module/Configuration.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/java.base/share/classes/java/lang/module/Configuration.java Tue Nov 19 11:35:29 2019 +0000 @@ -312,7 +312,7 @@ { List parents = List.of(empty()); Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput); - resolver.resolve(roots).bind(); + resolver.resolve(roots).bind(/*bindIncubatorModules*/false); return new Configuration(parents, resolver); } diff -r 189f47d990b5 -r 580fb715b29d src/java.base/share/classes/java/lang/module/Resolver.java --- a/src/java.base/share/classes/java/lang/module/Resolver.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/java.base/share/classes/java/lang/module/Resolver.java Tue Nov 19 11:35:29 2019 +0000 @@ -28,7 +28,6 @@ import java.io.PrintStream; import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Requires.Modifier; -import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -45,6 +44,7 @@ import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleReferenceImpl; +import jdk.internal.module.ModuleResolution; import jdk.internal.module.ModuleTarget; /** @@ -215,15 +215,32 @@ * service-use relation. */ Resolver bind() { + return bind(/*bindIncubatorModules*/true); + } + /** + * Augments the set of resolved modules with modules induced by the + * service-use relation. + * + * @param bindIncubatorModules true if incubator modules are candidates to + * add to the module graph + */ + Resolver bind(boolean bindIncubatorModules) { // Scan the finders for all available service provider modules. As // java.base uses services then the module finders will be scanned // anyway. Map> availableProviders = new HashMap<>(); for (ModuleReference mref : findAll()) { ModuleDescriptor descriptor = mref.descriptor(); - if (!descriptor.provides().isEmpty()) { + boolean candidate; + if (!bindIncubatorModules && (mref instanceof ModuleReferenceImpl)) { + ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution(); + candidate = (mres == null) || (mres.hasIncubatingWarning() == false); + } else { + candidate = true; + } + if (candidate && !descriptor.provides().isEmpty()) { for (Provides provides : descriptor.provides()) { String sn = provides.service(); diff -r 189f47d990b5 -r 580fb715b29d src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Tue Nov 19 11:35:29 2019 +0000 @@ -352,7 +352,7 @@ Configuration cf; if (needResolution) { - cf = JLMA.resolveAndBind(finder, roots, traceOutput); + cf = Modules.newBootLayerConfiguration(finder, roots, traceOutput); } else { if (archivedModuleGraph != null) { cf = archivedModuleGraph.configuration(); diff -r 189f47d990b5 -r 580fb715b29d src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Tue Nov 19 11:35:29 2019 +0000 @@ -34,6 +34,7 @@ import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.ModuleVisitor; import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.commons.ModuleResolutionAttribute; import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; import static jdk.internal.org.objectweb.asm.Opcodes.*; @@ -78,7 +79,9 @@ * Writes the given module descriptor to a module-info.class file, * returning it in a byte array. */ - private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) { + private static byte[] toModuleInfo(ModuleDescriptor md, + ModuleResolution mres, + ModuleTarget target) { ClassWriter cw = new ClassWriter(0); cw.visit(Opcodes.V10, ACC_MODULE, "module-info", null, null, null); @@ -147,6 +150,11 @@ mv.visitEnd(); + // write ModuleResolution attribute if specified + if (mres != null) { + cw.visitAttribute(new ModuleResolutionAttribute(mres.value())); + } + // write ModuleTarget attribute if there is a target platform if (target != null && target.targetPlatform().length() > 0) { cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform())); @@ -161,11 +169,12 @@ * module-info.class. */ public static void write(ModuleDescriptor descriptor, + ModuleResolution mres, ModuleTarget target, OutputStream out) throws IOException { - byte[] bytes = toModuleInfo(descriptor, target); + byte[] bytes = toModuleInfo(descriptor, mres, target); out.write(bytes); } @@ -173,10 +182,34 @@ * Writes a module descriptor to the given output stream as a * module-info.class. */ + public static void write(ModuleDescriptor descriptor, + ModuleResolution mres, + OutputStream out) + throws IOException + { + write(descriptor, mres, null, out); + } + + /** + * Writes a module descriptor to the given output stream as a + * module-info.class. + */ + public static void write(ModuleDescriptor descriptor, + ModuleTarget target, + OutputStream out) + throws IOException + { + write(descriptor, null, target, out); + } + + /** + * Writes a module descriptor to the given output stream as a + * module-info.class. + */ public static void write(ModuleDescriptor descriptor, OutputStream out) throws IOException { - write(descriptor, null, out); + write(descriptor, null, null, out); } /** @@ -184,7 +217,7 @@ * in module-info.class format. */ public static ByteBuffer toByteBuffer(ModuleDescriptor descriptor) { - byte[] bytes = toModuleInfo(descriptor, null); + byte[] bytes = toModuleInfo(descriptor, null, null); return ByteBuffer.wrap(bytes); } } diff -r 189f47d990b5 -r 580fb715b29d src/java.base/share/classes/jdk/internal/module/Modules.java --- a/src/java.base/share/classes/jdk/internal/module/Modules.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/Modules.java Tue Nov 19 11:35:29 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, 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 @@ -25,6 +25,7 @@ package jdk.internal.module; +import java.io.PrintStream; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -33,6 +34,7 @@ import java.net.URI; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,6 +42,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.ClassLoaders; @@ -61,6 +64,7 @@ private Modules() { } private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); /** * Creates a new Module. The module has the given ModuleDescriptor and @@ -159,6 +163,20 @@ } /** + * Resolves a collection of root modules, with service binding and the empty + * Configuration as the parent to create a Configuration for the boot layer. + * + * This method is intended to be used to create the Configuration for the + * boot layer during startup or at a link-time. + */ + public static Configuration newBootLayerConfiguration(ModuleFinder finder, + Collection roots, + PrintStream traceOutput) + { + return JLMA.resolveAndBind(finder, roots, traceOutput); + } + + /** * Called by the VM when code in the given Module has been transformed by * an agent and so may have been instrumented to call into supporting * classes on the boot class path or application class path. diff -r 189f47d990b5 -r 580fb715b29d src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Mon Nov 18 23:41:06 2019 -0500 +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Tue Nov 19 11:35:29 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, 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 @@ -61,6 +61,7 @@ import jdk.internal.module.Checks; import jdk.internal.module.DefaultRoots; import jdk.internal.module.IllegalAccessMaps; +import jdk.internal.module.Modules; import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleInfo.Attributes; import jdk.internal.module.ModuleInfoExtender; @@ -291,10 +292,10 @@ /** * Resolves a collection of root modules, with service binding, to create - * configuration. + * a Configuration for the boot layer. */ private Configuration resolve(ModuleFinder finder, Set roots) { - return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots); + return Modules.newBootLayerConfiguration(finder, roots, null); } /** diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/etc/DefaultModules.java --- a/test/jdk/jdk/modules/etc/DefaultModules.java Mon Nov 18 23:41:06 2019 -0500 +++ b/test/jdk/jdk/modules/etc/DefaultModules.java Tue Nov 19 11:35:29 2019 +0000 @@ -59,8 +59,8 @@ String testSrc = System.getProperty("test.src"); // $JDK_HOME/bin/java TestModules.java - String source = Path.of(testSrc, "src", "TestRootModules.java").toString(); - ProcessTools.executeTestJava(source) + String source = Path.of(testSrc, "TestRootModules.java").toString(); + ProcessTools.executeTestJava("--add-exports", "java.base/jdk.internal.module=ALL-UNNAMED", source) .outputTo(System.out) .errorTo(System.err) .shouldHaveExitValue(0); @@ -89,15 +89,18 @@ javaLauncher += ".exe"; // $CUSTOM_JDK/bin/java TestRootModules.java - source = Path.of(testSrc, "src", "TestRootModules.java").toString(); - out.format("Command line: [%s %s]%n", javaLauncher, source); - ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) + source = Path.of(testSrc, "TestRootModules.java").toString(); + ProcessBuilder pb = new ProcessBuilder(javaLauncher, + "--add-exports", "java.base/jdk.internal.module=ALL-UNNAMED", + source); + out.format("Command line: [%s]%n", pb.command()); + ProcessTools.executeProcess(pb) .outputTo(System.out) .errorTo(System.err) .shouldHaveExitValue(0); // $CUSTOM_JDK/bin/java TestJson.java - source = Path.of(testSrc, "src", "TestJson.java").toString(); + source = Path.of(testSrc, "TestJson.java").toString(); out.format("Command line: [%s %s]%n", javaLauncher, source); ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) .outputTo(System.out) diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/etc/TestJson.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/etc/TestJson.java Tue Nov 19 11:35:29 2019 +0000 @@ -0,0 +1,35 @@ +/* + * 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. + */ + +import javax.json.*; +import java.io.InputStream; + +/** + * Exercise APIs exported by the java.json module + */ + +public class TestJson { + public static void main(String[] args) { + JsonParser parser = Json.createParser(InputStream.nullInputStream()); + } +} diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/etc/TestRootModules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/etc/TestRootModules.java Tue Nov 19 11:35:29 2019 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, 2019, 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. + */ + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; + +import jdk.internal.module.ModuleResolution; + +/** + * Test the set of modules in the boot layer includes all modules that export + * an API. Also test that java.se is not resolved. + */ + +public class TestRootModules { + public static void main(String[] args) { + // all modules that export an API should be resolved + // For now, this test ignores the ModuleResolution attribute + ModuleLayer bootLayer = ModuleLayer.boot(); + ModuleFinder.ofSystem().findAll().stream() + .filter(mref -> !ModuleResolution.doNotResolveByDefault(mref)) + .map(ModuleReference::descriptor) + .filter(descriptor -> descriptor.exports() + .stream() + .filter(e -> !e.isQualified()) + .findAny() + .isPresent()) + .map(ModuleDescriptor::name) + .forEach(name -> { + if (!bootLayer.findModule(name).isPresent()) + throw new RuntimeException(name + " not in boot layer"); + }); + + // java.se should not be resolved + ModuleLayer.boot() + .findModule("java.se") + .map(m -> { throw new RuntimeException("java.se should not be resolved"); }); + } +} diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/etc/src/TestJson.java --- a/test/jdk/jdk/modules/etc/src/TestJson.java Mon Nov 18 23:41:06 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * 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. - */ - -import javax.json.*; -import java.io.InputStream; - -/** - * Exercise APIs exported by the java.json module - */ - -public class TestJson { - public static void main(String[] args) { - JsonParser parser = Json.createParser(InputStream.nullInputStream()); - } -} diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/etc/src/TestRootModules.java --- a/test/jdk/jdk/modules/etc/src/TestRootModules.java Mon Nov 18 23:41:06 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * 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. - */ - - -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReference; - -/** - * Test the set of modules in the boot layer includes all modules that export - * an API. Also test that java.se is not resolved. - */ - -public class TestRootModules { - public static void main(String[] args) { - // all modules that export an API should be resolved - // For now, this test ignores the ModuleResolution attribute - ModuleLayer bootLayer = ModuleLayer.boot(); - ModuleFinder.ofSystem().findAll().stream() - .map(ModuleReference::descriptor) - .filter(descriptor -> descriptor.exports() - .stream() - .filter(e -> !e.isQualified()) - .findAny() - .isPresent()) - .map(ModuleDescriptor::name) - .forEach(name -> { - if (!bootLayer.findModule(name).isPresent()) - throw new RuntimeException(name + " not in boot layer"); - }); - - // java.se should not be resolved - ModuleLayer.boot() - .findModule("java.se") - .map(m -> { throw new RuntimeException("java.se should not be resolved"); }); - } -} diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/incubator/ServiceBinding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/incubator/ServiceBinding.java Tue Nov 19 11:35:29 2019 +0000 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2019, 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 8233922 + * @modules java.base/jdk.internal.module + * @library /test/lib + * @build ServiceBinding TestBootLayer + * @run testng ServiceBinding + * @summary Test service binding with incubator modules + */ + +import java.io.File; +import java.io.OutputStream; +import java.lang.module.ModuleDescriptor; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.module.ResolvedModule; +import java.nio.file.Path; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.Stream; + +import static java.lang.module.ModuleDescriptor.newModule; + +import jdk.internal.module.ModuleInfoWriter; +import jdk.internal.module.ModuleResolution; + +import org.testng.annotations.Test; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +@Test +public class ServiceBinding { + private static final Path HERE = Path.of("."); + + /** + * module m1 uses p.S + * (incubating) module m2 requires m1 provides p.S + */ + public void test1() throws Exception { + Path mlib = Files.createTempDirectory(HERE, "mlib"); + + var m1 = newModule("m1").exports("p").uses("p.S").build(); + var m2 = newModule("m2").requires("m1").provides("p.S", List.of("impl.S1")).build(); + + writeModule(mlib, m1); + writeIncubatingModule(mlib, m2); + + // boot layer: root=m1, incubator module m2 should not be resolved + testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2")) + .shouldNotMatch("WARNING:.*m2"); + + // custom configuration: root=m1, incubator module m2 should be resolved + testCustomConfiguration(mlib, Set.of("m1"), Set.of("m2")); + } + + /** + * module m1 uses p.S + * (incubating) module m2 requires m1 provides P.S uses q.S + * (incubating) module m3 requires m2 provides q.S + */ + public void test2() throws Exception { + Path mlib = Files.createTempDirectory("mlib"); + + var m1 = newModule("m1").exports("p").uses("p.S").build(); + var m2 = newModule("m2") + .requires("m1") + .provides("p.S", List.of("impl.S1")) + .exports("q") + .uses("q.S") + .build(); + var m3 = newModule("m3").requires("m2").provides("q.S", List.of("impl.S1")).build(); + + writeModule(mlib, m1); + writeIncubatingModule(mlib, m2); + writeIncubatingModule(mlib, m3); + + // boot layer: root=m1, incubator modules m2 and m3 should not be resolved + testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2", "m3")) + .shouldNotMatch("WARNING:.*m2") + .shouldNotMatch("WARNING:.*m3"); + + // boot layer: root=m2, incubator module m3 should not be resolved + testBootLayer(mlib, Set.of("m2"), Set.of("m1", "m2"), Set.of("m3")) + .shouldMatch("WARNING:.*m2") + .shouldNotMatch("WARNING:.*m3"); + + // custom configuration: root=m1, incubator modules m2 and m3 should be resolved + testCustomConfiguration(mlib, Set.of("m1"), Set.of("m1", "m2", "m3")); + + // custom configuration: root=m2, incubator module m3 should be resolved + testCustomConfiguration(mlib, Set.of("m2"), Set.of("m1", "m2", "m3")); + } + + /** + * Creates an exploded module on the file system. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + */ + void writeModule(Path mlib, ModuleDescriptor descriptor) throws Exception { + writeModule(mlib, descriptor, false); + } + + /** + * Creates an exploded module on the file system. The module will be an + * incubating module. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + */ + void writeIncubatingModule(Path mlib, ModuleDescriptor descriptor) throws Exception { + writeModule(mlib, descriptor, true); + } + + /** + * Creates an exploded module on the file system. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + * @param incubating to create an incubating module + */ + void writeModule(Path mlib, ModuleDescriptor descriptor, boolean incubating) + throws Exception + { + // create ModuleResolution attribute if incubating module + ModuleResolution mres = (incubating) ? ModuleResolution.empty().withIncubating() : null; + String name = descriptor.name(); + + // create directory for module + Path dir = Files.createDirectory(mlib.resolve(name)); + + // module-info.class + try (OutputStream out = Files.newOutputStream(dir.resolve("module-info.class"))) { + ModuleInfoWriter.write(descriptor, mres, out); + } + + // create a dummy class file for each package + for (String pn : descriptor.packages()) { + Path subdir = dir.resolve(pn.replace('.', File.separatorChar)); + Files.createDirectories(subdir); + Files.createFile(subdir.resolve("C.class")); + } + } + + /** + * Run TestBootLayer in a child VM with the given module path and the + * --add-modules option with additional root modules. TestBootLayer checks + * the modules in the boot layer. + * + * @param mlib the module path + * @param roots the modules to specify to --add-modules + * @param expected the names of modules that should be in the boot layer + * @param notExpected the names of modules that should not be in boot layer + */ + OutputAnalyzer testBootLayer(Path mlib, + Set roots, + Set expected, + Set notExpected) + throws Exception + { + var opts = Stream.of("-p", mlib.toString(), + "--add-modules", commaSeparated(roots), + "TestBootLayer", commaSeparated(expected), commaSeparated(notExpected)); + return ProcessTools.executeTestJava(opts.toArray(String[]::new)) + .outputTo(System.out) + .errorTo(System.out) + .shouldHaveExitValue(0); + } + + /** + * Creates a Configuration by resolving a set of root modules, with service + * binding, then checks that the Configuration includes the expected modules. + * + * @param mlib the module path + * @param roots the names of the root modules + * @param expected the names of modules that should be in the configuration + */ + void testCustomConfiguration(Path mlib, Set roots, Set expected) { + ModuleFinder finder = ModuleFinder.of(mlib); + Configuration cf = ModuleLayer.boot() + .configuration() + .resolveAndBind(finder, ModuleFinder.of(), roots); + + Set modules = cf.modules().stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + + expected.stream() + .filter(mn -> !modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " not in configuration!!!"); + }); + } + + String commaSeparated(Set s) { + return s.stream().collect(Collectors.joining(",")); + } +} diff -r 189f47d990b5 -r 580fb715b29d test/jdk/jdk/modules/incubator/TestBootLayer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/incubator/TestBootLayer.java Tue Nov 19 11:35:29 2019 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019, 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. + */ + +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Launched by the test ServiceBinding to test modules in the boot layer. + */ + +public class TestBootLayer { + public static void main(String[] args) throws Exception { + Pattern splitter = Pattern.compile(","); + + // the names of all modules in the boot layer + Set modules = ModuleLayer.boot().modules().stream() + .map(Module::getName) + .collect(Collectors.toSet()); + + // throw exception if an expected module is not in the boot layer + splitter.splitAsStream(args[0]) + .filter(Predicate.not(String::isEmpty)) + .filter(mn -> !modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " not in boot layer!!!"); + }); + + // throw exception if an unexpected module is in the boot layer + splitter.splitAsStream(args[1]) + .filter(Predicate.not(String::isEmpty)) + .filter(mn -> modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " in boot layer!!!!"); + }); + } +} \ No newline at end of file