1 /*
   2  * Copyright (c) 2020, 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.jvmci
  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.process.OutputAnalyzer;
  41 
  42 public class OptimizeModuleHandlingTest {
  43 
  44     private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
  45 
  46     private static final String TEST_SRC = System.getProperty("test.src");
  47 
  48     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  49     private static final Path MODS_DIR = Paths.get("mody");
  50 
  51     // the module name of the test module
  52     private static final String MAIN_MODULE = "com.bars";
  53     private static final String TEST_MODULE = "com.foos";
  54 
  55     // the module main class
  56     private static final String MAIN_CLASS = "com.bars.Main";
  57     private static final String TEST_CLASS = "com.foos.Test";
  58 
  59     private static String PATH_LIBS = "modylibs";
  60     private static Path libsDir = null;
  61     private static Path mainJar = null;
  62     private static Path testJar = null;
  63 
  64     private static String CLASS_FOUND_MESSAGE = "com.foos.Test found";
  65     private static String CLASS_NOT_FOUND_MESSAGE = "java.lang.ClassNotFoundException: com.foos.Test";
  66     private static String OPTIMIZE_ENABLED = "Using optimized module handling enabled";
  67     private static String OPTIMIZE_DISABLED = "Using optimized module handling disabled";
  68     private static String MAIN_FROM_JAR = "class,load.*com.bars.Main.*[.]jar";
  69     private static String MAIN_FROM_CDS = "class,load.*com.bars.Main.*shared objects file";
  70     private static String TEST_FROM_JAR = "class,load.*com.foos.Test.*[.]jar";
  71     private static String TEST_FROM_CDS = "class,load.*com.foos.Test.*shared objects file";
  72     private static String MAP_FAILED  = "Unable to use shared archive";
  73     private static String PATH_SEPARATOR = File.pathSeparator;
  74 
  75     public static void buildTestModule() throws Exception {
  76 
  77         // javac -d mods/$TESTMODULE src/$TESTMODULE/**
  78         JarBuilder.compileModule(SRC_DIR.resolve(TEST_MODULE),
  79                                  MODS_DIR.resolve(TEST_MODULE),
  80                                  null);
  81 
  82         // javac -d mods/$TESTMODULE --module-path MOD_DIR src/$TESTMODULE/**
  83         JarBuilder.compileModule(SRC_DIR.resolve(MAIN_MODULE),
  84                                  MODS_DIR.resolve(MAIN_MODULE),
  85                                  MODS_DIR.toString());
  86 
  87         libsDir = Files.createTempDirectory(USER_DIR, PATH_LIBS);
  88         mainJar = libsDir.resolve(MAIN_MODULE + ".jar");
  89         testJar = libsDir.resolve(TEST_MODULE + ".jar");
  90 
  91         // modylibs contains both modules com.foos.jar, com.bars.jar
  92         // build com.foos.jar
  93         String classes = MODS_DIR.resolve(TEST_MODULE).toString();
  94         JarBuilder.createModularJar(testJar.toString(), classes, TEST_CLASS);
  95 
  96         // build com.bars.jar
  97         classes = MODS_DIR.resolve(MAIN_MODULE).toString();
  98         JarBuilder.createModularJar(mainJar.toString(), classes, MAIN_CLASS);
  99     }
 100 
 101     public static void main(String... args) throws Exception {
 102         runWithModulePath();
 103         runWithJarPath();
 104     }
 105 
 106     private static void tty(String... args) {
 107         for (String s : args) {
 108             System.out.print(s + " ");
 109         }
 110         System.out.print("\n");
 111     }
 112 
 113     public static void runWithModulePath(String... extraRuntimeArgs) throws Exception {
 114         // compile the modules and create the modular jar files
 115         buildTestModule();
 116         String appClasses[] = {MAIN_CLASS, TEST_CLASS};
 117         // create an archive with the classes in the modules built in the
 118         // previous step
 119         OutputAnalyzer output = TestCommon.createArchive(
 120                                         null, appClasses,
 121                                         "--module-path",
 122                                         libsDir.toString(),
 123                                         "-m", MAIN_MODULE);
 124         TestCommon.checkDump(output);
 125 
 126         // following 1 - 4 test with CDS off
 127         tty("1. run with CDS off");
 128         TestCommon.execOff( "-p", libsDir.toString(),
 129                             "-m", MAIN_MODULE)
 130             .shouldHaveExitValue(0)
 131             .shouldNotContain(OPTIMIZE_ENABLED)
 132             .shouldContain(CLASS_FOUND_MESSAGE);
 133         tty("2. run with CDS off, without module path");
 134         TestCommon.execOff("-cp",
 135                            mainJar.toString(),
 136                            MAIN_CLASS)
 137             .shouldHaveExitValue(0)
 138             .shouldContain(CLASS_NOT_FOUND_MESSAGE);
 139         tty("3. run with CDS off, but with full jars in path");
 140         TestCommon.execOff( "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
 141                             MAIN_CLASS)
 142             .shouldHaveExitValue(0)
 143             .shouldNotContain(OPTIMIZE_ENABLED)
 144             .shouldContain(CLASS_FOUND_MESSAGE);
 145         tty("4. run with CDS off, only main jar on path, but given moudle path");
 146         TestCommon.execOff( "-cp", mainJar.toString(),
 147                             "--module-path", libsDir.toString(),
 148                             "--add-modules", TEST_MODULE,
 149                             MAIN_CLASS)
 150             .shouldHaveExitValue(0)
 151             .shouldNotContain(OPTIMIZE_ENABLED)
 152             .shouldContain(CLASS_FOUND_MESSAGE);
 153 
 154         // Following 5 - 10 test with CDS on
 155         tty("5. run with CDS on, with module path");
 156         String prefix[] = {"-Djava.class.path=", "-Xlog:cds", "-Xlog:class+load"};
 157         TestCommon.runWithModules(prefix,
 158                                  null,               // --upgrade-module-path
 159                                  libsDir.toString(), // --module-path
 160                                  MAIN_MODULE)        // -m
 161             .assertNormalExit(out -> {
 162                 out.shouldNotContain(OPTIMIZE_ENABLED)
 163                    .shouldContain(OPTIMIZE_DISABLED)
 164                    .shouldMatch(MAIN_FROM_CDS)       // // archived Main class is for module only
 165                    .shouldContain(CLASS_FOUND_MESSAGE);
 166             });
 167         tty("6. run with CDS on, with module paths set correctly");
 168         TestCommon.run("-Xlog:cds",
 169                        "-Xlog:class+load",
 170                        "-p", libsDir.toString(),
 171                        "-m", MAIN_MODULE)
 172             .assertNormalExit(out -> {
 173                 out.shouldContain(CLASS_FOUND_MESSAGE)
 174                    .shouldMatch(MAIN_FROM_CDS)
 175                    .shouldMatch(TEST_FROM_CDS)
 176                    .shouldContain(OPTIMIZE_DISABLED)
 177                    .shouldNotContain(OPTIMIZE_ENABLED);
 178             });
 179         tty("7. run with CDS on, with jar on path");
 180         TestCommon.run("-Xlog:cds",
 181                        "-Xlog:class+load",
 182                        "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
 183                        MAIN_CLASS)
 184             .assertNormalExit(out -> {
 185                 out.shouldContain(CLASS_FOUND_MESSAGE)
 186                    .shouldMatch(MAIN_FROM_JAR)
 187                    .shouldMatch(TEST_FROM_JAR)
 188                    .shouldContain(OPTIMIZE_DISABLED)
 189                    .shouldNotContain(OPTIMIZE_ENABLED);
 190             });
 191 
 192         tty("8. run with CDS on, with --module-path, with jar should fail");
 193         TestCommon.run("-Xlog:cds",
 194                        "-Xlog:class+load",
 195                        "-p", libsDir.toString(),
 196                        "-cp", mainJar.toString(),
 197                        MAIN_CLASS)
 198             .assertNormalExit(out -> {
 199                 out.shouldContain(CLASS_NOT_FOUND_MESSAGE)
 200                    .shouldMatch(MAIN_FROM_JAR)
 201                    .shouldNotContain(OPTIMIZE_ENABLED);
 202             });
 203         tty("9. run with CDS on, with com.foos on --module-path, with main jar on cp should pass");
 204         TestCommon.run("-Xlog:cds",
 205                        "-Xlog:class+load",
 206                        "--module-path", libsDir.toString(),
 207                        "--add-modules", TEST_MODULE,
 208                        "-cp", mainJar.toString(),
 209                        MAIN_CLASS)
 210             .assertNormalExit(out -> {
 211                 out.shouldContain(CLASS_FOUND_MESSAGE)
 212                    .shouldMatch(MAIN_FROM_JAR)
 213                    .shouldMatch(TEST_FROM_CDS)
 214                    .shouldNotContain(OPTIMIZE_ENABLED);
 215             });
 216         tty("10. run with CDS on, --module-path, with -Xbootclasspath/a: .");
 217         TestCommon.run("-Xlog:cds",
 218                        "-Xbootclasspath/a:", ".",
 219                        "--module-path", libsDir.toString(),
 220                        MAIN_CLASS)
 221             .assertAbnormalExit(out -> {
 222                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
 223                    .shouldContain(OPTIMIZE_DISABLED)           // mapping info
 224                    .shouldContain("shared class paths mismatch");
 225             });
 226     }
 227 
 228     public static void runWithJarPath(String... extraRuntimeArgs) throws Exception {
 229         // compile the modules and create the modular jar files
 230         buildTestModule();
 231         String appClasses[] = {MAIN_CLASS, TEST_CLASS};
 232         // create an archive with the classes in the modules built in the
 233         // previous step
 234         OutputAnalyzer output = TestCommon.createArchive(
 235                                     testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
 236                                     appClasses);
 237         TestCommon.checkDump(output);
 238 
 239         // tests 1 - 4 test with CDS off are same as with module archive.
 240         tty("tests 1 - 4 test with CDS off are same as with module archive, skipped");
 241 
 242         // Following 5 - 9 test with CDS on
 243         tty("5. run with CDS on, with module path");
 244         String prefix[] = {"-Djava.class.path=", "-Xlog:cds"};
 245         TestCommon.runWithModules(prefix,
 246                                   null,               // --upgrade-module-path
 247                                   libsDir.toString(), // --module-path
 248                                   MAIN_MODULE)        // -m
 249             .assertAbnormalExit(out -> {
 250                 out.shouldContain(MAP_FAILED)
 251                    .shouldNotContain(OPTIMIZE_ENABLED)
 252                    .shouldNotContain(CLASS_FOUND_MESSAGE);
 253             });
 254         tty("6. run with CDS on, with module paths set correctly");
 255         TestCommon.run("-Xlog:cds",
 256                        "-p", libsDir.toString(),
 257                        "-m", MAIN_MODULE)
 258             .assertAbnormalExit(out -> {
 259                 out.shouldContain(MAP_FAILED)
 260                    .shouldNotContain(CLASS_FOUND_MESSAGE)
 261                    .shouldNotContain(OPTIMIZE_ENABLED);
 262             });
 263         tty("7. run with CDS on, with jar on path");
 264         TestCommon.run("-Xlog:cds",
 265                        "-Xlog:class+load",
 266                        "-cp", testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
 267                        MAIN_CLASS)
 268             .assertNormalExit(out -> {
 269                 out.shouldMatch(MAIN_FROM_CDS)
 270                    .shouldMatch(TEST_FROM_CDS)
 271                    .shouldContain(CLASS_FOUND_MESSAGE)
 272                    .shouldContain(OPTIMIZE_ENABLED);
 273             });
 274         tty("8. run with CDS on, with --module-path, with jars on classpath should run but not optimized");
 275         TestCommon.run("-Xlog:cds",
 276                        "-Xlog:class+load",
 277                        "-p", libsDir.toString(),
 278                        "-cp", testJar.toString() + PATH_SEPARATOR + mainJar.toString(),
 279                        "--add-modules=com.bars",         // Main/Test from jars
 280                        MAIN_CLASS)
 281             .assertNormalExit(out -> {
 282                 out.shouldMatch(MAIN_FROM_JAR)
 283                    .shouldMatch(TEST_FROM_JAR)
 284                    .shouldContain(CLASS_FOUND_MESSAGE)
 285                    .shouldNotContain(OPTIMIZE_ENABLED);
 286             });
 287         tty("9. run with CDS on,  with main jar only on classpath should not pass");
 288         TestCommon.run("-Xlog:cds",
 289                        "-cp", mainJar.toString(),
 290                        MAIN_CLASS)
 291             .assertAbnormalExit(out -> {
 292                 out.shouldContain(MAP_FAILED)
 293                    .shouldNotContain(CLASS_FOUND_MESSAGE)
 294                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
 295                    .shouldNotContain(OPTIMIZE_ENABLED)
 296                    .shouldNotContain(OPTIMIZE_DISABLED);
 297             });
 298         tty("10. run with CDS on,  with main/test jars on classpath also with -Xbootclasspath/a:  should not pass");
 299         TestCommon.run("-Xlog:cds",
 300                        "-cp", mainJar.toString() + PATH_SEPARATOR + testJar.toString(),
 301                        "-Xbootclasspath/a:", ".",
 302                        MAIN_CLASS)
 303             .assertAbnormalExit(out -> {
 304                 out.shouldNotContain(CLASS_FOUND_MESSAGE)
 305                    .shouldNotContain(CLASS_NOT_FOUND_MESSAGE)
 306                    .shouldContain(OPTIMIZE_DISABLED)
 307                    .shouldNotContain(OPTIMIZE_ENABLED)
 308                    .shouldContain(MAP_FAILED);
 309             });
 310     }
 311 }