1 /*
   2  * Copyright (c) 2015, 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.FileWriter;
  26 import java.io.Reader;
  27 import java.io.IOException;
  28 import java.io.InputStream;
  29 import java.io.InputStreamReader;
  30 import java.io.SequenceInputStream;
  31 import java.io.StringWriter;
  32 import java.io.Writer;
  33 import java.nio.file.Files;
  34 import java.nio.file.Path;
  35 import java.nio.file.Paths;
  36 import java.util.ArrayList;
  37 import java.util.Collection;
  38 import java.util.Collections;
  39 import java.util.List;
  40 import java.util.function.Consumer;
  41 import java.util.stream.Collectors;
  42 import java.util.stream.Stream;
  43 import javax.tools.JavaCompiler;
  44 import javax.tools.JavaFileObject;
  45 import javax.tools.StandardJavaFileManager;
  46 import javax.tools.StandardLocation;
  47 import javax.tools.ToolProvider;
  48 import jdk.test.lib.util.FileUtils;
  49 import jdk.testlibrary.JDKToolFinder;
  50 import static java.lang.String.format;
  51 import static java.util.Arrays.asList;
  52 
  53 /*
  54  * @test
  55  * @bug 8064924
  56  * @modules jdk.compiler
  57  * @summary Basic test for URLStreamHandlerProvider
  58  * @library /lib/testlibrary /test/lib
  59  * @build jdk.test.lib.Platform
  60  *        jdk.test.lib.util.FileUtils
  61  *        jdk.testlibrary.JDKToolFinder
  62  * @compile Basic.java Child.java
  63  * @run main Basic
  64  */
  65 
  66 public class Basic {
  67 
  68     static final Path TEST_SRC = Paths.get(System.getProperty("test.src", "."));
  69     static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
  70 
  71     public static void main(String[] args) throws Throwable {
  72         unknownProtocol("foo", UNKNOWN);
  73         unknownProtocol("bar", UNKNOWN);
  74         viaProvider("baz", KNOWN);
  75         viaProvider("bert", KNOWN);
  76         viaProvider("ernie", UNKNOWN, "-Djava.security.manager");
  77         viaProvider("curly", UNKNOWN, "-Djava.security.manager");
  78         viaProvider("larry", KNOWN, "-Djava.security.manager",
  79                 "-Djava.security.policy=" + TEST_SRC + File.separator + "basic.policy");
  80         viaProvider("moe", KNOWN, "-Djava.security.manager",
  81                 "-Djava.security.policy=" + TEST_SRC + File.separator + "basic.policy");
  82         viaBadProvider("tom", SCE);
  83         viaBadProvider("jerry", SCE);
  84     }
  85 
  86     static final Consumer<Result> KNOWN = r -> {
  87         if (r.exitValue != 0 || !r.output.isEmpty())
  88             throw new RuntimeException(r.output);
  89     };
  90     static final Consumer<Result> UNKNOWN = r -> {
  91         if (r.exitValue == 0 ||
  92             !r.output.contains("java.net.MalformedURLException: unknown protocol")) {
  93             throw new RuntimeException("exitValue: "+ r.exitValue + ", output:[" +r.output +"]");
  94         }
  95     };
  96     static final Consumer<Result> SCE = r -> {
  97         if (r.exitValue == 0 ||
  98             !r.output.contains("java.util.ServiceConfigurationError")) {
  99             throw new RuntimeException("exitValue: "+ r.exitValue + ", output:[" +r.output +"]");
 100         }
 101     };
 102 
 103     static void unknownProtocol(String protocol, Consumer<Result> resultChecker) {
 104         System.out.println("\nTesting " + protocol);
 105         Result r = java(Collections.emptyList(), asList(TEST_CLASSES),
 106                 "Child", protocol);
 107         resultChecker.accept(r);
 108     }
 109 
 110     static void viaProvider(String protocol, Consumer<Result> resultChecker,
 111                             String... sysProps)
 112         throws Exception
 113     {
 114         viaProviderWithTemplate(protocol, resultChecker,
 115                                 TEST_SRC.resolve("provider.template"),
 116                                 sysProps);
 117     }
 118 
 119     static void viaBadProvider(String protocol, Consumer<Result> resultChecker,
 120                                String... sysProps)
 121         throws Exception
 122     {
 123         viaProviderWithTemplate(protocol, resultChecker,
 124                                 TEST_SRC.resolve("bad.provider.template"),
 125                                 sysProps);
 126     }
 127 
 128     static void viaProviderWithTemplate(String protocol,
 129                                         Consumer<Result> resultChecker,
 130                                         Path template, String... sysProps)
 131         throws Exception
 132     {
 133         System.out.println("\nTesting " + protocol);
 134         Path testRoot = Paths.get("URLStreamHandlerProvider-" + protocol);
 135         if (Files.exists(testRoot))
 136             FileUtils.deleteFileTreeWithRetry(testRoot);
 137         Files.createDirectory(testRoot);
 138 
 139         Path srcPath = Files.createDirectory(testRoot.resolve("src"));
 140         Path srcClass = createProvider(protocol, template, srcPath);
 141 
 142         Path build = Files.createDirectory(testRoot.resolve("build"));
 143         javac(build, srcClass);
 144         createServices(build, protocol);
 145         Path testJar = testRoot.resolve("test.jar");
 146         jar(testJar, build);
 147 
 148         List<String> props = new ArrayList<>();
 149         for (String p : sysProps)
 150             props.add(p);
 151 
 152         Result r = java(props, asList(testJar, TEST_CLASSES),
 153                         "Child", protocol);
 154 
 155         resultChecker.accept(r);
 156     }
 157 
 158     static String platformPath(String p) { return p.replace("/", File.separator); }
 159     static String binaryName(String name) { return name.replace(".", "/"); }
 160 
 161     static final String SERVICE_IMPL_PREFIX = "net.java.openjdk.test";
 162 
 163     static void createServices(Path dst, String protocol) throws IOException {
 164         Path services = Files.createDirectories(dst.resolve("META-INF")
 165                                                    .resolve("services"));
 166 
 167         final String implName =  SERVICE_IMPL_PREFIX + "." + protocol + ".Provider";
 168         Path s = services.resolve("java.net.spi.URLStreamHandlerProvider");
 169         FileWriter fw = new FileWriter(s.toFile());
 170         try {
 171             fw.write(implName);
 172         } finally {
 173             fw.close();
 174         }
 175     }
 176 
 177     static Path createProvider(String protocol, Path srcTemplate, Path dst)
 178         throws IOException
 179     {
 180         String pkg = SERVICE_IMPL_PREFIX + "." + protocol;
 181         Path classDst = dst.resolve(platformPath(binaryName(pkg)));
 182         Files.createDirectories(classDst);
 183         Path classPath = classDst.resolve("Provider.java");
 184 
 185         List<String> lines = Files.lines(srcTemplate)
 186                                   .map(s -> s.replaceAll("\\$package", pkg))
 187                                   .map(s -> s.replaceAll("\\$protocol", protocol))
 188                                   .collect(Collectors.toList());
 189         Files.write(classPath, lines);
 190 
 191         return classPath;
 192     }
 193 
 194     static void jar(Path jarName, Path jarRoot) { String jar = getJDKTool("jar");
 195         ProcessBuilder p = new ProcessBuilder(jar, "cf", jarName.toString(),
 196                 "-C", jarRoot.toString(), ".");
 197         quickFail(run(p));
 198     }
 199 
 200     static void javac(Path dest, Path... sourceFiles) throws IOException {
 201         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 202         try (StandardJavaFileManager fileManager =
 203                     compiler.getStandardFileManager(null, null, null)) {
 204 
 205             List<File> files = Stream.of(sourceFiles)
 206                     .map(p -> p.toFile())
 207                     .collect(Collectors.toList());
 208             List<File> dests = Stream.of(dest)
 209                     .map(p -> p.toFile())
 210                     .collect(Collectors.toList());
 211             Iterable<? extends JavaFileObject> compilationUnits =
 212                     fileManager.getJavaFileObjectsFromFiles(files);
 213             fileManager.setLocation(StandardLocation.CLASS_OUTPUT, dests);
 214             JavaCompiler.CompilationTask task =
 215                     compiler.getTask(null, fileManager, null, null, null, compilationUnits);
 216             boolean passed = task.call();
 217             if (!passed)
 218                 throw new RuntimeException("Error compiling " + files);
 219         }
 220     }
 221 
 222     static void quickFail(Result r) {
 223         if (r.exitValue != 0)
 224             throw new RuntimeException(r.output);
 225     }
 226 
 227     static Result java(List<String> sysProps, Collection<Path> classpath,
 228                        String classname, String arg) {
 229         String java = getJDKTool("java");
 230 
 231         List<String> commands = new ArrayList<>();
 232         commands.add(java);
 233         for (String prop : sysProps)
 234             commands.add(prop);
 235 
 236         String cp = classpath.stream()
 237                 .map(Path::toString)
 238                 .collect(Collectors.joining(File.pathSeparator));
 239         commands.add("-cp");
 240         commands.add(cp);
 241         commands.add(classname);
 242         commands.add(arg);
 243 
 244         return run(new ProcessBuilder(commands));
 245     }
 246 
 247     static Result run(ProcessBuilder pb) {
 248         Process p = null;
 249         System.out.println("running: " + pb.command());
 250         try {
 251             p = pb.start();
 252         } catch (IOException e) {
 253             throw new RuntimeException(
 254                     format("Couldn't start process '%s'", pb.command()), e);
 255         }
 256 
 257         String output;
 258         try {
 259             output = toString(p.getInputStream(), p.getErrorStream());
 260         } catch (IOException e) {
 261             throw new RuntimeException(
 262                     format("Couldn't read process output '%s'", pb.command()), e);
 263         }
 264 
 265         try {
 266             p.waitFor();
 267         } catch (InterruptedException e) {
 268             throw new RuntimeException(
 269                     format("Process hasn't finished '%s'", pb.command()), e);
 270         }
 271 
 272         return new Result(p.exitValue(), output);
 273     }
 274 
 275     static final String DEFAULT_IMAGE_BIN = System.getProperty("java.home")
 276             + File.separator + "bin" + File.separator;
 277 
 278     static String getJDKTool(String name) {
 279         try {
 280             return JDKToolFinder.getJDKTool(name);
 281         } catch (Exception x) {
 282             return DEFAULT_IMAGE_BIN + name;
 283         }
 284     }
 285 
 286     static String toString(InputStream... src) throws IOException {
 287         StringWriter dst = new StringWriter();
 288         Reader concatenated =
 289                 new InputStreamReader(
 290                         new SequenceInputStream(
 291                                 Collections.enumeration(asList(src))));
 292         copy(concatenated, dst);
 293         return dst.toString();
 294     }
 295 
 296     static void copy(Reader src, Writer dst) throws IOException {
 297         int len;
 298         char[] buf = new char[1024];
 299         try {
 300             while ((len = src.read(buf)) != -1)
 301                 dst.write(buf, 0, len);
 302         } finally {
 303             try {
 304                 src.close();
 305             } catch (IOException ignored1) {
 306             } finally {
 307                 try {
 308                     dst.close();
 309                 } catch (IOException ignored2) {
 310                 }
 311             }
 312         }
 313     }
 314 
 315     static class Result {
 316         final int exitValue;
 317         final String output;
 318 
 319         private Result(int exitValue, String output) {
 320             this.exitValue = exitValue;
 321             this.output = output;
 322         }
 323     }
 324 }