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 static jdk.testlibrary.Asserts.assertTrue;
  37 
  38 import org.testng.annotations.BeforeTest;
  39 import org.testng.annotations.Test;
  40 import static org.testng.Assert.*;
  41 
  42 /**
  43  * @test
  44  * @bug 8174826
  45  * @library /lib/testlibrary
  46  * @modules jdk.charsets jdk.compiler jdk.jlink
  47  * @build SuggestProviders CompilerUtils
  48  * @run testng SuggestProviders
  49  */
  50 
  51 public class SuggestProviders {
  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 suggestProviders() throws Throwable {
  90         if (!hasJmods()) return;
  91 
  92         List<String> output = JLink.run("--module-path", MODULE_PATH,
  93                                         "--add-modules", "m1",
  94                                         "--suggest-providers").output();
  95         // check a subset of services used by java.base
  96         List<String> expected = List.of(
  97             "uses java.lang.System$LoggerFinder",
  98             "uses java.net.ContentHandlerFactory",
  99             "uses java.net.spi.URLStreamHandlerProvider",
 100             "uses java.nio.channels.spi.AsynchronousChannelProvider",
 101             "uses java.nio.channels.spi.SelectorProvider",
 102             "uses java.nio.charset.spi.CharsetProvider",
 103             "uses java.nio.file.spi.FileSystemProvider",
 104             "uses java.nio.file.spi.FileTypeDetector",
 105             "uses java.security.Provider",
 106             "uses java.util.spi.ToolProvider",
 107             "uses p1.S",
 108             "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base",
 109             "module jdk.compiler provides java.util.spi.ToolProvider, used by java.base",
 110             "module jdk.jlink provides java.util.spi.ToolProvider, used by java.base",
 111             "module m1 provides p1.S, used by m1",
 112             "module m2 provides p1.S, used by m1"
 113         );
 114 
 115         assertTrue(output.containsAll(expected));
 116     }
 117 
 118     @Test
 119     public void providersForServices() throws Throwable {
 120         if (!hasJmods()) return;
 121 
 122         List<String> output =
 123             JLink.run("--module-path", MODULE_PATH,
 124                       "--add-modules", "m1",
 125                       "--suggest-providers",
 126                       "java.nio.charset.spi.CharsetProvider,p1.S,p2.T").output();
 127 
 128         System.out.println(output);
 129         List<String> expected = List.of(
 130             "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base",
 131             "module m1 provides p1.S, used by m1",
 132             "module m2 provides p1.S, used by m1",
 133             "module m2 provides p2.T, used by m2",
 134             "module m3 provides p2.T, used by m2"
 135         );
 136 
 137         assertTrue(output.containsAll(expected));
 138     }
 139 
 140     @Test
 141     public void unusedService() throws Throwable {
 142         if (!hasJmods()) return;
 143 
 144         List<String> output =
 145             JLink.run("--module-path", MODULE_PATH,
 146                 "--add-modules", "m1",
 147                 "--suggest-providers",
 148                 "nonExistentType").output();
 149 
 150         System.out.println(output);
 151         List<String> expected = List.of(
 152             "Services specified in --suggest-providers not used: nonExistentType"
 153         );
 154 
 155         assertTrue(output.containsAll(expected));
 156     }
 157 
 158     @Test
 159     public void noSuggestProviders() throws Throwable {
 160         if (!hasJmods()) return;
 161 
 162         List<String> output =
 163             JLink.run("--module-path", MODULE_PATH,
 164                       "--add-modules", "m1",
 165                       "--bind-services",
 166                       "--limit-modules", "m1,m2,m3,java.base",
 167                       "--suggest-providers").output();
 168 
 169         String expected = "--bind-services option is specified. No additional providers suggested.";
 170         assertTrue(output.contains(expected));
 171 
 172     }
 173 
 174     static class JLink {
 175         static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 176             .orElseThrow(() ->
 177                 new RuntimeException("jlink tool not found")
 178             );
 179 
 180         static JLink run(String... options) {
 181             JLink jlink = new JLink();
 182             assertTrue(jlink.execute(options) == 0);
 183             return jlink;
 184         }
 185 
 186         final List<String> output = new ArrayList<>();
 187         private int execute(String... options) {
 188             System.out.println("jlink " +
 189                 Stream.of(options).collect(Collectors.joining(" ")));
 190 
 191             StringWriter writer = new StringWriter();
 192             PrintWriter pw = new PrintWriter(writer);
 193             int rc = JLINK_TOOL.run(pw, pw, options);
 194             System.out.println(writer.toString());
 195             Stream.of(writer.toString().split("\\v"))
 196                   .map(String::trim)
 197                   .forEach(output::add);
 198             return rc;
 199         }
 200 
 201         boolean contains(String s) {
 202             return output.contains(s);
 203         }
 204 
 205         List<String> output() {
 206             return output;
 207         }
 208     }
 209 }