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 
  24 import java.io.File;
  25 import java.io.PrintWriter;
  26 import java.io.StringWriter;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.nio.file.Paths;
  30 import java.util.ArrayList;
  31 import java.util.List;
  32 import java.util.spi.ToolProvider;
  33 import java.util.stream.Collectors;
  34 import java.util.stream.Stream;
  35 
  36 import jdk.test.lib.compiler.CompilerUtils;
  37 import static jdk.testlibrary.ProcessTools.*;
  38 
  39 import org.testng.annotations.BeforeTest;
  40 import org.testng.annotations.Test;
  41 import 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 
  53 public 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 }