1/*
2 * Copyright (c) 2017, 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
24import java.io.File;
25import java.io.PrintWriter;
26import java.io.StringWriter;
27import java.nio.file.Files;
28import java.nio.file.Path;
29import java.nio.file.Paths;
30import java.util.ArrayList;
31import java.util.List;
32import java.util.spi.ToolProvider;
33import java.util.stream.Collectors;
34import java.util.stream.Stream;
35
36import jdk.test.lib.compiler.CompilerUtils;
37import static jdk.testlibrary.ProcessTools.*;
38
39import org.testng.annotations.BeforeTest;
40import org.testng.annotations.Test;
41import static org.testng.Assert.*;
42
43/**
44 * @test
45 * @bug 8174826
46 * @library /lib/testlibrary /test/lib
47 * @modules jdk.compiler jdk.jlink
48 * @build BindServices jdk.testlibrary.ProcessTools
49 *        jdk.test.lib.compiler.CompilerUtils
50 * @run testng BindServices
51 */
52
53public class BindServices {
54    private static final String JAVA_HOME = System.getProperty("java.home");
55    private static final String TEST_SRC = System.getProperty("test.src");
56
57    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
58    private static final Path MODS_DIR = Paths.get("mods");
59
60    private static final String MODULE_PATH =
61        Paths.get(JAVA_HOME, "jmods").toString() +
62            File.pathSeparator + MODS_DIR.toString();
63
64    // the names of the modules in this test
65    private static String[] modules = new String[] {"m1", "m2", "m3"};
66
67
68    private static boolean hasJmods() {
69        if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
70            System.err.println("Test skipped. NO jmods directory");
71            return false;
72        }
73        return true;
74    }
75
76    /*
77     * Compiles all modules used by the test
78     */
79    @BeforeTest
80    public void compileAll() throws Throwable {
81        if (!hasJmods()) return;
82
83        for (String mn : modules) {
84            Path msrc = SRC_DIR.resolve(mn);
85            assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
86                "--module-source-path", SRC_DIR.toString()));
87        }
88    }
89
90    @Test
91    public void noServiceBinding() throws Throwable {
92        if (!hasJmods()) return;
93
94        Path dir = Paths.get("noServiceBinding");
95
96        // no service binding and does not link m2,m3 providers.
97        JLink.run("--output", dir.toString(),
98                  "--module-path", MODULE_PATH,
99                  "--add-modules", "m1").output();
100
101        testImage(dir, "m1");
102    }
103
104    @Test
105    public void fullServiceBinding() throws Throwable {
106        if (!hasJmods()) return;
107
108        Path dir = Paths.get("fullServiceBinding");
109
110        // full service binding
111        // m2 is a provider used by m1.  During service binding, when m2 is
112        // resolved, m2 uses p2.T that causes m3 to be linked as it is a
113        // provider to p2.T
114        JLink.run("--output", dir.toString(),
115                  "--module-path", MODULE_PATH,
116                  "--add-modules", "m1",
117                  "--bind-services",
118                  "--limit-modules", "m1,m2,m3");
119
120        testImage(dir, "m1", "m2", "m3");
121    }
122
123    @Test
124    public void testVerbose() throws Throwable {
125        if (!hasJmods()) return;
126
127        Path dir = Paths.get("verbose");
128
129        List<String> output =
130            JLink.run("--output", dir.toString(),
131                      "--module-path", MODULE_PATH,
132                      "--add-modules", "m1",
133                      "--bind-services",
134                      "--verbose",
135                      "--limit-modules", "m1,m2,m3").output();
136
137        List<String> expected = List.of(
138            "m1 " + MODS_DIR.resolve("m1").toUri().toString(),
139            "m2 " + MODS_DIR.resolve("m2").toUri().toString(),
140            "m3 " + MODS_DIR.resolve("m3").toUri().toString(),
141            "java.base provides java.nio.file.spi.FileSystemProvider used by java.base",
142            "m1 provides p1.S used by m1",
143            "m2 provides p1.S used by m1",
144            "m2 provides p2.T used by m2",
145            "m3 provides p2.T used by m2",
146            "m3 provides p3.S not used by any observable module"
147        );
148
149        assertTrue(output.containsAll(expected));
150
151        testImage(dir, "m1", "m2", "m3");
152    }
153
154    @Test
155    public void testVerboseAndNoBindServices() throws Throwable {
156        if (!hasJmods()) return;
157
158        Path dir = Paths.get("verboseNoBind");
159
160        List<String> output =
161            JLink.run("--output", dir.toString(),
162                      "--module-path", MODULE_PATH,
163                      "--verbose",
164                      "--add-modules", "m1").output();
165
166        assertTrue(output.contains("m1 provides p1.S used by m1"));
167
168        testImage(dir, "m1");
169    }
170
171    /*
172     * Tests the given ${java.home} to only contain the specified modules
173     */
174    private void testImage(Path javaHome, String... modules) throws Throwable {
175        Path java = javaHome.resolve("bin").resolve("java");
176        String[] cmd = Stream.concat(
177            Stream.of(java.toString(), "-m", "m1/p1.Main"),
178            Stream.of(modules)).toArray(String[]::new);
179
180        assertTrue(executeProcess(cmd).outputTo(System.out)
181                                      .errorTo(System.out)
182                                      .getExitValue() == 0);
183    }
184
185    static class JLink {
186        static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
187            .orElseThrow(() ->
188                new RuntimeException("jlink tool not found")
189            );
190
191        static JLink run(String... options) {
192            JLink jlink = new JLink();
193            assertTrue(jlink.execute(options) == 0);
194            return jlink;
195        }
196
197        final List<String> output = new ArrayList<>();
198        private int execute(String... options) {
199            System.out.println("jlink " +
200                Stream.of(options).collect(Collectors.joining(" ")));
201
202            StringWriter writer = new StringWriter();
203            PrintWriter pw = new PrintWriter(writer);
204            int rc = JLINK_TOOL.run(pw, pw, options);
205            System.out.println(writer.toString());
206            Stream.of(writer.toString().split("\\v"))
207                  .map(String::trim)
208                  .forEach(output::add);
209            return rc;
210        }
211
212        boolean contains(String s) {
213            return output.contains(s);
214        }
215
216        List<String> output() {
217            return output;
218        }
219    }
220}
221