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