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