1 /*
  2  * Copyright (c) 2020, 2022, 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 /**
 26  * @test
 27  * @requires vm.cds & !vm.graal.enabled
 28  * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
 29  * @run driver OptimizeModuleHandlingTest
 30  * @summary test module path changes for optimization of
 31  *          module handling.
 32  *
 33  */
 34 
 35 import java.io.File;
 36 import java.nio.file.Files;
 37 import java.nio.file.Path;
 38 import java.nio.file.Paths;
 39 
 40 import jdk.test.lib.cds.CDSTestUtils;
 41 import jdk.test.lib.process.OutputAnalyzer;
 42 
 43 public class OptimizeModuleHandlingTest {
 44 
 45     private static final Path USER_DIR = Paths.get(CDSTestUtils.getOutputDir());
 46 
 47     private static final String TEST_SRC = System.getProperty("test.src");
 48 
 49     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
 50     private static final Path MODS_DIR = Paths.get("mody");
 51 
 52     // the module name of the test module
 53     private static final String MAIN_MODULE = "com.bars";
 54     private static final String TEST_MODULE = "com.foos";
 55 
 56     // the module main class
 57     private static final String MAIN_CLASS = "com.bars.Main";
 58     private static final String TEST_CLASS = "com.foos.Test";
 59 
 60     private static String PATH_LIBS = "modylibs";
 61     private static Path libsDir = null;
 62     private static Path mainJar = null;
 63     private static Path testJar = null;
 64 
 65     private static String CLASS_FOUND_MESSAGE = "com.foos.Test found";
 66     private static String CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test";
 67     private static String OPTIMIZE_ENABLED = "optimized module handling: enabled";
 68     private static String OPTIMIZE_DISABLED = "optimized module handling: disabled";
 69     private static String MAIN_FROM_JAR = "class,load.*com.bars.Main.*[.]jar";
 70     private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file";
 71     private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar";
 72     private static String TEST_FROM_CDS = "class,load.*com.foos.Test.*shared objects file";
 73     private static String MAP_FAILED  = "Unable to use shared archive";
 74     private static String PATH_SEPARATOR = File.pathSeparator;
 75 
 76     public static void buildTestModule() throws Exception {
 77 
 78         // javac -d mods/$TESTMODULE src/$TESTMODULE/**
 79         JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE),
 80                                  MODS_DIR.resolve(TEST_MODULE),
 81                                  null);
 82 
 83         // javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/**
 84         JarBuilder.compileModule(SRC_DIR.resolve(MAIN_MODULE),
 85                                  MODS_DIR.resolve(MAIN_MODULE),
 86                                  MODS_DIR.toString());
 87 
 88         libsDir = Files.createTempDirectory(USER_DIR, PATH_LIBS);
 89         mainJar = libsDir.resolve(MAIN_MODULE + ".jar");
 90         testJar = libsDir.resolve(TEST_MODULE + ".jar");
 91 
 92         // modylibs contains both modules com.foos.jar, com.bars.jar
 93         // build com.foos.jar
 94         String classes = MODS_DIR.resolve(TEST_MODULE).toString();
 95         JarBuilder.createModularJar(testJar.toString(), classes, TEST_CLASS);
 96 
 97         // build com.bars.jar
 98         classes = MODS_DIR.resolve(MAIN_MODULE).toString();
 99         JarBuilder.createModularJar(mainJar.toString(), classes, MAIN_CLASS);
100     }
101 
102     public static void main(String... args) throws Exception {
103         runWithModulePath();
104         runWithJarPath();
105     }
106 
107     private static void tty(String... args) {
108         for (String s : args) {
109             System.out.print(s + " ");
110         }
111         System.out.print("\n");
112     }
113 
114     public static void runWithModulePath(String... extraRuntimeArgs) throws Exception {
115         // compile the modules and create the modular jar files
116         buildTestModule();
117         String appClasses[] = {MAIN_CLASS, TEST_CLASS};
118         // create an archive with the classes in the modules built in the
119         // previous step
120         OutputAnalyzer output = TestCommon.createArchive(
121                                         null, appClasses,
122                                         "--module-path",
123                                         libsDir.toString(),
124                                         "-m", MAIN_MODULE);
125         TestCommon.checkDump(output);
126 
127         // following 1 - 4 test with CDS off
128         tty("1. run with CDS off");
129         TestCommon.execOff( "-p", libsDir.toString(),
130                             "-m", MAIN_MODULE)
131             .shouldHaveExitValue(0)
132             .shouldNotContain(OPTIMIZE_ENABLED)
133             .shouldContain(CLASS_FOUND_MESSAGE);
134         tty("2. run with CDS off, without module path");
135         TestCommon.execOff("-cp",
136                            mainJar.toString(),
137                            MAIN_CLASS)
138             .shouldHaveExitValue(0)
139             .shouldContain(CLASS_NOT_FOUND_MESSAGE);
140         tty("3. run with CDS off, but with full jars in path");
141         TestCommon.execOff( "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
142                             MAIN_CLASS)
143             .shouldHaveExitValue(0)
144             .shouldNotContain(OPTIMIZE_ENABLED)
145             .shouldContain(CLASS_FOUND_MESSAGE);
146         tty("4. run with CDS off, only main jar on path, but given moudle path");
147         TestCommon.execOff( "-cp", mainJar.toString(),
148                             "--module-path", libsDir.toString(),
149                             "--add-modules", TEST_MODULE,
150                             MAIN_CLASS)
151             .shouldHaveExitValue(0)
152             .shouldNotContain(OPTIMIZE_ENABLED)
153             .shouldContain(CLASS_FOUND_MESSAGE);
154 
155         // Following 5 - 10 test with CDS on
156         tty("5. run with CDS on, with module path");
157         String prefix[] = {"-Djava.class.path=", "-Xlog:cds", "-Xlog:class+load"};
158         TestCommon.runWithModules(prefix,
159                                  null,               // --upgrade-module-path
160                                  libsDir.toString(), // --module-path
161                                  MAIN_MODULE)        // -m
162             .assertNormalExit(out -> {
163                 out.shouldNotContain(OPTIMIZE_ENABLED)
164                    .shouldContain(OPTIMIZE_DISABLED)
165                    .shouldMatch(MAIN_FROM_CDS)       // // archived Main class is for module only
166                    .shouldContain(CLASS_FOUND_MESSAGE);
167             });
168         tty("6. run with CDS on, with module paths set correctly");
169         TestCommon.run("-Xlog:cds",
170                        "-Xlog:class+load",
171                        "-p", libsDir.toString(),
172                        "-m", MAIN_MODULE)
173             .assertNormalExit(out -> {
174                 out.shouldContain(CLASS_FOUND_MESSAGE)
175                    .shouldMatch(MAIN_FROM_CDS)
176                    .shouldMatch(TEST_FROM_CDS)
177                    .shouldContain(OPTIMIZE_DISABLED)
178                    .shouldNotContain(OPTIMIZE_ENABLED);
179             });
180         tty("7. run with CDS on, with jar on path");
181         TestCommon.run("-Xlog:cds",
182                        "-Xlog:class+load",
183                        "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
184                        MAIN_CLASS)
185             .assertNormalExit(out -> {
186                 out.shouldContain(CLASS_FOUND_MESSAGE)
187                    .shouldMatch(MAIN_FROM_JAR)
188                    .shouldMatch(TEST_FROM_JAR)
189                    .shouldContain(OPTIMIZE_DISABLED)
190                    .shouldNotContain(OPTIMIZE_ENABLED);
191             });
192 
193         tty("8. run with CDS on, with --module-path, with jar should fail");
194         TestCommon.run("-Xlog:cds",
195                        "-Xlog:class+load",
196                        "-p", libsDir.toString(),
197                        "-cp", mainJar.toString(),
198                        MAIN_CLASS)
199             .assertNormalExit(out -> {
200                 out.shouldContain(CLASS_NOT_FOUND_MESSAGE)
201                    .shouldMatch(MAIN_FROM_JAR)
202                    .shouldNotContain(OPTIMIZE_ENABLED);
203             });
204         tty("9. run with CDS on, with com.foos on --module-path, with main jar on cp should pass");
205         TestCommon.run("-Xlog:cds",
206                        "-Xlog:class+load",
207                        "--module-path", libsDir.toString(),
208                        "--add-modules", TEST_MODULE,
209                        "-cp", mainJar.toString(),
210                        MAIN_CLASS)
211             .assertNormalExit(out -> {
212                 out.shouldContain(CLASS_FOUND_MESSAGE)
213                    .shouldMatch(MAIN_FROM_JAR)
214                    .shouldMatch(TEST_FROM_CDS)
215                    .shouldNotContain(OPTIMIZE_ENABLED);
216             });
217         tty("10. run with CDS on, --module-path, with -Xbootclasspath/a: .");
218         TestCommon.run("-Xlog:cds",
219                        "-Xbootclasspath/a:", ".",
220                        "--module-path", libsDir.toString(),
221                        MAIN_CLASS)
222             .assertAbnormalExit(out -> {
223                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
224                    .shouldContain(OPTIMIZE_DISABLED)           // mapping info
225                    .shouldContain("Error: Could not find or load main class .");
226             });
227         tty("11. run with CDS on, --module-path, with -Xbootclasspath/a:.");
228         TestCommon.run("-Xlog:cds",
229                        "-Xbootclasspath/a:.",
230                        "--module-path", libsDir.toString(),
231                        MAIN_CLASS)
232             .assertAbnormalExit(out -> {
233                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
234                    .shouldContain(OPTIMIZE_DISABLED)           // mapping info
235                    .shouldContain("shared class paths mismatch");
236             });
237     }
238 
239     public static void runWithJarPath(String... extraRuntimeArgs) throws Exception {
240         // compile the modules and create the modular jar files
241         buildTestModule();
242         String appClasses[] = {MAIN_CLASS, TEST_CLASS};
243         // create an archive with the classes in the modules built in the
244         // previous step
245         OutputAnalyzer output = TestCommon.createArchive(
246                                     testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
247                                     appClasses);
248         TestCommon.checkDump(output);
249 
250         // tests 1 - 4 test with CDS off are same as with module archive.
251         tty("tests 1 - 4 test with CDS off are same as with module archive, skipped");
252 
253         // Following 5 - 9 test with CDS on
254         tty("5. run with CDS on, with module path");
255         String prefix[] = {"-Djava.class.path=", "-Xlog:cds"};
256         TestCommon.runWithModules(prefix,
257                                   null,               // --upgrade-module-path
258                                   libsDir.toString(), // --module-path
259                                   MAIN_MODULE)        // -m
260             .assertAbnormalExit(out -> {
261                 out.shouldContain(MAP_FAILED)
262                    .shouldNotContain(OPTIMIZE_ENABLED)
263                    .shouldNotContain(CLASS_FOUND_MESSAGE);
264             });
265         tty("6. run with CDS on, with module paths set correctly");
266         TestCommon.run("-Xlog:cds",
267                        "-p", libsDir.toString(),
268                        "-m", MAIN_MODULE)
269             .assertAbnormalExit(out -> {
270                 out.shouldContain(MAP_FAILED)
271                    .shouldNotContain(CLASS_FOUND_MESSAGE)
272                    .shouldNotContain(OPTIMIZE_ENABLED);
273             });
274         tty("7. run with CDS on, with jar on path");
275         TestCommon.run("-Xlog:cds",
276                        "-Xlog:class+load",
277                        "-cp", testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
278                        MAIN_CLASS)
279             .assertNormalExit(out -> {
280                 out.shouldMatch(MAIN_FROM_CDS)
281                    .shouldMatch(TEST_FROM_CDS)
282                    .shouldContain(CLASS_FOUND_MESSAGE)
283                    .shouldContain(OPTIMIZE_ENABLED);
284             });
285         tty("8. run with CDS on, with --module-path, with jars on classpath should run but not optimized");
286         TestCommon.run("-Xlog:cds",
287                        "-Xlog:class+load",
288                        "-p", libsDir.toString(),
289                        "-cp", testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
290                        "--add-modules=com.bars",         // Main/Test from jars
291                        MAIN_CLASS)
292             .assertNormalExit(out -> {
293                 out.shouldMatch(MAIN_FROM_JAR)
294                    .shouldMatch(TEST_FROM_JAR)
295                    .shouldContain(CLASS_FOUND_MESSAGE)
296                    .shouldNotContain(OPTIMIZE_ENABLED);
297             });
298         tty("9. run with CDS on,  with main jar only on classpath should not pass");
299         TestCommon.run("-Xlog:cds",
300                        "-cp", mainJar.toString(),
301                        MAIN_CLASS)
302             .assertAbnormalExit(out -> {
303                 out.shouldContain(MAP_FAILED)
304                    .shouldNotContain(CLASS_FOUND_MESSAGE)
305                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
306                    .shouldNotContain(OPTIMIZE_ENABLED)
307                    .shouldNotContain(OPTIMIZE_DISABLED);
308             });
309         tty("10. run with CDS on,  with main/test jars on classpath also with -Xbootclasspath/a:  should pass");
310         TestCommon.run("-Xlog:cds",
311                        "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
312                        "-Xbootclasspath/a:", ".",
313                        MAIN_CLASS)
314             .assertAbnormalExit(out -> {
315                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
316                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
317                    .shouldNotContain(OPTIMIZE_ENABLED)
318                    .shouldContain(MAP_FAILED);
319             });
320 
321         // Dump an archive with -Xbootclasspath/a
322         output = TestCommon.createArchive(
323                                 testJar.toString(),
324                                 appClasses,
325                                 "-Xbootclasspath/a:" + mainJar.toString());
326         TestCommon.checkDump(output);
327         tty("11. run with CDS on,  with test jar on classpath and with main jar on -Xbootclasspath/a:  should pass");
328         TestCommon.run("-Xlog:cds",
329                        "-cp", testJar.toString(),
330                        "-Xbootclasspath/a:" + mainJar.toString(),
331                        MAIN_CLASS)
332             .assertNormalExit(out -> {
333                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
334                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
335                    .shouldContain(OPTIMIZE_ENABLED);
336             });
337         tty("12. run with CDS on,  with main jar on classpath and with test jar on -Xbootclasspath/a:  should not pass due to class paths mismatch");
338         TestCommon.run("-Xlog:cds",
339                        "-cp", mainJar.toString(),
340                        "-Xbootclasspath/a:" + testJar.toString(),
341                        MAIN_CLASS)
342             .assertAbnormalExit(out -> {
343                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
344                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
345                    .shouldNotContain(OPTIMIZE_ENABLED)
346                    .shouldContain(MAP_FAILED);
347             });
348 
349         // Skip the following test for dynamic CDS archive because the current
350         // dynamic dump test utililty does not support empty -cp with a classlist.
351         // (see createArchive(CDSOptions opts) in TestCommon.java)
352         if (!CDSTestUtils.isDynamicArchive()) {
353             // Dump an archive with only -Xbootclasspath/a
354             output = TestCommon.createArchive(
355                                     null,
356                                     appClasses,
357                                     "-Xbootclasspath/a:" + mainJar.toString());
358             TestCommon.checkDump(output);
359             tty("13. run with CDS on,  with the same -Xbootclasspath/a as dump time and adding a -cp with test.jar:  should pass");
360             TestCommon.run("-Xlog:cds,class+load",
361                            "-cp", testJar.toString(),
362                            "-Xbootclasspath/a:" + mainJar.toString(),
363                            MAIN_CLASS)
364                 .assertNormalExit(out -> {
365                     out.shouldMatch(MAIN_FROM_CDS)
366                        .shouldContain(OPTIMIZE_ENABLED);
367             });
368         }
369     }
370 }