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