1 /*
  2  * Copyright (c) 2017, 2023, 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  /*
 25  * @test
 26  * @bug 8168423
 27  * @summary Different types of ClassLoader running with(out) SecurityManager and
 28  *          (in)valid security policy file.
 29  * @enablePreview
 30  * @modules java.base/jdk.internal.module
 31  * @library /test/lib
 32  * @build jdk.test.lib.util.JarUtils
 33  *        jdk.test.lib.util.ModuleInfoWriter
 34  * @build TestClassLoader TestClient
 35  * @run main ClassLoaderTest -noPolicy
 36  * @run main ClassLoaderTest -validPolicy
 37  * @run main ClassLoaderTest -invalidPolicy
 38  * @run main ClassLoaderTest -noPolicy      -customSCL
 39  * @run main ClassLoaderTest -validPolicy   -customSCL
 40  * @run main ClassLoaderTest -invalidPolicy -customSCL
 41  */
 42 import java.io.File;
 43 import java.io.OutputStream;
 44 import java.nio.file.Files;
 45 import java.nio.file.Path;
 46 import java.nio.file.Paths;
 47 import java.nio.file.StandardCopyOption;
 48 import java.util.stream.Stream;
 49 import java.lang.module.ModuleDescriptor;
 50 import java.util.Collections;
 51 import java.util.LinkedList;
 52 import java.util.List;
 53 import jdk.test.lib.process.ProcessTools;
 54 import jdk.test.lib.util.JarUtils;
 55 import jdk.test.lib.util.ModuleInfoWriter;
 56 
 57 public class ClassLoaderTest {
 58 
 59     private static final String SRC = System.getProperty("test.src");
 60     private static final Path TEST_CLASSES =
 61             Paths.get(System.getProperty("test.classes"));
 62     private static final Path ARTIFACT_DIR = Paths.get("jars");
 63     private static final Path VALID_POLICY = Paths.get(SRC, "valid.policy");
 64     private static final Path INVALID_POLICY
 65             = Paths.get(SRC, "malformed.policy");
 66     /*
 67      * Here is the naming convention followed for each jar.
 68      * cl.jar   - Regular custom class loader jar.
 69      * mcl.jar  - Modular custom class loader jar.
 70      * c.jar    - Regular client jar.
 71      * mc.jar   - Modular client jar.
 72      * amc.jar  - Modular client referring automated custom class loader jar.
 73      */
 74     private static final Path CL_JAR = ARTIFACT_DIR.resolve("cl.jar");
 75     private static final Path MCL_JAR = ARTIFACT_DIR.resolve("mcl.jar");
 76     private static final Path C_JAR = ARTIFACT_DIR.resolve("c.jar");
 77     private static final Path MC_JAR = ARTIFACT_DIR.resolve("mc.jar");
 78     private static final Path AMC_JAR = ARTIFACT_DIR.resolve("amc.jar");
 79 
 80     // Expected output messages
 81     private static final String MISSING_MODULE =
 82             "Module cl not found, required by mc";
 83     private static final String POLICY_ERROR =
 84             "java.security.policy: error parsing file";
 85     private static final String SYSTEM_CL_MSG =
 86             "jdk.internal.loader.ClassLoaders$AppClassLoader";
 87     private static final String CUSTOM_CL_MSG = "cl.TestClassLoader";
 88 
 89     // Member vars
 90     private final boolean useSCL;       // Use default system loader, or custom
 91     private final String smMsg;         // Security manager message, or ""
 92     private final String autoAddModArg; // Flag to add cl modules, or ""
 93     private final String addmodArg;     // Flag to add mcl modules, or ""
 94     private final String expectedStatus;// Expected exit status from client
 95     private final String expectedMsg;   // Expected output message from client
 96 
 97     // Common set of VM arguments used in all test cases
 98     private final List<String> commonArgs;
 99 
100     public ClassLoaderTest(Path policy, boolean useSCL) {
101         this.useSCL = useSCL;
102 
103         List<String> argList = new LinkedList<>();
104         argList.add("-Duser.language=en");
105         argList.add("-Duser.region=US");
106 
107         boolean malformedPolicy = false;
108         if (policy == null) {
109             smMsg = "Without SecurityManager";
110         } else {
111             malformedPolicy = policy.equals(INVALID_POLICY);
112             argList.add("-Djava.security.manager");
113             argList.add("-Djava.security.policy=" +
114                     policy.toFile().getAbsolutePath());
115             smMsg = "With SecurityManager";
116         }
117 
118         if (useSCL) {
119             autoAddModArg = "";
120             addmodArg = "";
121         } else {
122             argList.add("-Djava.system.class.loader=cl.TestClassLoader");
123             autoAddModArg = "--add-modules=cl";
124             addmodArg = "--add-modules=mcl";
125         }
126 
127         if (malformedPolicy) {
128             expectedStatus = "FAIL";
129             expectedMsg = POLICY_ERROR;
130         } else if (useSCL) {
131             expectedStatus = "PASS";
132             expectedMsg = SYSTEM_CL_MSG;
133         } else {
134             expectedStatus = "PASS";
135             expectedMsg = CUSTOM_CL_MSG;
136         }
137         commonArgs = Collections.unmodifiableList(argList);
138     }
139 
140     public static void main(String[] args) throws Exception {
141         Path policy;
142         if (args[0].equals("-noPolicy")) {
143             policy = null;
144         } else if (args[0].equals("-validPolicy")) {
145             policy = VALID_POLICY;
146         } else if (args[0].equals("-invalidPolicy")) {
147             policy = INVALID_POLICY;
148         } else {
149             throw new RuntimeException("Unknown policy arg: " + args[0]);
150         }
151 
152         boolean useSystemLoader = true;
153         if (args.length > 1) {
154             if (args[1].equals("-customSCL")) {
155                 useSystemLoader = false;
156             } else {
157                 throw new RuntimeException("Unknown custom loader arg: " + args[1]);
158             }
159         }
160 
161         ClassLoaderTest test = new ClassLoaderTest(policy, useSystemLoader);
162         setUp();
163         test.processForPolicyFile();
164     }
165 
166     /**
167      * Test cases are based on the following logic,
168      *  given: a policyFile in {none, valid, malformed} and
169      *         a classLoader in {SystemClassLoader, CustomClassLoader}:
170      *  for (clientModule : {"NAMED", "UNNAMED"}) {
171      *      for (classLoaderModule : {"NAMED", "UNNAMED"}) {
172      *          Create and run java command for each possible Test case
173      *      }
174      *  }
175      */
176     private void processForPolicyFile() throws Exception {
177         final String regLoaderLoc = CL_JAR.toFile().getAbsolutePath();
178         final String modLoadrLoc = MCL_JAR.toFile().getAbsolutePath();
179         final String regClientLoc = C_JAR.toFile().getAbsolutePath();
180         final String modClientLoc = MC_JAR.toFile().getAbsolutePath();
181         final String autoModCloc = AMC_JAR.toFile().getAbsolutePath();
182         final String separator = File.pathSeparator;
183 
184         // NAMED-NAMED:
185         System.out.println("Case:- Modular Client and " +
186                 ((useSCL) ? "SystemClassLoader"
187                         : "Modular CustomClassLoader") + " " + smMsg);
188         execute("--module-path", modClientLoc + separator + modLoadrLoc, "-m",
189                 "mc/c.TestClient");
190 
191         // NAMED-UNNAMED:
192         System.out.println("Case:- Modular Client and " + ((useSCL)
193                 ? "SystemClassLoader"
194                 : "Unknown modular CustomClassLoader") + " " + smMsg);
195         execute(new String[] {"--module-path", autoModCloc, "-cp", regLoaderLoc,
196                 "-m", "mc/c.TestClient"},
197                 "FAIL", MISSING_MODULE);
198 
199         // UNNAMED-NAMED:
200         System.out.println("Case:- Unknown modular Client and " +
201                 ((useSCL) ? "SystemClassLoader"
202                       : "Modular CustomClassLoader") + " " + smMsg);
203         execute("-cp", regClientLoc, "--module-path", modLoadrLoc, addmodArg,
204                 "c.TestClient");
205 
206         // UNNAMED-UNNAMED:
207         System.out.println("Case:- Unknown modular Client and " +
208                 ((useSCL) ? "SystemClassLoader"
209                         : "Unknown modular CustomClassLoader") + " " + smMsg);
210         execute("-cp", regClientLoc + separator + regLoaderLoc, "c.TestClient");
211 
212         // Regular jars in module-path
213         System.out.println("Case:- Regular Client and " + ((useSCL)
214                 ? "SystemClassLoader"
215                 : "Unknown modular CustomClassLoader") +
216                 " inside --module-path " + smMsg);
217         execute("--module-path", regClientLoc + separator + regLoaderLoc,
218                 autoAddModArg, "-m", "c/c.TestClient");
219 
220         // Modular jars in class-path
221         System.out.println("Case:- Modular Client and " +
222                 ((useSCL) ? "SystemClassLoader"
223                         : "Modular CustomClassLoader") + " in -cp " + smMsg);
224         execute("-cp", modClientLoc + separator + modLoadrLoc, "c.TestClient");
225     }
226 
227     private void execute(String... args) throws Exception {
228         execute(args, this.expectedStatus, this.expectedMsg);
229     }
230 
231     /**
232      * Execute with command arguments and process the result.
233      */
234     private void execute(String[] args, String status, String msg) throws Exception {
235 
236         // Combine with commonArgs, and perform sanity check
237         String[] safeArgs = Stream.concat(commonArgs.stream(), Stream.of(args))
238                 .filter(s -> {
239                     if (s.contains(" ")) { throw new RuntimeException("No spaces in args");}
240                     return !s.isEmpty();
241                 }).toArray(String[]::new);
242         String out = ProcessTools.executeTestJava(safeArgs).getOutput();
243         // Handle response.
244         if ("PASS".equals(status) && out.contains(msg)) {
245             System.out.println("PASS: Expected Result: " + msg);
246         } else if ("FAIL".equals(status) && out.contains(msg)) {
247             System.out.printf("PASS: Expected Failure: " +  msg);
248         } else if (out.contains("Exception") || out.contains("Error")) {
249             System.out.printf("OUTPUT: %s", out);
250             throw new RuntimeException("FAIL: Unknown Exception.");
251         } else {
252             System.out.printf("OUTPUT: %s", out);
253             throw new RuntimeException("FAIL: Unknown Test case found");
254         }
255     }
256 
257     /**
258      * Creates regular/modular jar files for TestClient and TestClassLoader.
259      */
260     private static void setUp() throws Exception {
261 
262         // Generate regular jar files for TestClient and TestClassLoader
263         JarUtils.createJarFile(CL_JAR, TEST_CLASSES,
264                                "cl/TestClassLoader.class");
265         JarUtils.createJarFile(C_JAR, TEST_CLASSES,
266                                "c/TestClient.class");
267         // Generate modular jar files for TestClient and TestClassLoader with
268         // their corresponding ModuleDescriptor.
269         Files.copy(CL_JAR, MCL_JAR,
270                 StandardCopyOption.REPLACE_EXISTING);
271         updateModuleDescr(MCL_JAR, ModuleDescriptor.newModule("mcl")
272                 .exports("cl").requires("java.base").build());
273         Files.copy(C_JAR, MC_JAR,
274                 StandardCopyOption.REPLACE_EXISTING);
275         updateModuleDescr(MC_JAR, ModuleDescriptor.newModule("mc")
276                 .exports("c").requires("java.base").requires("mcl").build());
277         Files.copy(C_JAR, AMC_JAR,
278                 StandardCopyOption.REPLACE_EXISTING);
279         updateModuleDescr(AMC_JAR, ModuleDescriptor.newModule("mc")
280                 .exports("c").requires("java.base").requires("cl").build());
281     }
282 
283     /**
284      * Update regular jars and include module-info.class inside it to make
285      * modular jars.
286      */
287     private static void updateModuleDescr(Path jar, ModuleDescriptor mDescr)
288             throws Exception {
289         if (mDescr != null) {
290             Path dir = Files.createTempDirectory("tmp");
291             Path mi = dir.resolve("module-info.class");
292             try (OutputStream out = Files.newOutputStream(mi)) {
293                 ModuleInfoWriter.write(mDescr, out);
294             }
295             System.out.format("Adding 'module-info.class' to jar '%s'%n", jar);
296             JarUtils.updateJarFile(jar, dir);
297         }
298     }
299 }