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