1 /*
   2  * Copyright (c) 2016, 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 
  24 import java.nio.file.Files;
  25 import java.nio.file.Path;
  26 import java.nio.file.Paths;
  27 import java.nio.file.StandardCopyOption;
  28 import java.util.Collections;
  29 import java.util.LinkedList;
  30 import java.util.List;
  31 import java.io.File;
  32 import java.io.OutputStream;
  33 import java.lang.module.ModuleDescriptor;
  34 import java.lang.module.ModuleDescriptor.Builder;
  35 import jdk.internal.module.ModuleInfoWriter;
  36 import java.util.stream.Stream;
  37 import jdk.test.lib.process.ProcessTools;
  38 import jdk.test.lib.process.OutputAnalyzer;
  39 
  40 /*
  41  * @test
  42  * @bug 8151654 8183310
  43  * @summary Test default callback handler with all possible modular option.
  44  * @library /lib/testlibrary /test/lib
  45  * @modules java.base/jdk.internal.module
  46  * @build JarUtils
  47  * @build TestCallbackHandler TestLoginModule JaasClientWithDefaultHandler
  48  * @run main JaasModularDefaultHandlerTest
  49  */
  50 public class JaasModularDefaultHandlerTest {
  51 
  52     private static final Path SRC = Paths.get(System.getProperty("test.src"));
  53     private static final Path TEST_CLASSES
  54             = Paths.get(System.getProperty("test.classes"));
  55     private static final Path ARTIFACT_DIR = Paths.get("jars");
  56     private static final String PS = File.pathSeparator;
  57     private static final String H_TYPE = "handler.TestCallbackHandler";
  58     private static final String C_TYPE = "login.JaasClientWithDefaultHandler";
  59 
  60     /**
  61      * Here is the naming convention followed for each jar.
  62      * h.jar   - Unnamed handler jar.
  63      * mh.jar  - Modular handler jar.
  64      * c.jar   - Unnamed client jar.
  65      * mc.jar  - Modular client jar.
  66      * amc.jar - Modular client used for automatic handler jar.
  67      */
  68     private static final Path H_JAR = artifact("h.jar");
  69     private static final Path MH_JAR = artifact("mh.jar");
  70     private static final Path C_JAR = artifact("c.jar");
  71     private static final Path MC_JAR = artifact("mc.jar");
  72     private static final Path AMC_JAR = artifact("amc.jar");
  73 
  74     private final String unnH;
  75     private final String modH;
  76     private final String unnC;
  77     private final String modC;
  78     private final String autoMC;
  79     // Common set of VM arguments used in all test cases
  80     private final List<String> commonArgs;
  81 
  82     public JaasModularDefaultHandlerTest() {
  83 
  84         List<String> argList = new LinkedList<>();
  85         argList.add("-Djava.security.auth.login.config="
  86                 + toAbsPath(SRC.resolve("jaas.conf")));
  87         commonArgs = Collections.unmodifiableList(argList);
  88 
  89         // Based on Testcase, select unnamed/modular jar files to use.
  90         unnH = toAbsPath(H_JAR);
  91         modH = toAbsPath(MH_JAR);
  92         unnC = toAbsPath(C_JAR);
  93         modC = toAbsPath(MC_JAR);
  94         autoMC = toAbsPath(AMC_JAR);
  95     }
  96 
  97     /*
  98      * Test cases are based on the following logic,
  99      * for (clientType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
 100      *     for (handlerType : {"NAMED", "AUTOMATIC", "UNNAMED"}) {
 101      *         Create and run java command for each possible case
 102      *     }
 103      * }
 104      */
 105     public static void main(String[] args) throws Exception {
 106 
 107         // Generates unnamed and modular jars.
 108         setUp();
 109         JaasModularDefaultHandlerTest jt = new JaasModularDefaultHandlerTest();
 110         jt.process();
 111     }
 112 
 113     private void process() throws Exception {
 114 
 115         // Case: NAMED-NAMED, NAMED-AUTOMATIC, NAMED-UNNAMED
 116         System.out.println("Case: Modular Client and Modular Handler");
 117         execute(String.format("--module-path %s%s%s -m mc/%s %s",
 118                 modC, PS, modH, C_TYPE, H_TYPE));
 119         System.out.println("Case: Modular Client and automatic Handler");
 120         execute(String.format("--module-path %s%s%s --add-modules=h -m mc/%s %s",
 121                 autoMC, PS, unnH, C_TYPE, H_TYPE));
 122         System.out.println("Case: Modular Client and unnamed Handler");
 123         execute(String.format("--module-path %s -cp %s -m mc/%s %s", autoMC,
 124                 unnH, C_TYPE, H_TYPE));
 125 
 126         // Case: AUTOMATIC-NAMED, AUTOMATIC-AUTOMATIC, AUTOMATIC-UNNAMED
 127         System.out.println("Case: Automatic Client and modular Handler");
 128         execute(String.format("--module-path %s%s%s --add-modules=mh -m c/%s %s",
 129                 unnC, PS, modH, C_TYPE, H_TYPE));
 130         System.out.println("Case: Automatic Client and automatic Handler");
 131         execute(String.format("--module-path %s%s%s --add-modules=h -m c/%s %s",
 132                 unnC, PS, unnH, C_TYPE, H_TYPE));
 133         System.out.println("Case: Automatic Client and unnamed Handler");
 134         execute(String.format("--module-path %s -cp %s -m c/%s %s", unnC,
 135                 unnH, C_TYPE, H_TYPE));
 136 
 137         // Case: UNNAMED-NAMED, UNNAMED-AUTOMATIC, UNNAMED-UNNAMED
 138         System.out.println("Case: Unnamed Client and modular Handler");
 139         execute(String.format("-cp %s --module-path %s --add-modules=mh %s %s",
 140                 unnC, modH, C_TYPE, H_TYPE));
 141         System.out.println("Case: Unnamed Client and automatic Handler");
 142         execute(String.format("-cp %s --module-path %s --add-modules=h %s %s",
 143                 unnC, unnH, C_TYPE, H_TYPE));
 144         System.out.println("Case: Unnamed Client and unnamed Handler");
 145         execute(String.format("-cp %s%s%s %s %s", unnC, PS, unnH, C_TYPE,
 146                 H_TYPE));
 147 
 148         // Case: unnamed jars in --module-path and modular jars in -cp.
 149         System.out.println("Case: Unnamed Client and Handler in modulepath");
 150         execute(String.format("--module-path %s%s%s --add-modules=h -m c/%s %s",
 151                 unnC, PS, unnH, C_TYPE, H_TYPE));
 152         System.out.println("Case: Modular Client and Provider in classpath");
 153         execute(String.format("-cp %s%s%s %s %s",
 154                 modC, PS, modH, C_TYPE, H_TYPE));
 155     }
 156 
 157     /**
 158      * Execute with command arguments and process the result.
 159      */
 160     private void execute(String args) throws Exception {
 161 
 162         String[] safeArgs = Stream.concat(commonArgs.stream(),
 163                 Stream.of(args.split("\\s+"))).filter(s -> {
 164             if (s.contains(" ")) {
 165                 throw new RuntimeException("No spaces in args");
 166             }
 167             return !s.isEmpty();
 168         }).toArray(String[]::new);
 169         OutputAnalyzer out = ProcessTools.executeTestJvm(safeArgs);
 170         // Handle response.
 171         if (out.getExitValue() != 0) {
 172             System.out.printf("OUTPUT: %s", out.getOutput());
 173             throw new RuntimeException("FAIL: Unknown failure occured.");
 174         } else {
 175             System.out.println("Passed.");
 176         }
 177     }
 178 
 179     /**
 180      * Creates Unnamed/modular jar files for TestClient and TestClassLoader.
 181      */
 182     private static void setUp() throws Exception {
 183 
 184         if (ARTIFACT_DIR.toFile().exists()) {
 185             System.out.println("Skipping setup: Artifacts already exists.");
 186             return;
 187         }
 188         // Generate unnamed handler jar file.
 189         JarUtils.createJarFile(H_JAR, TEST_CLASSES,
 190                 "handler/TestCallbackHandler.class");
 191         // Generate unnamed client jar file.
 192         JarUtils.createJarFile(C_JAR, TEST_CLASSES,
 193                 "login/TestLoginModule.class",
 194                 "login/JaasClientWithDefaultHandler.class");
 195 
 196         Builder mBuilder = ModuleDescriptor.newModule("mh");
 197         // Modular jar exports package to let the handler type accessible.
 198         generateJar(H_JAR, MH_JAR, mBuilder.exports("handler").build());
 199 
 200         mBuilder = ModuleDescriptor.newModule("mc").exports("login")
 201                 .requires("jdk.security.auth");
 202         // Generate modular client jar file to use automatic handler jar.
 203         generateJar(C_JAR, AMC_JAR, mBuilder.build());
 204         // Generate modular client jar file to use modular handler jar.
 205         generateJar(C_JAR, MC_JAR, mBuilder.requires("mh").build());
 206     }
 207 
 208     /**
 209      * Update Unnamed jars and include module descriptor files.
 210      */
 211     private static void generateJar(Path sjar, Path djar,
 212             ModuleDescriptor mDesc) throws Exception {
 213 
 214         Files.copy(sjar, djar, StandardCopyOption.REPLACE_EXISTING);
 215         Path dir = Files.createTempDirectory("tmp");
 216         if (mDesc != null) {
 217             Path mi = dir.resolve("module-info.class");
 218             try (OutputStream out = Files.newOutputStream(mi)) {
 219                 ModuleInfoWriter.write(mDesc, out);
 220             }
 221             System.out.format("Added 'module-info.class' in '%s'%n", djar);
 222         }
 223         JarUtils.updateJarFile(djar, dir);
 224     }
 225 
 226     /**
 227      * Look for file path in generated jars.
 228      */
 229     private static Path artifact(String file) {
 230         return ARTIFACT_DIR.resolve(file);
 231     }
 232 
 233     /**
 234      * Convert to absolute file path.
 235      */
 236     private static String toAbsPath(Path path) {
 237         return path.toFile().getAbsolutePath();
 238     }
 239 }