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