1 /* 2 * Copyright (c) 2015, 2017, 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 * @library /lib/testlibrary 27 * @build AutomaticModulesTest ModuleUtils JarUtils 28 * @run testng AutomaticModulesTest 29 * @summary Basic tests for automatic modules 30 */ 31 32 import java.io.IOException; 33 import java.lang.module.Configuration; 34 import java.lang.module.FindException; 35 import java.lang.module.ModuleDescriptor; 36 import java.lang.module.ModuleDescriptor.Requires.Modifier; 37 import java.lang.module.ModuleFinder; 38 import java.lang.module.ModuleReference; 39 import java.lang.module.ResolutionException; 40 import java.lang.module.ResolvedModule; 41 import java.nio.file.Files; 42 import java.nio.file.Path; 43 import java.nio.file.Paths; 44 import java.util.Optional; 45 import java.util.Set; 46 import java.util.jar.Attributes; 47 import java.util.jar.Manifest; 48 import java.util.stream.Collectors; 49 import java.util.stream.Stream; 50 51 import org.testng.annotations.DataProvider; 52 import org.testng.annotations.Test; 53 import static org.testng.Assert.*; 54 55 @Test 56 public class AutomaticModulesTest { 57 58 private static final Path USER_DIR 59 = Paths.get(System.getProperty("user.dir")); 60 61 @DataProvider(name = "jarnames") 62 public Object[][] createJarNames() { 63 return new Object[][] { 64 65 // JAR file name module-name[/version] 66 67 { "foo.jar", "foo" }, 68 { "foo4j.jar", "foo4j", }, 69 70 { "foo1.jar", "foo1" }, 71 { "foo10.jar", "foo10" }, 72 73 { "foo-1.jar", "foo/1" }, 74 { "foo-1.2.jar", "foo/1.2" }, 75 { "foo-1.2.3.jar", "foo/1.2.3" }, 76 { "foo-1.2.3.4.jar", "foo/1.2.3.4" }, 77 78 { "foo-10.jar", "foo/10" }, 79 { "foo-10.20.jar", "foo/10.20" }, 80 { "foo-10.20.30.jar", "foo/10.20.30" }, 81 { "foo-10.20.30.40.jar", "foo/10.20.30.40" }, 82 83 { "foo-bar.jar", "foo.bar" }, 84 { "foo-bar-1.jar", "foo.bar/1" }, 85 { "foo-bar-1.2.jar", "foo.bar/1.2"}, 86 { "foo-bar-10.jar", "foo.bar/10" }, 87 { "foo-bar-10.20.jar", "foo.bar/10.20" }, 88 89 { "foo.bar1.jar", "foo.bar1" }, 90 { "foo.bar10.jar", "foo.bar10" }, 91 92 { "foo-1.2-SNAPSHOT.jar", "foo/1.2-SNAPSHOT" }, 93 { "foo-bar-1.2-SNAPSHOT.jar", "foo.bar/1.2-SNAPSHOT" }, 94 95 { "foo--bar-1.0.jar", "foo.bar/1.0" }, 96 { "-foo-bar-1.0.jar", "foo.bar/1.0" }, 97 { "foo-bar--1.0.jar", "foo.bar/1.0" }, 98 99 }; 100 } 101 102 // JAR file names that do not map to a legal module name 103 @DataProvider(name = "badjarnames") 104 public Object[][] createBadNames() { 105 return new Object[][]{ 106 107 { ".jar", null }, 108 { "_.jar", null }, 109 110 { "foo.1.jar", null }, 111 { "1foo.jar", null }, 112 { "foo.1bar.jar", null }, 113 114 }; 115 } 116 117 /** 118 * Test mapping of JAR file names to module names 119 */ 120 @Test(dataProvider = "jarnames") 121 public void testNames(String fn, String mid) throws IOException { 122 String[] s = mid.split("/"); 123 String mn = s[0]; 124 String vs = (s.length == 2) ? s[1] : null; 125 126 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 127 Path jf = dir.resolve(fn); 128 129 // create empty JAR file 130 createDummyJarFile(jf); 131 132 // create a ModuleFinder to find modules in the directory 133 ModuleFinder finder = ModuleFinder.of(dir); 134 135 // a module with the expected name should be found 136 Optional<ModuleReference> mref = finder.find(mn); 137 assertTrue(mref.isPresent(), mn + " not found"); 138 139 ModuleDescriptor descriptor = mref.get().descriptor(); 140 assertTrue(descriptor.isAutomatic()); 141 assertEquals(descriptor.name(), mn); 142 if (vs == null) { 143 assertFalse(descriptor.version().isPresent()); 144 } else { 145 assertEquals(descriptor.version().get().toString(), vs); 146 } 147 } 148 149 /** 150 * Test impossible mapping of JAR files to modules names 151 */ 152 @Test(dataProvider = "badjarnames", expectedExceptions = FindException.class) 153 public void testBadNames(String fn, String ignore) throws IOException { 154 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 155 Path jf = dir.resolve(fn); 156 157 // create empty JAR file 158 createDummyJarFile(jf); 159 160 // should throw FindException 161 ModuleFinder.of(dir).findAll(); 162 } 163 164 165 @DataProvider(name = "modulenames") 166 public Object[][] createModuleNames() { 167 return new Object[][] { 168 { "foo", null }, 169 { "foo", "1.0" }, 170 { "foo.bar", null }, 171 { "foo.bar", "1.0" }, 172 { "class_", null }, 173 { "class_", "1.0" }, 174 }; 175 } 176 177 @DataProvider(name = "badmodulenames") 178 public Object[][] createBadModuleNames() { 179 return new Object[][] { 180 { "", null }, 181 { "", "1.0" }, 182 { "666", null }, 183 { "666", "1.0" }, 184 { "foo.class", null }, 185 { "foo.class", "1.0" }, 186 }; 187 } 188 189 /** 190 * Test JAR files with the Automatic-Module-Name attribute 191 */ 192 @Test(dataProvider = "modulenames") 193 public void testAutomaticModuleNameAttribute(String name, String vs) 194 throws IOException 195 { 196 Manifest man = new Manifest(); 197 Attributes attrs = man.getMainAttributes(); 198 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 199 attrs.put(new Attributes.Name("Automatic-Module-Name"), name); 200 201 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 202 String jar; 203 if (vs == null) { 204 jar = "m.jar"; 205 } else { 206 jar = "m-" + vs + ".jar"; 207 } 208 createDummyJarFile(dir.resolve(jar), man); 209 210 ModuleFinder finder = ModuleFinder.of(dir); 211 212 assertTrue(finder.findAll().size() == 1); 213 assertTrue(finder.find(name).isPresent()); 214 215 ModuleReference mref = finder.find(name).get(); 216 ModuleDescriptor descriptor = mref.descriptor(); 217 assertEquals(descriptor.name(), name); 218 assertEquals(descriptor.version() 219 .map(ModuleDescriptor.Version::toString) 220 .orElse(null), vs); 221 } 222 223 /** 224 * Test JAR files with the Automatic-Module-Name attribute with a value 225 * that is not a legal module name. 226 */ 227 @Test(dataProvider = "badmodulenames", expectedExceptions = FindException.class) 228 public void testBadAutomaticModuleNameAttribute(String name, String ignore) 229 throws IOException 230 { 231 // should throw FindException 232 testAutomaticModuleNameAttribute(name, null); 233 } 234 235 /** 236 * Test all packages are exported 237 */ 238 public void testPackages() throws IOException { 239 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 240 createDummyJarFile(dir.resolve("m.jar"), 241 "p/C1.class", "p/C2.class", "q/C1.class"); 242 243 ModuleFinder finder = ModuleFinder.of(dir); 244 Optional<ModuleReference> mref = finder.find("m"); 245 assertTrue(mref.isPresent(), "m not found"); 246 247 ModuleDescriptor descriptor = mref.get().descriptor(); 248 assertTrue(descriptor.isAutomatic()); 249 250 assertTrue(descriptor.packages().size() == 2); 251 assertTrue(descriptor.packages().contains("p")); 252 assertTrue(descriptor.packages().contains("q")); 253 254 assertTrue(descriptor.exports().isEmpty()); 255 assertTrue(descriptor.opens().isEmpty()); 256 } 257 258 /** 259 * Test class files in JAR file where the entry does not correspond to a 260 * legal package name. 261 */ 262 public void testBadPackage() throws IOException { 263 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 264 createDummyJarFile(dir.resolve("m.jar"), "p/C1.class", "p-/C2.class"); 265 266 ModuleFinder finder = ModuleFinder.of(dir); 267 Optional<ModuleReference> mref = finder.find("m"); 268 assertTrue(mref.isPresent(), "m not found"); 269 270 ModuleDescriptor descriptor = mref.get().descriptor(); 271 assertTrue(descriptor.isAutomatic()); 272 273 assertTrue(descriptor.packages().size() == 1); 274 assertTrue(descriptor.packages().contains("p")); 275 276 assertTrue(descriptor.exports().isEmpty()); 277 assertTrue(descriptor.opens().isEmpty()); 278 } 279 280 /** 281 * Test non-class resources in a JAR file. 282 */ 283 public void testNonClassResources() throws IOException { 284 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 285 createDummyJarFile(dir.resolve("m.jar"), 286 "LICENSE", 287 "README", 288 "WEB-INF/tags", 289 "p/Type.class", 290 "p/resources/m.properties"); 291 292 ModuleFinder finder = ModuleFinder.of(dir); 293 Optional<ModuleReference> mref = finder.find("m"); 294 assertTrue(mref.isPresent(), "m not found"); 295 296 ModuleDescriptor descriptor = mref.get().descriptor(); 297 assertTrue(descriptor.isAutomatic()); 298 299 assertTrue(descriptor.packages().size() == 1); 300 assertTrue(descriptor.packages().contains("p")); 301 } 302 303 /** 304 * Test .class file in unnamed package (top-level directory) 305 */ 306 @Test(expectedExceptions = FindException.class) 307 public void testClassInUnnamedPackage() throws IOException { 308 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 309 createDummyJarFile(dir.resolve("m.jar"), "Mojo.class"); 310 ModuleFinder finder = ModuleFinder.of(dir); 311 finder.findAll(); 312 } 313 314 /** 315 * Test JAR file with META-INF/services configuration file 316 */ 317 public void testServicesConfiguration() throws IOException { 318 String service = "p.S"; 319 String provider = "p.S1"; 320 321 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 322 323 // provider class 324 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); 325 Files.createDirectories(providerClass.getParent()); 326 Files.createFile(providerClass); 327 328 // services configuration file 329 Path services = tmpdir.resolve("META-INF").resolve("services"); 330 Files.createDirectories(services); 331 Files.write(services.resolve(service), Set.of(provider)); 332 333 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 334 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 335 336 ModuleFinder finder = ModuleFinder.of(dir); 337 338 Optional<ModuleReference> mref = finder.find("m"); 339 assertTrue(mref.isPresent(), "m not found"); 340 341 ModuleDescriptor descriptor = mref.get().descriptor(); 342 assertTrue(descriptor.provides().size() == 1); 343 ModuleDescriptor.Provides provides = descriptor.provides().iterator().next(); 344 assertEquals(provides.service(), service); 345 assertTrue(provides.providers().size() == 1); 346 assertTrue(provides.providers().contains((provider))); 347 } 348 349 // META-INF/services files that don't map to legal service names 350 @DataProvider(name = "badservices") 351 public Object[][] createBadServices() { 352 return new Object[][] { 353 354 // service type provider type 355 { "-", "p.S1" }, 356 { ".S", "p.S1" }, 357 }; 358 } 359 360 /** 361 * Test JAR file with META-INF/services configuration file with bad 362 * values or names. 363 */ 364 @Test(dataProvider = "badservices") 365 public void testBadServicesNames(String service, String provider) 366 throws IOException 367 { 368 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 369 Path services = tmpdir.resolve("META-INF").resolve("services"); 370 Files.createDirectories(services); 371 Files.write(services.resolve(service), Set.of(provider)); 372 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 373 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 374 375 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m"); 376 assertTrue(omref.isPresent()); 377 ModuleDescriptor descriptor = omref.get().descriptor(); 378 assertTrue(descriptor.provides().isEmpty()); 379 } 380 381 // META-INF/services configuration file entries that are not legal 382 @DataProvider(name = "badproviders") 383 public Object[][] createBadProviders() { 384 return new Object[][] { 385 386 // service type provider type 387 { "p.S", "-" }, 388 { "p.S", "p..S1" }, 389 { "p.S", "S1." }, 390 }; 391 } 392 393 /** 394 * Test JAR file with META-INF/services configuration file with bad 395 * values or names. 396 */ 397 @Test(dataProvider = "badproviders", expectedExceptions = FindException.class) 398 public void testBadProviderNames(String service, String provider) 399 throws IOException 400 { 401 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 402 403 // provider class 404 Path providerClass = tmpdir.resolve(provider.replace('.', '/') + ".class"); 405 Files.createDirectories(providerClass.getParent()); 406 Files.createFile(providerClass); 407 408 // services configuration file 409 Path services = tmpdir.resolve("META-INF").resolve("services"); 410 Files.createDirectories(services); 411 Files.write(services.resolve(service), Set.of(provider)); 412 413 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 414 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 415 416 // should throw FindException 417 ModuleFinder.of(dir).findAll(); 418 } 419 420 /** 421 * Test JAR file with META-INF/services configuration file listing a 422 * provider that is not in the module. 423 */ 424 @Test(expectedExceptions = FindException.class) 425 public void testMissingProviderPackage() throws IOException { 426 Path tmpdir = Files.createTempDirectory(USER_DIR, "tmp"); 427 428 // services configuration file 429 Path services = tmpdir.resolve("META-INF").resolve("services"); 430 Files.createDirectories(services); 431 Files.write(services.resolve("p.S"), Set.of("q.P")); 432 433 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 434 JarUtils.createJarFile(dir.resolve("m.jar"), tmpdir); 435 436 // should throw FindException 437 ModuleFinder.of(dir).findAll(); 438 } 439 440 /** 441 * Test that a JAR file with a Main-Class attribute results 442 * in a module with a main class. 443 */ 444 public void testMainClass() throws IOException { 445 String mainClass = "p.Main"; 446 447 Manifest man = new Manifest(); 448 Attributes attrs = man.getMainAttributes(); 449 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 450 attrs.put(Attributes.Name.MAIN_CLASS, mainClass); 451 452 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 453 String entry = mainClass.replace('.', '/') + ".class"; 454 createDummyJarFile(dir.resolve("m.jar"), man, entry); 455 456 ModuleFinder finder = ModuleFinder.of(dir); 457 458 Configuration parent = ModuleLayer.boot().configuration(); 459 Configuration cf = resolve(parent, finder, "m"); 460 461 ModuleDescriptor descriptor = findDescriptor(cf, "m"); 462 463 assertTrue(descriptor.mainClass().isPresent()); 464 assertEquals(descriptor.mainClass().get(), mainClass); 465 } 466 467 // Main-Class files that do not map to a legal qualified type name 468 @DataProvider(name = "badmainclass") 469 public Object[][] createBadMainClass() { 470 return new Object[][] { 471 { "p..Main", null }, 472 { "p-.Main", null }, 473 474 }; 475 } 476 477 /** 478 * Test that a JAR file with a Main-Class attribute that is not a qualified 479 * type name. 480 */ 481 @Test(dataProvider = "badmainclass") 482 public void testBadMainClass(String mainClass, String ignore) throws IOException { 483 Manifest man = new Manifest(); 484 Attributes attrs = man.getMainAttributes(); 485 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 486 attrs.put(Attributes.Name.MAIN_CLASS, mainClass); 487 488 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 489 String entry = mainClass.replace('.', '/') + ".class"; 490 createDummyJarFile(dir.resolve("m.jar"), man, entry); 491 492 // bad Main-Class value should be ignored 493 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m"); 494 assertTrue(omref.isPresent()); 495 ModuleDescriptor descriptor = omref.get().descriptor(); 496 assertFalse(descriptor.mainClass().isPresent()); 497 } 498 499 /** 500 * Test that a JAR file with a Main-Class attribute that is not in the module 501 */ 502 public void testMissingMainClassPackage() throws IOException { 503 Manifest man = new Manifest(); 504 Attributes attrs = man.getMainAttributes(); 505 attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0"); 506 attrs.put(Attributes.Name.MAIN_CLASS, "p.Main"); 507 508 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 509 createDummyJarFile(dir.resolve("m.jar"), man); 510 511 // Main-Class should be ignored because package p is not in module 512 Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m"); 513 assertTrue(omref.isPresent()); 514 ModuleDescriptor descriptor = omref.get().descriptor(); 515 assertFalse(descriptor.mainClass().isPresent()); 516 } 517 518 /** 519 * Basic test of a configuration created with automatic modules. 520 * a requires b* 521 * a requires c* 522 * b* 523 * c* 524 */ 525 public void testConfiguration1() throws Exception { 526 ModuleDescriptor descriptor1 527 = ModuleDescriptor.newModule("a") 528 .requires("b") 529 .requires("c") 530 .requires("java.base") 531 .build(); 532 533 // b and c are automatic modules 534 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 535 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 536 createDummyJarFile(dir.resolve("c.jar"), "q/T.class"); 537 538 // module finder locates a and the modules in the directory 539 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); 540 ModuleFinder finder2 = ModuleFinder.of(dir); 541 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 542 543 Configuration parent = ModuleLayer.boot().configuration(); 544 Configuration cf = resolve(parent, finder, "a"); 545 546 assertTrue(cf.modules().size() == 3); 547 assertTrue(cf.findModule("a").isPresent()); 548 assertTrue(cf.findModule("b").isPresent()); 549 assertTrue(cf.findModule("c").isPresent()); 550 551 ResolvedModule base = cf.findModule("java.base").get(); 552 assertTrue(base.configuration() == ModuleLayer.boot().configuration()); 553 ResolvedModule a = cf.findModule("a").get(); 554 ResolvedModule b = cf.findModule("b").get(); 555 ResolvedModule c = cf.findModule("c").get(); 556 557 // b && c only require java.base 558 assertTrue(b.reference().descriptor().requires().size() == 1); 559 assertTrue(c.reference().descriptor().requires().size() == 1); 560 561 // readability 562 563 assertTrue(a.reads().size() == 3); 564 assertTrue(a.reads().contains(base)); 565 assertTrue(a.reads().contains(b)); 566 assertTrue(a.reads().contains(c)); 567 568 assertTrue(b.reads().contains(a)); 569 assertTrue(b.reads().contains(c)); 570 testReadAllBootModules(cf, "b"); // b reads all modules in boot layer 571 572 assertTrue(c.reads().contains(a)); 573 assertTrue(c.reads().contains(b)); 574 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 575 576 } 577 578 /** 579 * Basic test of a configuration created with automatic modules 580 * a requires b 581 * b requires c* 582 * c* 583 * d* 584 */ 585 public void testInConfiguration2() throws IOException { 586 ModuleDescriptor descriptor1 587 = ModuleDescriptor.newModule("a") 588 .requires("b") 589 .requires("java.base") 590 .build(); 591 592 ModuleDescriptor descriptor2 593 = ModuleDescriptor.newModule("b") 594 .requires("c") 595 .requires("java.base") 596 .build(); 597 598 // c and d are automatic modules 599 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 600 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 601 createDummyJarFile(dir.resolve("d.jar"), "q/T.class"); 602 603 // module finder locates a and the modules in the directory 604 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); 605 ModuleFinder finder2 = ModuleFinder.of(dir); 606 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 607 608 Configuration parent = ModuleLayer.boot().configuration(); 609 Configuration cf = resolve(parent, finder, "a", "d"); 610 611 assertTrue(cf.modules().size() == 4); 612 assertTrue(cf.findModule("a").isPresent()); 613 assertTrue(cf.findModule("b").isPresent()); 614 assertTrue(cf.findModule("c").isPresent()); 615 assertTrue(cf.findModule("d").isPresent()); 616 617 // c && d should only require java.base 618 assertTrue(findDescriptor(cf, "c").requires().size() == 1); 619 assertTrue(findDescriptor(cf, "d").requires().size() == 1); 620 621 // readability 622 623 ResolvedModule base = cf.findModule("java.base").get(); 624 assertTrue(base.configuration() == ModuleLayer.boot().configuration()); 625 ResolvedModule a = cf.findModule("a").get(); 626 ResolvedModule b = cf.findModule("b").get(); 627 ResolvedModule c = cf.findModule("c").get(); 628 ResolvedModule d = cf.findModule("d").get(); 629 630 assertTrue(a.reads().size() == 2); 631 assertTrue(a.reads().contains(b)); 632 assertTrue(a.reads().contains(base)); 633 634 assertTrue(b.reads().size() == 3); 635 assertTrue(b.reads().contains(c)); 636 assertTrue(b.reads().contains(d)); 637 assertTrue(b.reads().contains(base)); 638 639 assertTrue(c.reads().contains(a)); 640 assertTrue(c.reads().contains(b)); 641 assertTrue(c.reads().contains(d)); 642 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 643 644 assertTrue(d.reads().contains(a)); 645 assertTrue(d.reads().contains(b)); 646 assertTrue(d.reads().contains(c)); 647 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer 648 } 649 650 /** 651 * Basic test of a configuration created with automatic modules 652 * a requires b 653 * b requires transitive c* 654 * c* 655 * d* 656 */ 657 public void testInConfiguration3() throws IOException { 658 ModuleDescriptor descriptor1 659 = ModuleDescriptor.newModule("a") 660 .requires("b") 661 .requires("java.base") 662 .build(); 663 664 ModuleDescriptor descriptor2 665 = ModuleDescriptor.newModule("b") 666 .requires(Set.of(Modifier.TRANSITIVE), "c") 667 .requires("java.base") 668 .build(); 669 670 // c and d are automatic modules 671 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 672 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 673 createDummyJarFile(dir.resolve("d.jar"), "q/T.class"); 674 675 // module finder locates a and the modules in the directory 676 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); 677 ModuleFinder finder2 = ModuleFinder.of(dir); 678 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 679 680 Configuration parent = ModuleLayer.boot().configuration(); 681 Configuration cf = resolve(parent, finder, "a", "d"); 682 683 assertTrue(cf.modules().size() == 4); 684 assertTrue(cf.findModule("a").isPresent()); 685 assertTrue(cf.findModule("b").isPresent()); 686 assertTrue(cf.findModule("c").isPresent()); 687 assertTrue(cf.findModule("d").isPresent()); 688 689 ResolvedModule base = cf.findModule("java.base").get(); 690 assertTrue(base.configuration() == ModuleLayer.boot().configuration()); 691 ResolvedModule a = cf.findModule("a").get(); 692 ResolvedModule b = cf.findModule("b").get(); 693 ResolvedModule c = cf.findModule("c").get(); 694 ResolvedModule d = cf.findModule("d").get(); 695 696 // c && d should only require java.base 697 assertTrue(findDescriptor(cf, "c").requires().size() == 1); 698 assertTrue(findDescriptor(cf, "d").requires().size() == 1); 699 700 // readability 701 702 assertTrue(a.reads().size() == 4); 703 assertTrue(a.reads().contains(b)); 704 assertTrue(a.reads().contains(c)); 705 assertTrue(a.reads().contains(d)); 706 assertTrue(a.reads().contains(base)); 707 708 assertTrue(b.reads().size() == 3); 709 assertTrue(b.reads().contains(c)); 710 assertTrue(b.reads().contains(d)); 711 assertTrue(b.reads().contains(base)); 712 713 assertTrue(reads(cf, "b", "c")); 714 assertTrue(reads(cf, "b", "d")); 715 assertTrue(reads(cf, "b", "java.base")); 716 717 assertTrue(c.reads().contains(a)); 718 assertTrue(c.reads().contains(b)); 719 assertTrue(c.reads().contains(d)); 720 testReadAllBootModules(cf, "c"); // c reads all modules in boot layer 721 722 assertTrue(d.reads().contains(a)); 723 assertTrue(d.reads().contains(b)); 724 assertTrue(d.reads().contains(c)); 725 testReadAllBootModules(cf, "d"); // d reads all modules in boot layer 726 } 727 728 /** 729 * Basic test to ensure that no automatic modules are resolved when 730 * an automatic module is not a root or required by other modules. 731 */ 732 public void testInConfiguration4() throws IOException { 733 ModuleDescriptor descriptor1 734 = ModuleDescriptor.newModule("m1") 735 .requires("java.base") 736 .build(); 737 738 // automatic modules 739 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 740 createDummyJarFile(dir.resolve("auto1.jar"), "p1/C.class"); 741 createDummyJarFile(dir.resolve("auto2.jar"), "p2/C.class"); 742 createDummyJarFile(dir.resolve("auto3.jar"), "p3/C.class"); 743 744 // module finder locates m1 and the modules in the directory 745 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); 746 ModuleFinder finder2 = ModuleFinder.of(dir); 747 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 748 749 Configuration parent = ModuleLayer.boot().configuration(); 750 Configuration cf = resolve(parent, finder, "m1"); 751 752 // ensure that no automatic module is resolved 753 assertTrue(cf.modules().size() == 1); 754 assertTrue(cf.findModule("m1").isPresent()); 755 } 756 757 /** 758 * Basic test to ensure that if an automatic module is resolved then 759 * all observable automatic modules are resolved. 760 */ 761 public void testInConfiguration5() throws IOException { 762 // m1 requires m2 763 ModuleDescriptor descriptor1 764 = ModuleDescriptor.newModule("m1") 765 .requires("m2").build(); 766 767 // m2 requires automatic module 768 ModuleDescriptor descriptor2 769 = ModuleDescriptor.newModule("m2") 770 .requires("auto1") 771 .build(); 772 773 // automatic modules 774 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 775 createDummyJarFile(dir.resolve("auto1.jar"), "p1/C.class"); 776 createDummyJarFile(dir.resolve("auto2.jar"), "p2/C.class"); 777 createDummyJarFile(dir.resolve("auto3.jar"), "p3/C.class"); 778 779 // module finder locates m1, m2, and the modules in the directory 780 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2); 781 ModuleFinder finder2 = ModuleFinder.of(dir); 782 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 783 784 Configuration parent = ModuleLayer.boot().configuration(); 785 Configuration cf = resolve(parent, finder, "m1"); 786 787 // all automatic modules should be resolved 788 assertTrue(cf.modules().size() == 5); 789 assertTrue(cf.findModule("m1").isPresent()); 790 assertTrue(cf.findModule("m2").isPresent()); 791 assertTrue(cf.findModule("auto1").isPresent()); 792 assertTrue(cf.findModule("auto2").isPresent()); 793 assertTrue(cf.findModule("auto3").isPresent()); 794 795 ResolvedModule base = parent.findModule("java.base") 796 .orElseThrow(() -> new RuntimeException()); 797 ResolvedModule m1 = cf.findModule("m1").get(); 798 ResolvedModule m2 = cf.findModule("m2").get(); 799 ResolvedModule auto1 = cf.findModule("auto1").get(); 800 ResolvedModule auto2 = cf.findModule("auto2").get(); 801 ResolvedModule auto3 = cf.findModule("auto3").get(); 802 803 // m1 does not read the automatic modules 804 assertTrue(m1.reads().size() == 2); 805 assertTrue(m1.reads().contains(m2)); 806 assertTrue(m1.reads().contains(base)); 807 808 // m2 should read all the automatic modules 809 assertTrue(m2.reads().size() == 4); 810 assertTrue(m2.reads().contains(auto1)); 811 assertTrue(m2.reads().contains(auto2)); 812 assertTrue(m2.reads().contains(auto3)); 813 assertTrue(m2.reads().contains(base)); 814 815 assertTrue(auto1.reads().contains(m1)); 816 assertTrue(auto1.reads().contains(m2)); 817 assertTrue(auto1.reads().contains(auto2)); 818 assertTrue(auto1.reads().contains(auto3)); 819 assertTrue(auto1.reads().contains(base)); 820 821 assertTrue(auto2.reads().contains(m1)); 822 assertTrue(auto2.reads().contains(m2)); 823 assertTrue(auto2.reads().contains(auto1)); 824 assertTrue(auto2.reads().contains(auto3)); 825 assertTrue(auto2.reads().contains(base)); 826 827 assertTrue(auto3.reads().contains(m1)); 828 assertTrue(auto3.reads().contains(m2)); 829 assertTrue(auto3.reads().contains(auto1)); 830 assertTrue(auto3.reads().contains(auto2)); 831 assertTrue(auto3.reads().contains(base)); 832 } 833 834 /** 835 * Basic test of automatic modules in a child configuration. All automatic 836 * modules that are found with the before finder should be resolved. The 837 * automatic modules that are found by the after finder and not shadowed 838 * by the before finder, or parent configurations, should also be resolved. 839 */ 840 public void testInConfiguration6() throws IOException { 841 // m1 requires auto1 842 ModuleDescriptor descriptor1 843 = ModuleDescriptor.newModule("m1") 844 .requires("auto1") 845 .build(); 846 847 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 848 createDummyJarFile(dir.resolve("auto1.jar"), "p1/C.class"); 849 850 // module finder locates m1 and auto1 851 ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1); 852 ModuleFinder finder2 = ModuleFinder.of(dir); 853 ModuleFinder finder = ModuleFinder.compose(finder1, finder2); 854 855 Configuration parent = ModuleLayer.boot().configuration(); 856 Configuration cf1 = resolve(parent, finder, "m1"); 857 858 assertTrue(cf1.modules().size() == 2); 859 assertTrue(cf1.findModule("m1").isPresent()); 860 assertTrue(cf1.findModule("auto1").isPresent()); 861 862 ResolvedModule base = parent.findModule("java.base") 863 .orElseThrow(() -> new RuntimeException()); 864 ResolvedModule m1 = cf1.findModule("m1").get(); 865 ResolvedModule auto1 = cf1.findModule("auto1").get(); 866 867 assertTrue(m1.reads().size() == 2); 868 assertTrue(m1.reads().contains(auto1)); 869 assertTrue(m1.reads().contains(base)); 870 871 assertTrue(auto1.reads().contains(m1)); 872 assertTrue(auto1.reads().contains(base)); 873 874 875 // create child configuration - the after finder locates auto1 876 877 dir = Files.createTempDirectory(USER_DIR, "mods"); 878 createDummyJarFile(dir.resolve("auto2.jar"), "p2/C.class"); 879 ModuleFinder beforeFinder = ModuleFinder.of(dir); 880 881 dir = Files.createTempDirectory(USER_DIR, "mods"); 882 createDummyJarFile(dir.resolve("auto1.jar"), "p1/C.class"); 883 createDummyJarFile(dir.resolve("auto2.jar"), "p2/C.class"); 884 createDummyJarFile(dir.resolve("auto3.jar"), "p3/C.class"); 885 ModuleFinder afterFinder = ModuleFinder.of(dir); 886 887 Configuration cf2 = cf1.resolve(beforeFinder, afterFinder, Set.of("auto2")); 888 889 // auto1 should be found in parent and should not be in cf2 890 assertTrue(cf2.modules().size() == 2); 891 assertTrue(cf2.findModule("auto2").isPresent()); 892 assertTrue(cf2.findModule("auto3").isPresent()); 893 894 ResolvedModule auto2 = cf2.findModule("auto2").get(); 895 ResolvedModule auto3 = cf2.findModule("auto3").get(); 896 897 assertTrue(auto2.reads().contains(m1)); 898 assertTrue(auto2.reads().contains(auto1)); 899 assertTrue(auto2.reads().contains(auto3)); 900 assertTrue(auto2.reads().contains(base)); 901 902 assertTrue(auto3.reads().contains(m1)); 903 assertTrue(auto3.reads().contains(auto1)); 904 assertTrue(auto3.reads().contains(auto2)); 905 assertTrue(auto3.reads().contains(base)); 906 } 907 908 /** 909 * Basic test of a configuration created with automatic modules 910 * a requires b* and c* 911 * b* contains p 912 * c* contains p 913 */ 914 @Test(expectedExceptions = { ResolutionException.class }) 915 public void testDuplicateSuppliers1() throws IOException { 916 ModuleDescriptor descriptor 917 = ModuleDescriptor.newModule("a") 918 .requires("b") 919 .requires("c") 920 .build(); 921 922 // c and d are automatic modules with the same package 923 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 924 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 925 createDummyJarFile(dir.resolve("c.jar"), "p/T.class"); 926 927 // module finder locates 'a' and the modules in the directory 928 ModuleFinder finder 929 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 930 ModuleFinder.of(dir)); 931 932 Configuration parent = ModuleLayer.boot().configuration(); 933 resolve(parent, finder, "a"); 934 } 935 936 /** 937 * Basic test of a configuration created with automatic modules 938 * a contains p, requires b* 939 * b* contains p 940 */ 941 @Test(expectedExceptions = { ResolutionException.class }) 942 public void testDuplicateSuppliers2() throws IOException { 943 ModuleDescriptor descriptor 944 = ModuleDescriptor.newModule("a") 945 .packages(Set.of("p")) 946 .requires("b") 947 .build(); 948 949 // c and d are automatic modules with the same package 950 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 951 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 952 953 // module finder locates 'a' and the modules in the directory 954 ModuleFinder finder 955 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 956 ModuleFinder.of(dir)); 957 958 Configuration parent = ModuleLayer.boot().configuration(); 959 resolve(parent, finder, "a"); 960 } 961 962 /** 963 * Basic test of layer containing automatic modules 964 */ 965 public void testInLayer() throws IOException { 966 ModuleDescriptor descriptor 967 = ModuleDescriptor.newModule("a") 968 .requires("b") 969 .requires("c") 970 .build(); 971 972 // b and c are simple JAR files 973 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 974 createDummyJarFile(dir.resolve("b.jar"), "p/T.class"); 975 createDummyJarFile(dir.resolve("c.jar"), "q/T2.class"); 976 977 // module finder locates a and the modules in the directory 978 ModuleFinder finder 979 = ModuleFinder.compose(ModuleUtils.finderOf(descriptor), 980 ModuleFinder.of(dir)); 981 982 Configuration parent = ModuleLayer.boot().configuration(); 983 Configuration cf = resolve(parent, finder, "a"); 984 assertTrue(cf.modules().size() == 3); 985 986 // each module gets its own loader 987 ModuleLayer layer = ModuleLayer.boot().defineModules(cf, mn -> new ClassLoader() { }); 988 989 // an unnamed module 990 Module unnamed = (new ClassLoader() { }).getUnnamedModule(); 991 992 Module b = layer.findModule("b").get(); 993 assertTrue(b.isNamed()); 994 assertTrue(b.canRead(unnamed)); 995 testsReadsAll(b, layer); 996 997 Module c = layer.findModule("c").get(); 998 assertTrue(c.isNamed()); 999 assertTrue(b.canRead(unnamed)); 1000 testsReadsAll(c, layer); 1001 } 1002 1003 /** 1004 * Test miscellaneous methods. 1005 */ 1006 public void testMisc() throws IOException { 1007 Path dir = Files.createTempDirectory(USER_DIR, "mods"); 1008 Path m_jar = createDummyJarFile(dir.resolve("m.jar"), "p/T.class"); 1009 1010 ModuleFinder finder = ModuleFinder.of(m_jar); 1011 1012 assertTrue(finder.find("m").isPresent()); 1013 ModuleDescriptor m = finder.find("m").get().descriptor(); 1014 1015 // test miscellaneous methods 1016 assertTrue(m.isAutomatic()); 1017 assertFalse(m.modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)); 1018 } 1019 1020 /** 1021 * Invokes parent.resolve to resolve the given root modules. 1022 */ 1023 static Configuration resolve(Configuration parent, 1024 ModuleFinder finder, 1025 String... roots) { 1026 return parent.resolve(finder, ModuleFinder.of(), Set.of(roots)); 1027 } 1028 1029 /** 1030 * Finds a module in the given configuration or its parents, returning 1031 * the module descriptor (or null if not found) 1032 */ 1033 static ModuleDescriptor findDescriptor(Configuration cf, String name) { 1034 Optional<ResolvedModule> om = cf.findModule(name); 1035 if (om.isPresent()) { 1036 return om.get().reference().descriptor(); 1037 } else { 1038 return null; 1039 } 1040 } 1041 1042 /** 1043 * Test that a module in a configuration reads all modules in the boot 1044 * configuration. 1045 */ 1046 static void testReadAllBootModules(Configuration cf, String mn) { 1047 1048 Set<String> bootModules = ModuleLayer.boot().modules().stream() 1049 .map(Module::getName) 1050 .collect(Collectors.toSet()); 1051 1052 bootModules.forEach(other -> assertTrue(reads(cf, mn, other))); 1053 1054 } 1055 1056 /** 1057 * Test that the given Module reads all module in the given layer 1058 * and its parent layers. 1059 */ 1060 static void testsReadsAll(Module m, ModuleLayer layer) { 1061 // check that m reads all modules in the layer 1062 layer.configuration().modules().stream() 1063 .map(ResolvedModule::name) 1064 .map(layer::findModule) 1065 .map(Optional::get) 1066 .forEach(other -> assertTrue(m.canRead(other))); 1067 1068 // also check parent layers 1069 layer.parents().forEach(l -> testsReadsAll(m, l)); 1070 } 1071 1072 /** 1073 * Returns {@code true} if the configuration contains module mn1 1074 * that reads module mn2. 1075 */ 1076 static boolean reads(Configuration cf, String mn1, String mn2) { 1077 Optional<ResolvedModule> om = cf.findModule(mn1); 1078 if (!om.isPresent()) 1079 return false; 1080 1081 return om.get().reads().stream() 1082 .map(ResolvedModule::name) 1083 .anyMatch(mn2::equals); 1084 } 1085 1086 /** 1087 * Creates a JAR file, optionally with a manifest, and with the given 1088 * entries. The entries will be empty in the resulting JAR file. 1089 */ 1090 static Path createDummyJarFile(Path jarfile, Manifest man, String... entries) 1091 throws IOException 1092 { 1093 Path dir = Files.createTempDirectory(USER_DIR, "tmp"); 1094 1095 for (String entry : entries) { 1096 Path file = dir.resolve(entry); 1097 Path parent = file.getParent(); 1098 if (parent != null) 1099 Files.createDirectories(parent); 1100 Files.createFile(file); 1101 } 1102 1103 Path[] paths = Stream.of(entries).map(Paths::get).toArray(Path[]::new); 1104 JarUtils.createJarFile(jarfile, man, dir, paths); 1105 return jarfile; 1106 } 1107 1108 /** 1109 * Creates a JAR file and with the given entries. The entries will be empty 1110 * in the resulting JAR file. 1111 */ 1112 static Path createDummyJarFile(Path jarfile, String... entries) 1113 throws IOException 1114 { 1115 return createDummyJarFile(jarfile, null, entries); 1116 } 1117 1118 }