1 /*
   2  * Copyright (c) 2016, 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  * @requires vm.cds & !vm.graal.enabled
  27  * @summary Testing -Xbootclasspath/a support for CDS
  28  * @requires vm.cds
  29  * @library /test/lib
  30  * @modules java.base/jdk.internal.misc
  31  *          java.management
  32  *          jdk.internal.jvmstat/sun.jvmstat.monitor
  33  * @compile javax/sound/sampled/MyClass.jasm
  34  * @compile javax/annotation/processing/FilerException.jasm
  35  * @compile nonjdk/myPackage/MyClass.java
  36  * @build LoadClass
  37  * @run main/othervm BootAppendTests
  38  */
  39 
  40 import java.io.File;
  41 import java.io.FileOutputStream;
  42 import java.io.IOException;
  43 import java.io.PrintStream;
  44 
  45 import java.nio.file.Path;
  46 import java.nio.file.Paths;
  47 
  48 import jdk.test.lib.cds.CDSOptions;
  49 import jdk.test.lib.cds.CDSTestUtils;
  50 import jdk.test.lib.process.ProcessTools;
  51 import jdk.test.lib.process.OutputAnalyzer;
  52 
  53 public class BootAppendTests {
  54     private static final String APP_CLASS = "LoadClass";
  55     private static final String BOOT_APPEND_MODULE_CLASS = "javax/sound/sampled/MyClass";
  56     private static final String BOOT_APPEND_DUPLICATE_MODULE_CLASS =
  57         "javax/annotation/processing/FilerException";
  58     private static final String BOOT_APPEND_CLASS = "nonjdk/myPackage/MyClass";
  59     private static final String BOOT_APPEND_MODULE_CLASS_NAME =
  60         BOOT_APPEND_MODULE_CLASS.replace('/', '.');
  61     private static final String BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME =
  62         BOOT_APPEND_DUPLICATE_MODULE_CLASS.replace('/', '.');
  63     private static final String BOOT_APPEND_CLASS_NAME =
  64         BOOT_APPEND_CLASS.replace('/', '.');
  65     private static final String[] ARCHIVE_CLASSES =
  66         {BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS, BOOT_APPEND_CLASS};
  67 
  68     private static final String modes[] = {"on", "off"};
  69 
  70     private static String appJar;
  71     private static String bootAppendJar;
  72 
  73     public static void main(String... args) throws Exception {
  74         dumpArchive();
  75 
  76         logTestCase("1");
  77         testBootAppendModuleClass();
  78 
  79         logTestCase("2");
  80         testBootAppendDuplicateModuleClass();
  81 
  82         logTestCase("3");
  83         testBootAppendExcludedModuleClass();
  84 
  85         logTestCase("4");
  86         testBootAppendDuplicateExcludedModuleClass();
  87 
  88         logTestCase("5");
  89         testBootAppendClass();
  90 
  91         logTestCase("6");
  92         testBootAppendExtraDir();
  93     }
  94 
  95     private static void logTestCase(String msg) {
  96         System.out.println();
  97         System.out.printf("TESTCASE: %s", msg);
  98         System.out.println();
  99     }
 100 
 101     static void dumpArchive() throws Exception {
 102         // create the classlist
 103         File classlist = CDSTestUtils.makeClassList(ARCHIVE_CLASSES);
 104 
 105         // build jar files
 106         appJar = ClassFileInstaller.writeJar("app.jar", APP_CLASS);
 107         bootAppendJar = ClassFileInstaller.writeJar("bootAppend.jar",
 108             BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS, BOOT_APPEND_CLASS);
 109 
 110 
 111         OutputAnalyzer out = CDSTestUtils.createArchiveAndCheck(
 112                                  "-Xbootclasspath/a:" + bootAppendJar,
 113                                  "-XX:SharedClassListFile=" + classlist.getPath());
 114         // Make sure all the classes were successfully archived.
 115         for (String archiveClass : ARCHIVE_CLASSES) {
 116             out.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
 117         }
 118     }
 119 
 120     // Test #1: If a class on -Xbootclasspath/a is from a package defined in
 121     //          bootmodules, the class is not loaded at runtime.
 122     //          Verify the behavior is the same when the class is archived
 123     //          with CDS enabled at runtime.
 124     //
 125     //          The javax.sound.sampled package is defined in the java.desktop module.
 126     //          The archived javax.sound.sampled.MyClass from the -Xbootclasspath/a
 127     //          should not be loaded at runtime.
 128     public static void testBootAppendModuleClass() throws Exception {
 129         for (String mode : modes) {
 130             CDSOptions opts = (new CDSOptions())
 131                 .setXShareMode(mode).setUseVersion(false)
 132                 .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar, "-showversion")
 133                 .addSuffix(APP_CLASS, BOOT_APPEND_MODULE_CLASS_NAME);
 134 
 135             OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
 136             CDSTestUtils.checkExec(out, opts, "java.lang.ClassNotFoundException: javax.sound.sampled.MyClass");
 137         }
 138     }
 139 
 140     // Test #2: If a class on -Xbootclasspath/a has the same fully qualified
 141     //          name as a class defined in boot modules, the class is not loaded
 142     //          from -Xbootclasspath/a. Verify the behavior is the same at runtime
 143     //          when CDS is enabled.
 144     //
 145     //          The javax/annotation/processing/FilerException is a platform module
 146     //          class. The class on the -Xbootclasspath/a path that has the same
 147     //          fully-qualified name should not be loaded at runtime when CDS is enabled.
 148     //          The one from the platform modules should be loaded instead.
 149     public static void testBootAppendDuplicateModuleClass() throws Exception {
 150         for (String mode : modes) {
 151             CDSOptions opts = (new CDSOptions())
 152                 .setXShareMode(mode).setUseVersion(false)
 153                 .addPrefix("-showversion",
 154                            "-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar)
 155                 .addSuffix("-Xlog:class+load=info",
 156                            APP_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME);
 157 
 158             OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
 159             CDSTestUtils.checkExec(out, opts, "[class,load] javax.annotation.processing.FilerException source: jrt:/java.compiler");
 160         }
 161     }
 162 
 163     // Test #3: If a class on -Xbootclasspath/a is from a package defined in boot modules,
 164     //          the class can be loaded from -Xbootclasspath/a when the module is excluded
 165     //          using --limit-modules. Verify the behavior is the same at runtime when CDS
 166     //          is enabled.
 167     //
 168     //          The java.desktop module is excluded using --limit-modules at runtime
 169     //          CDS will be disabled with the --limit-modules option during runtime.
 170     //          javax.sound.sampled.MyClass will be loaded from the jar at runtime.
 171     public static void testBootAppendExcludedModuleClass() throws Exception {
 172         for (String mode : modes) {
 173             CDSOptions opts = (new CDSOptions())
 174                 .setXShareMode(mode).setUseVersion(false)
 175                 .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion",
 176                            "--limit-modules=java.base", "-cp", appJar)
 177                 .addSuffix("-Xlog:class+load=info",
 178                            APP_CLASS, BOOT_APPEND_MODULE_CLASS_NAME);
 179             CDSTestUtils.Result res = CDSTestUtils.run(opts);
 180             String MATCH_PATTERN =
 181                 ".class.load. javax.sound.sampled.MyClass source:.*bootAppend.jar*";
 182             if (mode.equals("on")) {
 183                 res.assertSilentlyDisabledCDS(out -> {
 184                     out.shouldHaveExitValue(0)
 185                        .shouldMatch(MATCH_PATTERN);
 186                     });
 187             } else {
 188                 res.assertNormalExit(out -> {
 189                     out.shouldMatch(MATCH_PATTERN);
 190                     });
 191             }
 192         }
 193     }
 194 
 195     // Test #4: If a class on -Xbootclasspath/a has the same fully qualified
 196     //          name as a class defined in boot modules, the class is loaded
 197     //          from -Xbootclasspath/a when the boot module is excluded using
 198     //          --limit-modules. Verify the behavior is the same at runtime
 199     //          when CDS is enabled.
 200     //
 201     //          The javax.annotation.processing.FilerException is a platform module class.
 202     //          The class on -Xbootclasspath/a that has the same fully-qualified name
 203     //          as javax.annotation.processing.FilerException can be loaded at runtime when
 204     //          java.compiler is excluded.
 205     //          CDS is disabled during runtime if the --limit-modules option is
 206     //          specified.
 207     public static void testBootAppendDuplicateExcludedModuleClass() throws Exception {
 208         for (String mode : modes) {
 209             CDSOptions opts = (new CDSOptions())
 210                 .setXShareMode(mode).setUseVersion(false)
 211                 .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion",
 212                            "--limit-modules=java.base", "-cp", appJar)
 213                 .addSuffix("-Xlog:class+load=info",
 214                            APP_CLASS, BOOT_APPEND_DUPLICATE_MODULE_CLASS_NAME);
 215 
 216             CDSTestUtils.Result res = CDSTestUtils.run(opts);
 217             String MATCH_PATTERN =
 218                 ".class.load. javax.annotation.processing.FilerException source:.*bootAppend.jar*";
 219             if (mode.equals("on")) {
 220                 res.assertSilentlyDisabledCDS(out -> {
 221                     out.shouldHaveExitValue(0)
 222                        .shouldMatch(MATCH_PATTERN);
 223                     });
 224             } else {
 225                 res.assertNormalExit(out -> {
 226                     out.shouldMatch(MATCH_PATTERN);
 227                     });
 228             }
 229         }
 230     }
 231 
 232     // Test #5: If a class on -Xbootclasspath/a is not from named modules,
 233     //          the class can be loaded at runtime. Verify the behavior is
 234     //          the same at runtime when CDS is enabled.
 235     //
 236     //          The nonjdk.myPackage is not defined in named modules. The
 237     //          nonjdk.myPackage.MyClass will be loaded from the jar in
 238     //          -Xbootclasspath/a since CDS will be disabled with the
 239     //          --limit-modules option.
 240     public static void testBootAppendClass() throws Exception {
 241         for (String mode : modes) {
 242             CDSOptions opts = (new CDSOptions())
 243                 .setXShareMode(mode).setUseVersion(false)
 244                 .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-showversion",
 245                            "--limit-modules=java.base", "-cp", appJar)
 246                 .addSuffix("-Xlog:class+load=info",
 247                            APP_CLASS, BOOT_APPEND_CLASS_NAME);
 248 
 249             CDSTestUtils.Result res = CDSTestUtils.run(opts);
 250             String MATCH_PATTERN =
 251                 ".class.load. nonjdk.myPackage.MyClass source:.*bootAppend.jar*";
 252             if (mode.equals("on")) {
 253                 res.assertSilentlyDisabledCDS(out -> {
 254                     out.shouldHaveExitValue(0)
 255                        .shouldMatch(MATCH_PATTERN);
 256                     });
 257             } else {
 258                 res.assertNormalExit(out -> {
 259                     out.shouldMatch(MATCH_PATTERN);
 260                     });
 261             }
 262         }
 263     }
 264 
 265     // Test #6: This is similar to Test #5. During runtime, an extra dir
 266     //          is appended to the bootclasspath. It should not invalidate
 267     //          the shared archive. However, CDS will be disabled with the
 268     //          --limit-modules in the command line.
 269     public static void testBootAppendExtraDir() throws Exception {
 270         for (String mode : modes) {
 271             CDSOptions opts = (new CDSOptions())
 272                 .setXShareMode(mode).setUseVersion(false)
 273                 .addPrefix("-Xbootclasspath/a:" + bootAppendJar + File.pathSeparator + appJar,
 274                            "-showversion", "--limit-modules=java.base", "-cp", appJar)
 275                 .addSuffix("-Xlog:class+load=info",
 276                            APP_CLASS, BOOT_APPEND_CLASS_NAME);
 277 
 278             CDSTestUtils.Result res = CDSTestUtils.run(opts);
 279             String MATCH_PATTERN =
 280                 ".class.load. nonjdk.myPackage.MyClass source:.*bootAppend.jar*";
 281             if (mode.equals("on")) {
 282                 res.assertSilentlyDisabledCDS(out -> {
 283                     out.shouldHaveExitValue(0)
 284                        .shouldMatch(MATCH_PATTERN);
 285                     });
 286             } else {
 287                 res.assertNormalExit(out -> {
 288                     out.shouldMatch(MATCH_PATTERN);
 289                     });
 290             }
 291         }
 292     }
 293 }