jdk/test/java/net/URLConnection/ContentHandlers/ContentHandlersTest.java
changeset 31642 7ae76e376fcd
equal deleted inserted replaced
31564:9b3a9d72f07b 31642:7ae76e376fcd
       
     1 /*
       
     2  * Copyright (c) 2015, 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.BufferedReader;
       
    25 import java.io.BufferedWriter;
       
    26 import java.io.File;
       
    27 import java.io.FileReader;
       
    28 import java.io.FileWriter;
       
    29 import java.io.IOException;
       
    30 import java.io.InputStream;
       
    31 import java.io.InputStreamReader;
       
    32 import java.io.Reader;
       
    33 import java.io.SequenceInputStream;
       
    34 import java.io.StringWriter;
       
    35 import java.io.Writer;
       
    36 import java.nio.file.Files;
       
    37 import java.nio.file.Path;
       
    38 import java.nio.file.Paths;
       
    39 import java.util.ArrayList;
       
    40 import java.util.Arrays;
       
    41 import java.util.Collection;
       
    42 import java.util.Collections;
       
    43 import java.util.Iterator;
       
    44 import java.util.List;
       
    45 import java.util.Map;
       
    46 import java.util.function.Function;
       
    47 import java.util.stream.Collectors;
       
    48 import java.util.stream.Stream;
       
    49 
       
    50 import static java.lang.String.format;
       
    51 import static java.util.Arrays.asList;
       
    52 import static java.util.Collections.emptyMap;
       
    53 import static java.util.Collections.singleton;
       
    54 import static java.util.Collections.singletonMap;
       
    55 
       
    56 /*
       
    57  * @test
       
    58  * @bug 8064925
       
    59  * @summary Basic test for ContentHandler. Ensures discovery paths for content
       
    60  *          handlers follow a particular order.
       
    61  */
       
    62 public class ContentHandlersTest {
       
    63 
       
    64     public static void main(String[] args) throws Throwable {
       
    65         step1_ContentHandlerFactory();
       
    66         step2_ServiceLoader();
       
    67         step3_UserDefined();
       
    68         step4_BuiltIn();
       
    69     }
       
    70 
       
    71     private static void step1_ContentHandlerFactory() throws IOException {
       
    72         String factoryClassFqn = "net.java.openjdk.test.TestContentHandlerFactory";
       
    73 
       
    74         Path tmp = Files.createDirectory(Paths.get("ContentHandlersTest-1"));
       
    75 
       
    76         Path src = templatesHome().resolve("test.template");
       
    77         Path dst = tmp.resolve("Test.java");
       
    78         Files.copy(src, dst);
       
    79 
       
    80         Path build = Files.createDirectory(tmp.resolve("build"));
       
    81 
       
    82         Path dst1 = fromTemplate(templatesHome().resolve("broken_factory.template"),
       
    83                 factoryClassFqn, tmp);
       
    84 
       
    85         javac(build, dst, dst1);
       
    86 
       
    87         Result r = java(emptyMap(), singleton(build), "Test", factoryClassFqn);
       
    88 
       
    89         if (r.exitValue == 0 || !r.output.startsWith(
       
    90                 stackTraceStringForBrokenFactory(factoryClassFqn))) {
       
    91             throw new RuntimeException(
       
    92                     "Expected a different kind of failure: " + r.output);
       
    93         }
       
    94     }
       
    95 
       
    96     private static void step2_ServiceLoader() throws IOException {
       
    97         String factoryClassFqn = "net.java.openjdk.test.TestContentHandlerFactory";
       
    98 
       
    99         Path tmp = Files.createDirectory(Paths.get("ContentHandlersTest-2"));
       
   100 
       
   101         Path src = templatesHome().resolve("test.template");
       
   102         Path dst = tmp.resolve("Test.java");
       
   103         Files.copy(src, dst);
       
   104 
       
   105         Path dst1 = fromTemplate(templatesHome().resolve("broken_constructor_factory.template"),
       
   106                 factoryClassFqn, tmp);
       
   107 
       
   108         Path build = Files.createDirectory(tmp.resolve("build"));
       
   109 
       
   110         javac(build, dst);
       
   111 
       
   112         Path explodedJar = Files.createDirectory(tmp.resolve("exploded-jar"));
       
   113         Path services = Files.createDirectories(explodedJar.resolve("META-INF")
       
   114                 .resolve("services"));
       
   115 
       
   116         Path s = services.resolve("java.net.ContentHandlerFactory");
       
   117 
       
   118         try (FileWriter fw = new FileWriter(s.toFile())) {
       
   119             fw.write(factoryClassFqn);
       
   120         }
       
   121 
       
   122         javac(explodedJar, dst1);
       
   123         jar(tmp.resolve("test.jar"), explodedJar);
       
   124 
       
   125         Files.copy(tmp.resolve("test.jar"), build.resolve("test.jar"));
       
   126 
       
   127         Result r = java(emptyMap(), asList(build.resolve("test.jar"), build), "Test");
       
   128 
       
   129         if (r.exitValue == 0 || !verifyOutput(r.output, factoryClassFqn))
       
   130             throw new RuntimeException(r.output);
       
   131     }
       
   132 
       
   133     private static void step3_UserDefined() throws IOException {
       
   134         String packagePrefix = "net.java.openjdk.test";
       
   135         String fqn = packagePrefix + ".text.plain";
       
   136 
       
   137         Path tmp = Files.createDirectory(Paths.get("ContentHandlersTest-3"));
       
   138 
       
   139         Path src = templatesHome().resolve("test.template");
       
   140         Path dst = tmp.resolve("Test.java");
       
   141         Files.copy(src, dst);
       
   142 
       
   143         Path dst1 = fromTemplate(templatesHome().resolve("plain.template"),
       
   144                 fqn, tmp);
       
   145 
       
   146         Path build = Files.createDirectory(tmp.resolve("build"));
       
   147 
       
   148         javac(build, dst);
       
   149 
       
   150         Path classes = Files.createDirectory(tmp.resolve("classes"));
       
   151 
       
   152         javac(classes, dst1);
       
   153 
       
   154         Map<String, String> m = singletonMap("java.content.handler.pkgs", packagePrefix);
       
   155         Result r = java(m, asList(build, classes), "Test");
       
   156 
       
   157         if (r.exitValue != 0 || !r.output.contains(fqn))
       
   158             throw new RuntimeException(r.output);
       
   159     }
       
   160 
       
   161     private static void step4_BuiltIn() throws IOException {
       
   162         Path tmp = Files.createDirectory(Paths.get("ContentHandlersTest-4"));
       
   163 
       
   164         Path src = templatesHome().resolve("test.template");
       
   165         Path dst = tmp.resolve("Test.java");
       
   166         Files.copy(src, dst);
       
   167 
       
   168         Path build = Files.createDirectory(tmp.resolve("build"));
       
   169 
       
   170         javac(build, dst);
       
   171 
       
   172         Result r = java(emptyMap(), singleton(build), "Test");
       
   173 
       
   174         if (r.exitValue != 0 || !r.output.contains("sun.net.www.content.text.PlainTextInputStream"))
       
   175             throw new RuntimeException(r.output);
       
   176     }
       
   177 
       
   178     private static String stackTraceStringForBrokenFactory(String fqn) {
       
   179         return "Exception in thread \"main\" java.lang.RuntimeException: " +
       
   180                 "This is a broken factory. It is supposed to throw this exception.";
       
   181     }
       
   182 
       
   183     private static Path fromTemplate(Path srcTemplate,
       
   184                                      String factoryFqn,
       
   185                                      Path dstFolder) throws IOException {
       
   186 
       
   187         String factorySimpleName, packageName;
       
   188         int i = factoryFqn.lastIndexOf('.');
       
   189         if (i < 0) {
       
   190             packageName = "";
       
   191             factorySimpleName = factoryFqn;
       
   192         } else {
       
   193             packageName = factoryFqn.substring(0, i);
       
   194             factorySimpleName = factoryFqn.substring(i + 1);
       
   195         }
       
   196 
       
   197         Path result = dstFolder.resolve(factorySimpleName + ".java");
       
   198         File dst = result.toFile();
       
   199         File src = srcTemplate.toFile();
       
   200         try (BufferedReader r = new BufferedReader(new FileReader(src));
       
   201              BufferedWriter w = new BufferedWriter(new FileWriter(dst))) {
       
   202 
       
   203             List<String> lines = processTemplate(packageName, factorySimpleName,
       
   204                     r.lines()).collect(Collectors.toList());
       
   205 
       
   206             Iterator<String> it = lines.iterator();
       
   207             if (it.hasNext())
       
   208                 w.write(it.next());
       
   209             while (it.hasNext()) {
       
   210                 w.newLine();
       
   211                 w.write(it.next());
       
   212             }
       
   213         }
       
   214         return result;
       
   215     }
       
   216 
       
   217     private static Stream<String> processTemplate(String packageName,
       
   218                                                   String factorySimpleName,
       
   219                                                   Stream<String> lines) {
       
   220         Function<String, String> pckg;
       
   221 
       
   222         if (packageName.isEmpty()) {
       
   223             pckg = s -> s.contains("$package") ? "" : s;
       
   224         } else {
       
   225             pckg = s -> s.replaceAll("\\$package", packageName);
       
   226         }
       
   227 
       
   228         Function<String, String> factory
       
   229                 = s -> s.replaceAll("\\$className", factorySimpleName);
       
   230 
       
   231         return lines.map(pckg).map(factory);
       
   232     }
       
   233 
       
   234     // IMO, that's the easiest way that gives you a fair amount of confidence in
       
   235     // that j.u.ServiceLoader is loading a factory rather than Class.forName
       
   236     private static boolean verifyOutput(String output, String fqn) {
       
   237         String s1 = String.format("java.util.ServiceConfigurationError: " +
       
   238                 "java.net.ContentHandlerFactory: " +
       
   239                 "Provider %s could not be instantiated", fqn);
       
   240 
       
   241         return output.contains(s1);
       
   242     }
       
   243 
       
   244     private static void jar(Path jarName, Path jarRoot) {
       
   245         String jar = getJDKTool("jar");
       
   246         ProcessBuilder p = new ProcessBuilder(jar, "cf", jarName.toString(),
       
   247                 "-C", jarRoot.toString(), ".");
       
   248         quickFail(run(p));
       
   249     }
       
   250 
       
   251     private static void javac(Path compilationOutput, Path... sourceFiles) {
       
   252         String javac = getJDKTool("javac");
       
   253         List<String> commands = new ArrayList<>();
       
   254         commands.addAll(asList(javac, "-d", compilationOutput.toString()));
       
   255         List<Path> paths = asList(sourceFiles);
       
   256         commands.addAll(paths.stream()
       
   257                 .map(Path::toString)
       
   258                 .collect(Collectors.toList()));
       
   259         quickFail(run(new ProcessBuilder(commands)));
       
   260     }
       
   261 
       
   262     private static void quickFail(Result r) {
       
   263         if (r.exitValue != 0)
       
   264             throw new RuntimeException(r.output);
       
   265     }
       
   266 
       
   267     private static Result java(Map<String, String> properties,
       
   268                                Collection<Path> classpath,
       
   269                                String classname, String... args) {
       
   270 
       
   271         String java = getJDKTool("java");
       
   272 
       
   273         List<String> commands = new ArrayList<>();
       
   274         commands.add(java);
       
   275         commands.addAll(properties.entrySet()
       
   276                 .stream()
       
   277                 .map(e -> "-D" + e.getKey() + "=" + e.getValue())
       
   278                 .collect(Collectors.toList()));
       
   279 
       
   280         String cp = classpath.stream()
       
   281                 .map(Path::toString)
       
   282                 .collect(Collectors.joining(File.pathSeparator));
       
   283         commands.add("-cp");
       
   284         commands.add(cp);
       
   285         commands.add(classname);
       
   286         commands.addAll(Arrays.asList(args));
       
   287 
       
   288         return run(new ProcessBuilder(commands));
       
   289     }
       
   290 
       
   291     private static Result run(ProcessBuilder b) {
       
   292         Process p;
       
   293         try {
       
   294             p = b.start();
       
   295         } catch (IOException e) {
       
   296             throw new RuntimeException(
       
   297                     format("Couldn't start process '%s'", b.command()), e);
       
   298         }
       
   299 
       
   300         String output;
       
   301         try {
       
   302             output = toString(p.getInputStream(), p.getErrorStream());
       
   303         } catch (IOException e) {
       
   304             throw new RuntimeException(
       
   305                     format("Couldn't read process output '%s'", b.command()), e);
       
   306         }
       
   307 
       
   308         try {
       
   309             p.waitFor();
       
   310         } catch (InterruptedException e) {
       
   311             throw new RuntimeException(
       
   312                     format("Process hasn't finished '%s'", b.command()), e);
       
   313         }
       
   314 
       
   315         return new Result(p.exitValue(), output);
       
   316     }
       
   317 
       
   318     private static String getJDKTool(String name) {
       
   319         String testJdk = System.getProperty("test.jdk");
       
   320         if (testJdk == null)
       
   321             throw new RuntimeException("Please provide test.jdk property at a startup");
       
   322         return testJdk + File.separator + "bin" + File.separator + name;
       
   323     }
       
   324 
       
   325     private static Path templatesHome() {
       
   326         String testSrc = System.getProperty("test.src");
       
   327         if (testSrc == null)
       
   328             throw new RuntimeException("Please provide test.src property at a startup");
       
   329         return Paths.get(testSrc);
       
   330     }
       
   331 
       
   332     private static String toString(InputStream... src) throws IOException {
       
   333         StringWriter dst = new StringWriter();
       
   334         Reader concatenated =
       
   335                 new InputStreamReader(
       
   336                         new SequenceInputStream(
       
   337                                 Collections.enumeration(asList(src))));
       
   338         copy(concatenated, dst);
       
   339         return dst.toString();
       
   340     }
       
   341 
       
   342     private static void copy(Reader src, Writer dst) throws IOException {
       
   343         int len;
       
   344         char[] buf = new char[1024];
       
   345         try {
       
   346             while ((len = src.read(buf)) != -1)
       
   347                 dst.write(buf, 0, len);
       
   348         } finally {
       
   349             try {
       
   350                 src.close();
       
   351             } catch (IOException ignored1) {
       
   352             } finally {
       
   353                 try {
       
   354                     dst.close();
       
   355                 } catch (IOException ignored2) {
       
   356                 }
       
   357             }
       
   358         }
       
   359     }
       
   360 
       
   361     private static class Result {
       
   362 
       
   363         final int exitValue;
       
   364         final String output;
       
   365 
       
   366         private Result(int exitValue, String output) {
       
   367             this.exitValue = exitValue;
       
   368             this.output = output;
       
   369         }
       
   370     }
       
   371 }