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
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.test.lib.util.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 /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
66public 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}
325