1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8233922 27 * @modules java.base/jdk.internal.module 28 * @library /test/lib 29 * @build ServiceBinding TestBootLayer 30 * @run testng ServiceBinding 31 * @summary Test service binding with incubator modules 32 */ 33 34 import java.io.File; 35 import java.io.OutputStream; 36 import java.lang.module.ModuleDescriptor; 37 import java.lang.module.Configuration; 38 import java.lang.module.ModuleFinder; 39 import java.lang.module.ResolvedModule; 40 import java.nio.file.Path; 41 import java.nio.file.Files; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 import java.util.stream.Stream; 46 import java.util.stream.Stream; 47 48 import static java.lang.module.ModuleDescriptor.newModule; 49 50 import jdk.internal.module.ModuleInfoWriter; 51 import jdk.internal.module.ModuleResolution; 52 53 import org.testng.annotations.Test; 54 55 import jdk.test.lib.process.ProcessTools; 56 import jdk.test.lib.process.OutputAnalyzer; 57 58 @Test 59 public class ServiceBinding { 60 private static final Path HERE = Path.of("."); 61 62 /** 63 * module m1 uses p.S 64 * (incubating) module m2 requires m1 provides p.S 65 */ test1()66 public void test1() throws Exception { 67 Path mlib = Files.createTempDirectory(HERE, "mlib"); 68 69 var m1 = newModule("m1").exports("p").uses("p.S").build(); 70 var m2 = newModule("m2").requires("m1").provides("p.S", List.of("impl.S1")).build(); 71 72 writeModule(mlib, m1); 73 writeIncubatingModule(mlib, m2); 74 75 // boot layer: root=m1, incubator module m2 should not be resolved 76 testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2")) 77 .shouldNotMatch("WARNING:.*m2"); 78 79 // custom configuration: root=m1, incubator module m2 should be resolved 80 testCustomConfiguration(mlib, Set.of("m1"), Set.of("m2")); 81 } 82 83 /** 84 * module m1 uses p.S 85 * (incubating) module m2 requires m1 provides P.S uses q.S 86 * (incubating) module m3 requires m2 provides q.S 87 */ test2()88 public void test2() throws Exception { 89 Path mlib = Files.createTempDirectory("mlib"); 90 91 var m1 = newModule("m1").exports("p").uses("p.S").build(); 92 var m2 = newModule("m2") 93 .requires("m1") 94 .provides("p.S", List.of("impl.S1")) 95 .exports("q") 96 .uses("q.S") 97 .build(); 98 var m3 = newModule("m3").requires("m2").provides("q.S", List.of("impl.S1")).build(); 99 100 writeModule(mlib, m1); 101 writeIncubatingModule(mlib, m2); 102 writeIncubatingModule(mlib, m3); 103 104 // boot layer: root=m1, incubator modules m2 and m3 should not be resolved 105 testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2", "m3")) 106 .shouldNotMatch("WARNING:.*m2") 107 .shouldNotMatch("WARNING:.*m3"); 108 109 // boot layer: root=m2, incubator module m3 should not be resolved 110 testBootLayer(mlib, Set.of("m2"), Set.of("m1", "m2"), Set.of("m3")) 111 .shouldMatch("WARNING:.*m2") 112 .shouldNotMatch("WARNING:.*m3"); 113 114 // custom configuration: root=m1, incubator modules m2 and m3 should be resolved 115 testCustomConfiguration(mlib, Set.of("m1"), Set.of("m1", "m2", "m3")); 116 117 // custom configuration: root=m2, incubator module m3 should be resolved 118 testCustomConfiguration(mlib, Set.of("m2"), Set.of("m1", "m2", "m3")); 119 } 120 121 /** 122 * Creates an exploded module on the file system. 123 * 124 * @param mlib the top-level module directory 125 * @param descriptor the module descriptor of the module to write 126 */ writeModule(Path mlib, ModuleDescriptor descriptor)127 void writeModule(Path mlib, ModuleDescriptor descriptor) throws Exception { 128 writeModule(mlib, descriptor, false); 129 } 130 131 /** 132 * Creates an exploded module on the file system. The module will be an 133 * incubating module. 134 * 135 * @param mlib the top-level module directory 136 * @param descriptor the module descriptor of the module to write 137 */ writeIncubatingModule(Path mlib, ModuleDescriptor descriptor)138 void writeIncubatingModule(Path mlib, ModuleDescriptor descriptor) throws Exception { 139 writeModule(mlib, descriptor, true); 140 } 141 142 /** 143 * Creates an exploded module on the file system. 144 * 145 * @param mlib the top-level module directory 146 * @param descriptor the module descriptor of the module to write 147 * @param incubating to create an incubating module 148 */ writeModule(Path mlib, ModuleDescriptor descriptor, boolean incubating)149 void writeModule(Path mlib, ModuleDescriptor descriptor, boolean incubating) 150 throws Exception 151 { 152 // create ModuleResolution attribute if incubating module 153 ModuleResolution mres = (incubating) ? ModuleResolution.empty().withIncubating() : null; 154 String name = descriptor.name(); 155 156 // create directory for module 157 Path dir = Files.createDirectory(mlib.resolve(name)); 158 159 // module-info.class 160 try (OutputStream out = Files.newOutputStream(dir.resolve("module-info.class"))) { 161 ModuleInfoWriter.write(descriptor, mres, out); 162 } 163 164 // create a dummy class file for each package 165 for (String pn : descriptor.packages()) { 166 Path subdir = dir.resolve(pn.replace('.', File.separatorChar)); 167 Files.createDirectories(subdir); 168 Files.createFile(subdir.resolve("C.class")); 169 } 170 } 171 172 /** 173 * Run TestBootLayer in a child VM with the given module path and the 174 * --add-modules option with additional root modules. TestBootLayer checks 175 * the modules in the boot layer. 176 * 177 * @param mlib the module path 178 * @param roots the modules to specify to --add-modules 179 * @param expected the names of modules that should be in the boot layer 180 * @param notExpected the names of modules that should not be in boot layer 181 */ testBootLayer(Path mlib, Set<String> roots, Set<String> expected, Set<String> notExpected)182 OutputAnalyzer testBootLayer(Path mlib, 183 Set<String> roots, 184 Set<String> expected, 185 Set<String> notExpected) 186 throws Exception 187 { 188 var opts = Stream.of("-p", mlib.toString(), 189 "--add-modules", commaSeparated(roots), 190 "TestBootLayer", commaSeparated(expected), commaSeparated(notExpected)); 191 return ProcessTools.executeTestJava(opts.toArray(String[]::new)) 192 .outputTo(System.out) 193 .errorTo(System.out) 194 .shouldHaveExitValue(0); 195 } 196 197 /** 198 * Creates a Configuration by resolving a set of root modules, with service 199 * binding, then checks that the Configuration includes the expected modules. 200 * 201 * @param mlib the module path 202 * @param roots the names of the root modules 203 * @param expected the names of modules that should be in the configuration 204 */ testCustomConfiguration(Path mlib, Set<String> roots, Set<String> expected)205 void testCustomConfiguration(Path mlib, Set<String> roots, Set<String> expected) { 206 ModuleFinder finder = ModuleFinder.of(mlib); 207 Configuration cf = ModuleLayer.boot() 208 .configuration() 209 .resolveAndBind(finder, ModuleFinder.of(), roots); 210 211 Set<String> modules = cf.modules().stream() 212 .map(ResolvedModule::name) 213 .collect(Collectors.toSet()); 214 215 expected.stream() 216 .filter(mn -> !modules.contains(mn)) 217 .findAny() 218 .ifPresent(mn -> { 219 throw new RuntimeException(mn + " not in configuration!!!"); 220 }); 221 } 222 commaSeparated(Set<String> s)223 String commaSeparated(Set<String> s) { 224 return s.stream().collect(Collectors.joining(",")); 225 } 226 } 227