1 /* 2 * Copyright (c) 2015, 2026, 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 * @bug 8142968 8300228 27 * @library /test/lib 28 * @modules java.base/jdk.internal.module 29 * jdk.compiler 30 * jdk.jlink 31 * @build ModuleReaderTest 32 * jdk.test.lib.compiler.CompilerUtils 33 * jdk.test.lib.util.JarUtils 34 * @run junit ModuleReaderTest 35 * @summary Basic tests for java.lang.module.ModuleReader 36 */ 37 38 import java.io.File; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.lang.module.ModuleFinder; 42 import java.lang.module.ModuleReader; 43 import java.lang.module.ModuleReference; 44 import java.net.URI; 45 import java.net.URL; 46 import java.net.URLConnection; 47 import java.nio.ByteBuffer; 48 import java.nio.file.Files; 49 import java.nio.file.Path; 50 import java.nio.file.Paths; 51 import java.util.HashSet; 52 import java.util.List; 53 import java.util.Optional; 54 import java.util.Set; 55 import java.util.spi.ToolProvider; 56 import java.util.stream.Stream; 57 58 import jdk.internal.module.ModulePath; 59 import jdk.test.lib.compiler.CompilerUtils; 60 import jdk.test.lib.util.JarUtils; 61 import org.junit.jupiter.api.BeforeAll; 62 import org.junit.jupiter.api.Test; 63 64 import static org.junit.jupiter.api.Assertions.assertArrayEquals; 65 import static org.junit.jupiter.api.Assertions.assertEquals; 66 import static org.junit.jupiter.api.Assertions.assertFalse; 67 import static org.junit.jupiter.api.Assertions.assertThrows; 68 import static org.junit.jupiter.api.Assertions.assertTrue; 69 70 public class ModuleReaderTest { 71 private static final String TEST_SRC = System.getProperty("test.src"); 72 73 private static final Path USER_DIR = Paths.get(System.getProperty("user.dir")); 74 private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); 75 private static final Path MODS_DIR = Paths.get("mods"); 76 77 // the module name of the base module 78 private static final String BASE_MODULE = "java.base"; 79 80 // the module name of the test module 81 private static final String TEST_MODULE = "m"; 82 83 // resources in the base module 84 private static final String[] BASE_RESOURCES = { 85 "java/lang/Object.class" 86 }; 87 88 // (directory) resources that may be in the base module 89 private static final String[] MAYBE_BASE_RESOURCES = { 90 "java", 91 "java/", 92 "java/lang", 93 "java/lang/", 94 }; 95 96 // resource names that should not be found in the base module 97 private static final String[] NOT_BASE_RESOURCES = { 98 "NotFound", 99 "/java", 100 "//java", 101 "/java/lang", 102 "//java/lang", 103 "java//lang", 104 "/java/lang/Object.class", 105 "//java/lang/Object.class", 106 "java/lang/Object.class/", 107 "java//lang//Object.class", 108 "./java/lang/Object.class", 109 "java/./lang/Object.class", 110 "java/lang/./Object.class", 111 "../java/lang/Object.class", 112 "java/../lang/Object.class", 113 "java/lang/../Object.class", 114 115 // junk resource names 116 "java\u0000", 117 "C:java", 118 "C:\\java", 119 "java\\lang\\Object.class" 120 }; 121 122 // resources in test module (can't use module-info.class as a test 123 // resource as it will be modified by the jmod tool) 124 private static final String[] TEST_RESOURCES = { 125 "p/Main.class" 126 }; 127 128 // (directory) resources that may be in the test module 129 private static final String[] MAYBE_TEST_RESOURCES = { 130 "p", 131 "p/" 132 }; 133 134 // resource names that should not be found in the test module 135 private static final String[] NOT_TEST_RESOURCES = { 136 "NotFound", 137 "/p", 138 "//p", 139 "/p/Main.class", 140 "//p/Main.class", 141 "p/Main.class/", 142 "p//Main.class", 143 "./p/Main.class", 144 "p/./Main.class", 145 "../p/Main.class", 146 "p/../p/Main.class", 147 148 // junk resource names 149 "p\u0000", 150 "C:p", 151 "C:\\p", 152 "p\\Main.class" 153 }; 154 155 @BeforeAll 156 public static void compileTestModule() throws Exception { 157 // javac -d mods/$TESTMODULE src/$TESTMODULE/** 158 boolean compiled = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE), 159 MODS_DIR.resolve(TEST_MODULE)); 160 assertTrue(compiled, "test module did not compile"); 161 } 162 163 /** 164 * Test ModuleReader with module in runtime image. 165 */ 166 @Test 167 public void testImage() throws IOException { 168 ModuleFinder finder = ModuleFinder.ofSystem(); 169 ModuleReference mref = finder.find(BASE_MODULE).get(); 170 ModuleReader reader = mref.open(); 171 172 try (reader) { 173 174 for (String name : BASE_RESOURCES) { 175 byte[] expectedBytes; 176 Module baseModule = Object.class.getModule(); 177 try (InputStream in = baseModule.getResourceAsStream(name)) { 178 expectedBytes = in.readAllBytes(); 179 } 180 181 testFind(reader, name, expectedBytes); 182 testOpen(reader, name, expectedBytes); 183 testRead(reader, name, expectedBytes); 184 testList(reader, name); 185 } 186 187 // test resources that may be in the base module 188 for (String name : MAYBE_BASE_RESOURCES) { 189 Optional<URI> ouri = reader.find(name); 190 ouri.ifPresent(uri -> { 191 if (name.endsWith("/")) 192 assertTrue(uri.toString().endsWith("/"), 193 "mismatched directory URI for '" + name + "': " + uri); 194 }); 195 } 196 197 // test "not found" in java.base module 198 for (String name : NOT_BASE_RESOURCES) { 199 assertFalse(reader.find(name).isPresent(), "Unexpected resource found: " + name); 200 assertFalse(reader.open(name).isPresent(), "Unexpected resource opened: " + name); 201 assertFalse(reader.read(name).isPresent(), "Unexpected resource read: " + name); 202 } 203 204 // test nulls 205 assertThrows(NullPointerException.class, () -> reader.find(null)); 206 assertThrows(NullPointerException.class, () -> reader.open(null)); 207 assertThrows(NullPointerException.class, () -> reader.read(null)); 208 assertThrows(NullPointerException.class, () -> reader.release(null)); 209 } 210 211 // test closed ModuleReader 212 assertThrows(IOException.class, () -> reader.open(BASE_RESOURCES[0])); 213 assertThrows(IOException.class, () -> reader.read(BASE_RESOURCES[0])); 214 assertThrows(IOException.class, reader::list); 215 } 216 217 /** 218 * Test ModuleReader with exploded module. 219 */ 220 @Test 221 public void testExplodedModule() throws IOException { 222 test(MODS_DIR); 223 } 224 225 /** 226 * Test ModuleReader with module in modular JAR. 227 */ 228 @Test 229 public void testModularJar() throws IOException { 230 Path dir = Files.createTempDirectory(USER_DIR, "mlib"); 231 232 // jar cf mlib/${TESTMODULE}.jar -C mods . 233 JarUtils.createJarFile(dir.resolve("m.jar"), 234 MODS_DIR.resolve(TEST_MODULE)); 235 236 test(dir); 237 } 238 239 /** 240 * Test ModuleReader with module in a JMOD file. 241 */ 242 @Test 243 public void testJMod() throws IOException { 244 Path dir = Files.createTempDirectory(USER_DIR, "mlib"); 245 246 // jmod create --class-path mods/${TESTMODULE} mlib/${TESTMODULE}.jmod 247 String cp = MODS_DIR.resolve(TEST_MODULE).toString(); 248 String jmod = dir.resolve("m.jmod").toString(); 249 String[] args = { "create", "--class-path", cp, jmod }; 250 ToolProvider jmodTool = ToolProvider.findFirst("jmod") 251 .orElseThrow(() -> 252 new RuntimeException("jmod tool not found") 253 ); 254 assertEquals(0, jmodTool.run(System.out, System.out, args), "jmod tool failed"); 255 256 test(dir); 257 } 258 259 /** 260 * The test module is found on the given module path. Open a ModuleReader 261 * to the test module and test the reader. 262 */ 263 void test(Path mp) throws IOException { 264 ModuleFinder finder = ModulePath.of(Runtime.version(), true, mp); 265 ModuleReference mref = finder.find(TEST_MODULE).get(); 266 ModuleReader reader = mref.open(); 267 268 try (reader) { 269 270 // test resources in test module 271 for (String name : TEST_RESOURCES) { 272 System.out.println("resource: " + name); 273 byte[] expectedBytes 274 = Files.readAllBytes(MODS_DIR 275 .resolve(TEST_MODULE) 276 .resolve(name.replace('/', File.separatorChar))); 277 278 testFind(reader, name, expectedBytes); 279 testOpen(reader, name, expectedBytes); 280 testRead(reader, name, expectedBytes); 281 testList(reader, name); 282 } 283 284 // test resources that may be in the test module 285 for (String name : MAYBE_TEST_RESOURCES) { 286 System.out.println("resource: " + name); 287 Optional<URI> ouri = reader.find(name); 288 ouri.ifPresent(uri -> { 289 if (name.endsWith("/")) 290 assertTrue(uri.toString().endsWith("/"), 291 "mismatched directory URI for '" + name + "': " + uri); 292 }); 293 } 294 295 // test "not found" in test module 296 for (String name : NOT_TEST_RESOURCES) { 297 System.out.println("resource: " + name); 298 assertFalse(reader.find(name).isPresent(), "Unexpected resource found: " + name); 299 assertFalse(reader.open(name).isPresent(), "Unexpected resource open: " + name); 300 assertFalse(reader.read(name).isPresent(), "Unexpected resource read: " + name); 301 } 302 303 // test nulls 304 assertThrows(NullPointerException.class, () -> reader.find(null)); 305 assertThrows(NullPointerException.class, () -> reader.open(null)); 306 assertThrows(NullPointerException.class, () -> reader.read(null)); 307 assertThrows(NullPointerException.class, () -> reader.release(null)); 308 } 309 310 // test closed ModuleReader 311 assertThrows(IOException.class, () -> reader.open(BASE_RESOURCES[0])); 312 assertThrows(IOException.class, () -> reader.read(BASE_RESOURCES[0])); 313 assertThrows(IOException.class, reader::list); 314 } 315 316 /** 317 * Test ModuleReader#find 318 */ 319 void testFind(ModuleReader reader, String name, byte[] expectedBytes) 320 throws IOException 321 { 322 Optional<URI> ouri = reader.find(name); 323 assertTrue(ouri.isPresent(), "missing URI for: " + name); 324 325 URL url = ouri.get().toURL(); 326 if (!url.getProtocol().equalsIgnoreCase("jmod")) { 327 URLConnection uc = url.openConnection(); 328 uc.setUseCaches(false); 329 try (InputStream in = uc.getInputStream()) { 330 byte[] bytes = in.readAllBytes(); 331 assertArrayEquals(expectedBytes, bytes, "resource bytes differ for: " + name); 332 } 333 } 334 } 335 336 /** 337 * Test ModuleReader#open 338 */ 339 void testOpen(ModuleReader reader, String name, byte[] expectedBytes) 340 throws IOException 341 { 342 Optional<InputStream> oin = reader.open(name); 343 assertTrue(oin.isPresent(), "missing input stream for: " + name); 344 try (InputStream in = oin.get()) { 345 byte[] bytes = in.readAllBytes(); 346 assertArrayEquals(expectedBytes, bytes, "resource bytes differ for: " + name); 347 } 348 } 349 350 /** 351 * Test ModuleReader#read 352 */ 353 void testRead(ModuleReader reader, String name, byte[] expectedBytes) 354 throws IOException 355 { 356 Optional<ByteBuffer> obb = reader.read(name); 357 assertTrue(obb.isPresent()); 358 359 ByteBuffer bb = obb.get(); 360 try { 361 int rem = bb.remaining(); 362 assertEquals(expectedBytes.length, rem, "resource lengths differ: " + name); 363 byte[] bytes = new byte[rem]; 364 bb.get(bytes); 365 assertArrayEquals(expectedBytes, bytes, "resource bytes differ: " + name); 366 } finally { 367 reader.release(bb); 368 } 369 } 370 371 /** 372 * Test ModuleReader#list 373 */ 374 void testList(ModuleReader reader, String name) throws IOException { 375 final List<String> list; 376 try (Stream<String> stream = reader.list()) { 377 list = stream.toList(); 378 } 379 Set<String> names = new HashSet<>(list); 380 assertEquals(names.size(), list.size(), "resource list contains duplicates: " + list); 381 382 assertTrue(names.contains("module-info.class"), "resource list did not contain 'module-info.class': " + list); 383 assertTrue(names.contains(name), "resource list did not contain '" + name + "'" + list); 384 385 // all resources should be locatable via find 386 for (String e : names) { 387 assertTrue(reader.find(e).isPresent(), "resource not found: " + name); 388 } 389 } 390 391 } --- EOF ---