1 /*
  2  * Copyright (c) 2015, 2021, 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.test.lib.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 /test/lib
 59  * @build jdk.test.lib.Platform
 60  *        jdk.test.lib.util.FileUtils
 61  *        jdk.test.lib.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 String SECURITY_MANAGER_DEPRECATED
 87             = "WARNING: The Security Manager is deprecated and will be removed in a future release."
 88                     + System.getProperty("line.separator");
 89 
 90     private static String withoutWarning(String in) {
 91         return in.lines().filter(s -> !s.startsWith("WARNING:")).collect(Collectors.joining());
 92     }
 93 
 94     static final Consumer<Result> KNOWN = r -> {
 95         if (r.exitValue != 0 || !withoutWarning(r.output).isEmpty())
 96             throw new RuntimeException("[" + r.output + "]");
 97     };
 98     static final Consumer<Result> UNKNOWN = r -> {
 99         if (r.exitValue == 0 ||
100             !r.output.contains("java.net.MalformedURLException: unknown protocol")) {
101             throw new RuntimeException("exitValue: "+ r.exitValue + ", output:[" +r.output +"]");
102         }
103     };
104     static final Consumer<Result> SCE = r -> {
105         if (r.exitValue == 0 ||
106             !r.output.contains("java.util.ServiceConfigurationError")) {
107             throw new RuntimeException("exitValue: "+ r.exitValue + ", output:[" +r.output +"]");
108         }
109     };
110 
111     static void unknownProtocol(String protocol, Consumer<Result> resultChecker) {
112         System.out.println("\nTesting " + protocol);
113         Result r = java(Collections.emptyList(), asList(TEST_CLASSES),
114                 "Child", protocol);
115         resultChecker.accept(r);
116     }
117 
118     static void viaProvider(String protocol, Consumer<Result> resultChecker,
119                             String... sysProps)
120         throws Exception
121     {
122         viaProviderWithTemplate(protocol, resultChecker,
123                                 TEST_SRC.resolve("provider.template"),
124                                 sysProps);
125     }
126 
127     static void viaBadProvider(String protocol, Consumer<Result> resultChecker,
128                                String... sysProps)
129         throws Exception
130     {
131         viaProviderWithTemplate(protocol, resultChecker,
132                                 TEST_SRC.resolve("bad.provider.template"),
133                                 sysProps);
134     }
135 
136     static void viaProviderWithTemplate(String protocol,
137                                         Consumer<Result> resultChecker,
138                                         Path template, String... sysProps)
139         throws Exception
140     {
141         System.out.println("\nTesting " + protocol);
142         Path testRoot = Paths.get("URLStreamHandlerProvider-" + protocol);
143         if (Files.exists(testRoot))
144             FileUtils.deleteFileTreeWithRetry(testRoot);
145         Files.createDirectory(testRoot);
146 
147         Path srcPath = Files.createDirectory(testRoot.resolve("src"));
148         Path srcClass = createProvider(protocol, template, srcPath);
149 
150         Path build = Files.createDirectory(testRoot.resolve("build"));
151         javac(build, srcClass);
152         createServices(build, protocol);
153         Path testJar = testRoot.resolve("test.jar");
154         jar(testJar, build);
155 
156         List<String> props = new ArrayList<>();
157         for (String p : sysProps)
158             props.add(p);
159 
160         Result r = java(props, asList(testJar, TEST_CLASSES),
161                         "Child", protocol);
162 
163         resultChecker.accept(r);
164     }
165 
166     static String platformPath(String p) { return p.replace("/", File.separator); }
167     static String binaryName(String name) { return name.replace(".", "/"); }
168 
169     static final String SERVICE_IMPL_PREFIX = "net.java.openjdk.test";
170 
171     static void createServices(Path dst, String protocol) throws IOException {
172         Path services = Files.createDirectories(dst.resolve("META-INF")
173                                                    .resolve("services"));
174 
175         final String implName =  SERVICE_IMPL_PREFIX + "." + protocol + ".Provider";
176         Path s = services.resolve("java.net.spi.URLStreamHandlerProvider");
177         FileWriter fw = new FileWriter(s.toFile());
178         try {
179             fw.write(implName);
180         } finally {
181             fw.close();
182         }
183     }
184 
185     static Path createProvider(String protocol, Path srcTemplate, Path dst)
186         throws IOException
187     {
188         String pkg = SERVICE_IMPL_PREFIX + "." + protocol;
189         Path classDst = dst.resolve(platformPath(binaryName(pkg)));
190         Files.createDirectories(classDst);
191         Path classPath = classDst.resolve("Provider.java");
192 
193         List<String> lines = Files.lines(srcTemplate)
194                                   .map(s -> s.replaceAll("\\$package", pkg))
195                                   .map(s -> s.replaceAll("\\$protocol", protocol))
196                                   .collect(Collectors.toList());
197         Files.write(classPath, lines);
198 
199         return classPath;
200     }
201 
202     static void jar(Path jarName, Path jarRoot) { String jar = getJDKTool("jar");
203         ProcessBuilder p = new ProcessBuilder(jar, "cf", jarName.toString(),
204                 "-C", jarRoot.toString(), ".");
205         quickFail(run(p));
206     }
207 
208     static void javac(Path dest, Path... sourceFiles) throws IOException {
209         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
210         try (StandardJavaFileManager fileManager =
211                     compiler.getStandardFileManager(null, null, null)) {
212 
213             List<File> files = Stream.of(sourceFiles)
214                     .map(p -> p.toFile())
215                     .collect(Collectors.toList());
216             List<File> dests = Stream.of(dest)
217                     .map(p -> p.toFile())
218                     .collect(Collectors.toList());
219             Iterable<? extends JavaFileObject> compilationUnits =
220                     fileManager.getJavaFileObjectsFromFiles(files);
221             fileManager.setLocation(StandardLocation.CLASS_OUTPUT, dests);
222             JavaCompiler.CompilationTask task =
223                     compiler.getTask(null, fileManager, null, null, null, compilationUnits);
224             boolean passed = task.call();
225             if (!passed)
226                 throw new RuntimeException("Error compiling " + files);
227         }
228     }
229 
230     static void quickFail(Result r) {
231         if (r.exitValue != 0)
232             throw new RuntimeException(r.output);
233     }
234 
235     static Result java(List<String> sysProps, Collection<Path> classpath,
236                        String classname, String arg) {
237         String java = getJDKTool("java");
238 
239         List<String> commands = new ArrayList<>();
240         commands.add(java);
241         for (String prop : sysProps)
242             commands.add(prop);
243 
244         String cp = classpath.stream()
245                 .map(Path::toString)
246                 .collect(Collectors.joining(File.pathSeparator));
247         commands.add("-cp");
248         commands.add(cp);
249         commands.add(classname);
250         commands.add(arg);
251 
252         return run(new ProcessBuilder(commands));
253     }
254 
255     static Result run(ProcessBuilder pb) {
256         Process p = null;
257         System.out.println("running: " + pb.command());
258         try {
259             p = pb.start();
260         } catch (IOException e) {
261             throw new RuntimeException(
262                     format("Couldn't start process '%s'", pb.command()), e);
263         }
264 
265         String output;
266         try {
267             output = toString(p.getInputStream(), p.getErrorStream());
268         } catch (IOException e) {
269             throw new RuntimeException(
270                     format("Couldn't read process output '%s'", pb.command()), e);
271         }
272 
273         try {
274             p.waitFor();
275         } catch (InterruptedException e) {
276             throw new RuntimeException(
277                     format("Process hasn't finished '%s'", pb.command()), e);
278         }
279 
280         return new Result(p.exitValue(), output);
281     }
282 
283     static final String DEFAULT_IMAGE_BIN = System.getProperty("java.home")
284             + File.separator + "bin" + File.separator;
285 
286     static String getJDKTool(String name) {
287         try {
288             return JDKToolFinder.getJDKTool(name);
289         } catch (Exception x) {
290             return DEFAULT_IMAGE_BIN + name;
291         }
292     }
293 
294     static String toString(InputStream... src) throws IOException {
295         StringWriter dst = new StringWriter();
296         Reader concatenated =
297                 new InputStreamReader(
298                         new SequenceInputStream(
299                                 Collections.enumeration(asList(src))));
300         copy(concatenated, dst);
301         return dst.toString();
302     }
303 
304     static void copy(Reader src, Writer dst) throws IOException {
305         int len;
306         char[] buf = new char[1024];
307         try {
308             while ((len = src.read(buf)) != -1)
309                 dst.write(buf, 0, len);
310         } finally {
311             try {
312                 src.close();
313             } catch (IOException ignored1) {
314             } finally {
315                 try {
316                     dst.close();
317                 } catch (IOException ignored2) {
318                 }
319             }
320         }
321     }
322 
323     static class Result {
324         final int exitValue;
325         final String output;
326 
327         private Result(int exitValue, String output) {
328             this.exitValue = exitValue;
329             this.output = output;
330         }
331     }
332 }