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