1 /* 2 * Copyright (c) 2014, 2024, 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 * @summary Class-Path: attribute in MANIFEST file 28 * @requires vm.cds 29 * @library /test/lib 30 * @compile test-classes/Hello.java 31 * @run driver/timeout=240 ClassPathAttr 32 */ 33 34 import jdk.test.lib.Platform; 35 import jdk.test.lib.cds.CDSTestUtils; 36 import jdk.test.lib.process.OutputAnalyzer; 37 import java.io.File; 38 import java.nio.file.Files; 39 import java.nio.file.FileAlreadyExistsException; 40 import java.nio.file.StandardCopyOption; 41 import java.nio.file.Paths; 42 43 44 public class ClassPathAttr { 45 46 public static void main(String[] args) throws Exception { 47 testNormalOps(); 48 testNonExistentJars(); 49 testClassPathAttrJarOnCP(); 50 } 51 52 static void testNormalOps() throws Exception { 53 buildCpAttr("cpattr1", "cpattr1.mf", "CpAttr1", "CpAttr1"); 54 buildCpAttr("cpattr1_long", "cpattr1_long.mf", "CpAttr1", "CpAttr1"); 55 buildCpAttr("cpattr2", "cpattr2.mf", "CpAttr2", "CpAttr2"); 56 buildCpAttr("cpattr3", "cpattr3.mf", "CpAttr3", "CpAttr2", "CpAttr3"); 57 buildCpAttr("cpattr4", "cpattr4.mf", "CpAttr4", 58 "CpAttr2", "CpAttr3", "CpAttr4", "CpAttr5"); 59 buildCpAttr("cpattr5_123456789_223456789_323456789_423456789_523456789_623456789", "cpattr5_extra_long.mf", "CpAttr5", "CpAttr5"); 60 61 String[] classlist = { "CpAttr1", "CpAttr2", "CpAttr3", "CpAttr4", "CpAttr5"}; 62 String jar4 = TestCommon.getTestJar("cpattr4.jar"); 63 for (int i=1; i<=2; i++) { 64 String jar1 = TestCommon.getTestJar("cpattr1.jar"); 65 if (i == 2) { 66 // Test case #2 -- same as #1, except we use cpattr1_long.jar, which has a super-long 67 // Class-Path: attribute. 68 jar1 = TestCommon.getTestJar("cpattr1_long.jar"); 69 } 70 String cp = jar1 + File.pathSeparator + jar4; 71 72 TestCommon.testDump(cp, classlist); 73 74 TestCommon.run( 75 "-cp", cp, 76 "CpAttr1") 77 .assertNormalExit(); 78 79 // Logging test for class+path. 80 TestCommon.run( 81 "-Xlog:class+path", 82 "-cp", cp, 83 "CpAttr1") 84 .assertNormalExit(output -> { 85 output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); 86 output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); 87 }); 88 89 // Test handling of forward slash ('/') file separator when locating entries 90 // in the classpath entry on Windows. 91 // Skip the following test when CDS dynamic dump is enabled due to some 92 // issue when converting a relative path to real path. 93 if (Platform.isWindows() && !CDSTestUtils.DYNAMIC_DUMP) { 94 // Test with relative path 95 // Find the index to the dir before the jar file. 96 int idx = jar1.lastIndexOf(File.separator); 97 idx = jar1.substring(0, idx - 1).lastIndexOf(File.separator); 98 // Setup jar directory and names. 99 String jarDir = jar1.substring(0, idx); 100 String jar1Name = jar1.substring(idx + 1); 101 String jar4Name = jar4.substring(idx + 1); 102 String newCp = jar1Name.replace("\\", "/") + File.pathSeparator + jar4Name.replace("\\", "/"); 103 104 OutputAnalyzer out = TestCommon.testDump(jarDir, newCp, classlist, "-Xlog:class+path=info"); 105 if (i == 1) { 106 out.shouldMatch("opened:.*cpattr1.jar"); // first jar on -cp 107 } else { 108 // first jar on -cp with long Class-Path: attribute 109 out.shouldMatch("opened:.*cpattr1_long.jar"); 110 } 111 // one of the jar in the Class-Path: attribute of cpattr1.jar 112 out.shouldMatch("opened:.*cpattr2.jar"); 113 114 TestCommon.runWithRelativePath( 115 jarDir.replace("\\", "/"), 116 "-Xlog:class+path,class+load", 117 "-cp", newCp, 118 "CpAttr1") 119 .assertNormalExit(output -> { 120 output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); 121 output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); 122 }); 123 124 // Go one directory up. 125 int idx2 = jar1.substring(0, idx - 1).lastIndexOf(File.separator); 126 if (idx2 != -1) { 127 // Setup jar directory and names. 128 jarDir = jar1.substring(0, idx2); 129 // Set relative path to jar containing '\' and '/' file separators 130 // e.g. d1\d2/A.jar 131 jar1Name = jar1.substring(idx2 + 1).replace("\\", "/"); 132 jar4Name = jar4.substring(idx2 + 1).replace("\\", "/"); 133 jar1Name = jar1Name.replaceFirst("/", "\\\\"); 134 jar4Name = jar4Name.replaceFirst("/", "\\\\"); 135 136 newCp = jar1Name + File.pathSeparator + jar4Name; 137 out = TestCommon.testDump(jarDir, newCp, classlist, "-Xlog:class+path=info"); 138 if (i == 1) { 139 out.shouldMatch("opened:.*cpattr1.jar"); // first jar on -cp 140 } else { 141 // first jar on -cp with long Class-Path: attribute 142 out.shouldMatch("opened:.*cpattr1_long.jar"); 143 } 144 // one of the jar in the Class-Path: attribute of cpattr1.jar 145 out.shouldMatch("opened:.*cpattr2.jar"); 146 147 TestCommon.runWithRelativePath( 148 jarDir.replace("\\", "/"), 149 "-Xlog:class+path,class+load", 150 "-cp", newCp, 151 "CpAttr1") 152 .assertNormalExit(output -> { 153 output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); 154 output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); 155 }); 156 } 157 } 158 } 159 160 // test duplicate jars in the "Class-path" attribute in the jar manifest 161 buildCpAttr("cpattr_dup", "cpattr_dup.mf", "CpAttr1", "CpAttr1"); 162 String cp = TestCommon.getTestJar("cpattr_dup.jar") + File.pathSeparator + jar4; 163 TestCommon.testDump(cp, classlist); 164 165 TestCommon.run( 166 "-cp", cp, 167 "CpAttr1") 168 .assertNormalExit(); 169 } 170 171 static void testNonExistentJars() throws Exception { 172 buildCpAttr("cpattr6", "cpattr6.mf", "CpAttr6", "CpAttr6"); 173 174 String cp = TestCommon.getTestJar("cpattr6.jar"); 175 String nonExistPath = CDSTestUtils.getOutputDir() + File.separator + "cpattrX.jar"; 176 (new File(nonExistPath)).delete(); 177 178 TestCommon.testDump(cp, TestCommon.list("CpAttr6"), 179 "-Xlog:class+path"); 180 181 TestCommon.run( 182 "-Xlog:class+path", 183 "-cp", cp, 184 "CpAttr6") 185 .assertNormalExit(output -> { 186 output.shouldMatch("should be non-existent: .*cpattrX.jar"); 187 }); 188 189 // Now make nonExistPath exist. CDS still loads, but archived non-system classes will not be used. 190 Files.copy(Paths.get(cp), Paths.get(nonExistPath), 191 StandardCopyOption.REPLACE_EXISTING); 192 193 TestCommon.run( 194 "-Xlog:class+path", 195 "-cp", cp, 196 "CpAttr6") 197 .assertNormalExit(output -> { 198 output.shouldMatch("Archived non-system classes are disabled because the file .*cpattrX.jar exists"); 199 }); 200 } 201 202 static void testClassPathAttrJarOnCP() throws Exception { 203 String helloJar = JarBuilder.getOrCreateHelloJar(); 204 String jar1 = TestCommon.getTestJar("cpattr1.jar"); 205 String cp = jar1 + File.pathSeparator + helloJar; 206 207 // The cpattr1.jar contains "Class-Path: cpattr2.jar". 208 // The cpattr2.jar contains "Class-Path: cpattr3.jar cpattr5_123456789_223456789_323456789_42345678.jar". 209 // With -cp cpattr1:hello.jar, the following shared paths should be stored in the CDS archive: 210 // cpattr1.jar:cpattr2.jar:cpattr3.jar:cpattr5_123456789_223456789_323456789_42345678.jari:hello.jar 211 TestCommon.testDump(cp, TestCommon.list("Hello"), "-Xlog:class+path"); 212 213 // Run with the same -cp apattr1.jar:hello.jar. The Hello class should be 214 // loaded from the archive. 215 TestCommon.run("-Xlog:class+path,class+load", 216 "-cp", cp, 217 "Hello") 218 .assertNormalExit(output -> { 219 output.shouldContain("Hello source: shared objects file"); 220 }); 221 222 // Run with -cp apattr1.jar:cpattr2.jar:hello.jar. App classpath mismatch should be detected. 223 String jar2 = TestCommon.getTestJar("cpattr2.jar"); 224 cp = jar1 + File.pathSeparator + jar2 + File.pathSeparator + helloJar; 225 TestCommon.run("-Xlog:class+path,class+load", 226 "-cp", cp, 227 "Hello") 228 .assertAbnormalExit(output -> { 229 output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr1.jar.*cpattr2.jar.*hello.jar") 230 .shouldContain("Unable to use shared archive."); 231 }); 232 233 // Run with different -cp cpattr2.jar:hello.jar. App classpath mismatch should be detected. 234 cp = jar2 + File.pathSeparator + helloJar; 235 TestCommon.run("-Xlog:class+path,class+load", 236 "-cp", cp, 237 "Hello") 238 .assertAbnormalExit(output -> { 239 output.shouldMatch(".*APP classpath mismatch, actual: -Djava.class.path=.*cpattr2.jar.*hello.jar") 240 .shouldContain("Unable to use shared archive."); 241 }); 242 243 // Dumping with -cp cpattr1.jar:cpattr2.jar:hello.jar 244 // The cpattr2.jar is from the Class-Path: attribute of cpattr1.jar. 245 cp = jar1 + File.pathSeparator + jar2 + File.pathSeparator + helloJar; 246 TestCommon.testDump(cp, TestCommon.list("Hello"), "-Xlog:class+path"); 247 248 // Run with the same -cp as dump time. The Hello class should be loaded from the archive. 249 TestCommon.run("-Xlog:class+path,class+load", 250 "-cp", cp, 251 "Hello") 252 .assertNormalExit(output -> { 253 output.shouldContain("Hello source: shared objects file"); 254 }); 255 256 } 257 258 private static void buildCpAttr(String jarName, String manifest, String enclosingClassName, String ...testClassNames) throws Exception { 259 String jarClassesDir = CDSTestUtils.getOutputDir() + File.separator + jarName + "_classes"; 260 try { Files.createDirectory(Paths.get(jarClassesDir)); } catch (FileAlreadyExistsException e) { } 261 262 JarBuilder.compile(jarClassesDir, System.getProperty("test.src") + File.separator + 263 "test-classes" + File.separator + enclosingClassName + ".java"); 264 JarBuilder.buildWithManifest(jarName, manifest, jarClassesDir, testClassNames); 265 } 266 }