1/*
2 * Copyright (c) 2016, 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 * @summary Testing -Xbootclasspath/a support for CDS
27 * @library /test/lib
28 * @modules java.base/jdk.internal.misc
29 *          java.management
30 *          jdk.internal.jvmstat/sun.jvmstat.monitor
31 * @compile javax/sound/sampled/MyClass.jasm
32 * @compile org/omg/CORBA/Context.jasm
33 * @compile nonjdk/myPackage/MyClass.java
34 * @build LoadClass
35 * @run main/othervm BootAppendTests
36 */
37
38import java.io.File;
39import java.io.FileOutputStream;
40import java.io.IOException;
41import java.io.PrintStream;
42
43import java.nio.file.Path;
44import java.nio.file.Paths;
45
46import jdk.test.lib.process.ProcessTools;
47import jdk.test.lib.process.OutputAnalyzer;
48
49public class BootAppendTests {
50    private static final String APP_CLASS = "LoadClass";
51    private static final String BOOT_APPEND_MODULE_CLASS = "javax/sound/sampled/MyClass";
52    private static final String BOOT_APPEND_DUPLICATE_MODULE_CLASS = "org/omg/CORBA/Context";
53    private static final String BOOT_APPEND_CLASS = "nonjdk/myPackage/MyClass";
54    private static final String BOOT_APPEND_MODULE_CLASS_NAME =
55        BOOT_APPEND_MODULE_CLASS.replace('/', '.');
56    private static final String BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME =
57        BOOT_APPEND_DUPLICATE_MODULE_CLASS.replace('/', '.');
58    private static final String BOOT_APPEND_CLASS_NAME =
59        BOOT_APPEND_CLASS.replace('/', '.');
60    private static final String[] ARCHIVE_CLASSES =
61        {BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS, BOOT_APPEND_CLASS};
62
63    private static final String modes[] = {"on", "off"};
64
65    private static String appJar;
66    private static String bootAppendJar;
67
68    public static void main(String... args) throws Exception {
69        dumpArchive();
70        testBootAppendModuleClass();
71        testBootAppendDuplicateModuleClass();
72        testBootAppendExcludedModuleClass();
73        testBootAppendDuplicateExcludedModuleClass();
74        testBootAppendClass();
75    }
76
77    static void dumpArchive() throws Exception {
78        // create the classlist
79        File classlist = new File(new File(System.getProperty("test.classes", ".")),
80                                  "BootAppendTest.classlist");
81        FileOutputStream fos = new FileOutputStream(classlist);
82        PrintStream ps = new PrintStream(fos);
83        for (String s : ARCHIVE_CLASSES) {
84            ps.println(s);
85        }
86        ps.close();
87        fos.close();
88
89        // build jar files
90        appJar = ClassFileInstaller.writeJar("app.jar", APP_CLASS);
91        bootAppendJar = ClassFileInstaller.writeJar("bootAppend.jar",
92            BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS, BOOT_APPEND_CLASS);
93
94        // dump
95        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
96            "-XX:+UnlockDiagnosticVMOptions",
97            "-XX:SharedArchiveFile=./BootAppendTests.jsa",
98            "-XX:SharedClassListFile=" + classlist.getPath(),
99            "-XX:+PrintSharedSpaces",
100            "-Xbootclasspath/a:" + bootAppendJar,
101            "-Xshare:dump");
102        OutputAnalyzer output = new OutputAnalyzer(pb.start());
103        output.shouldContain("Loading classes to share")
104              .shouldHaveExitValue(0);
105
106        // Make sure all the classes were successfully archived.
107        for (String archiveClass : ARCHIVE_CLASSES) {
108            output.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
109        }
110    }
111
112    // Test #1: If a class on -Xbootclasspath/a is from a package defined in
113    //          bootmodules, the class is not loaded at runtime.
114    //          Verify the behavior is the same when the class is archived
115    //          with CDS enabled at runtime.
116    //
117    //          The javax.sound.sampled package is defined in the java.desktop module.
118    //          The archived javax.sound.sampled.MyClass from the -Xbootclasspath/a
119    //          should not be loaded at runtime.
120    public static void testBootAppendModuleClass() throws Exception {
121        for (String mode : modes) {
122            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
123                "-XX:+UnlockDiagnosticVMOptions",
124                "-XX:SharedArchiveFile=./BootAppendTests.jsa",
125                "-cp", appJar,
126                "-Xbootclasspath/a:" + bootAppendJar,
127                "-Xshare:" + mode,
128                APP_CLASS,
129                BOOT_APPEND_MODULE_CLASS_NAME);
130            OutputAnalyzer output = new OutputAnalyzer(pb.start());
131            output.shouldContain("java.lang.ClassNotFoundException: javax.sound.sampled.MyClass");
132        }
133    }
134
135    // Test #2: If a class on -Xbootclasspath/a has the same fully qualified
136    //          name as a class defined in boot modules, the class is not loaded
137    //          from -Xbootclasspath/a. Verify the behavior is the same at runtime
138    //          when CDS is enabled.
139    //
140    //          The org.omg.CORBA.Context is a boot module class. The class on
141    //          the -Xbootclasspath/a path that has the same fully-qualified name
142    //          should not be loaded at runtime when CDS is enabled.
143    //          The one from the boot modules should be loaded instead.
144    public static void testBootAppendDuplicateModuleClass() throws Exception {
145        for (String mode : modes) {
146            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
147                "-XX:+UnlockDiagnosticVMOptions",
148                "-XX:SharedArchiveFile=./BootAppendTests.jsa",
149                "-XX:+TraceClassLoading",
150                "-cp", appJar,
151                "-Xbootclasspath/a:" + bootAppendJar,
152                "-Xshare:" + mode,
153                APP_CLASS,
154                BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME);
155            OutputAnalyzer output = new OutputAnalyzer(pb.start());
156            output.shouldContain("[class,load] org.omg.CORBA.Context source: jrt:/java.corba");
157        }
158    }
159
160    // Test #3: If a class on -Xbootclasspath/a is from a package defined in boot modules,
161    //          the class can be loaded from -Xbootclasspath/a when the module is excluded
162    //          using --limit-modules. Verify the behavior is the same at runtime when CDS
163    //          is enabled.
164    //
165    //          The java.desktop module is excluded using --limit-modules at runtime,
166    //          javax.sound.sampled.MyClass is archived from -Xbootclasspath/a. It can be
167    //          loaded from the archive at runtime.
168    public static void testBootAppendExcludedModuleClass() throws Exception {
169        for (String mode : modes) {
170            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
171                "-XX:+UnlockDiagnosticVMOptions",
172                "-XX:SharedArchiveFile=./BootAppendTests.jsa",
173                "-XX:+TraceClassLoading",
174                "-cp", appJar,
175                "-Xbootclasspath/a:" + bootAppendJar,
176                "--limit-modules=java.base",
177                "-Xshare:" + mode,
178                APP_CLASS,
179                BOOT_APPEND_MODULE_CLASS_NAME);
180            OutputAnalyzer output = new OutputAnalyzer(pb.start());
181            output.shouldContain("[class,load] javax.sound.sampled.MyClass");
182
183            // When CDS is enabled, the shared class should be loaded from the archive.
184            if (mode.equals("on")) {
185                output.shouldContain("[class,load] javax.sound.sampled.MyClass source: shared objects file");
186            }
187        }
188    }
189
190    // Test #4: If a class on -Xbootclasspath/a has the same fully qualified
191    //          name as a class defined in boot modules, the class is loaded
192    //          from -Xbootclasspath/a when the boot module is excluded using
193    //          --limit-modules. Verify the behavior is the same at runtime
194    //          when CDS is enabled.
195    //
196    //          The org.omg.CORBA.Context is a boot module class. The class
197    //          on -Xbootclasspath/a that has the same fully-qualified name
198    //          as org.omg.CORBA.Context can be loaded at runtime when
199    //          java.corba is excluded.
200    public static void testBootAppendDuplicateExcludedModuleClass() throws Exception {
201        for (String mode : modes) {
202            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
203                "-XX:+UnlockDiagnosticVMOptions",
204                "-XX:SharedArchiveFile=./BootAppendTests.jsa",
205                "-XX:+TraceClassLoading",
206                "-cp", appJar,
207                "-Xbootclasspath/a:" + bootAppendJar,
208                "--limit-modules=java.base",
209                "-Xshare:" + mode,
210                APP_CLASS,
211                BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME);
212            OutputAnalyzer output = new OutputAnalyzer(pb.start());
213            output.shouldContain("[class,load] org.omg.CORBA.Context");
214            output.shouldMatch(".*\\[class,load\\] org.omg.CORBA.Context source:.*bootAppend.jar");
215        }
216    }
217
218    // Test #5: If a class on -Xbootclasspath/a is not from named modules,
219    //          the class can be loaded at runtime. Verify the behavior is
220    //          the same at runtime when CDS is enabled.
221    //
222    //          The nonjdk.myPackage is not defined in named modules. The
223    //          archived nonjdk.myPackage.MyClass from -Xbootclasspath/a
224    //          can be loaded at runtime when CDS is enabled.
225    public static void testBootAppendClass() throws Exception {
226        for (String mode : modes) {
227            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
228                "-XX:+UnlockDiagnosticVMOptions",
229                "-XX:SharedArchiveFile=./BootAppendTests.jsa",
230                "-XX:+TraceClassLoading",
231                "-cp", appJar,
232                "-Xbootclasspath/a:" + bootAppendJar,
233                "-Xshare:" + mode,
234                APP_CLASS,
235                BOOT_APPEND_CLASS_NAME);
236            OutputAnalyzer output = new OutputAnalyzer(pb.start());
237            output.shouldContain("[class,load] nonjdk.myPackage.MyClass");
238
239            // If CDS is enabled, the nonjdk.myPackage.MyClass should be loaded
240            // from the shared archive.
241            if (mode.equals("on")) {
242                output.shouldContain(
243                    "[class,load] nonjdk.myPackage.MyClass source: shared objects file");
244            }
245        }
246    }
247}
248