1 /* 2 * Copyright (c) 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.module; 27 28 import java.io.PrintStream; 29 import java.lang.module.ModuleDescriptor.Provides; 30 import java.lang.module.ModuleDescriptor.Requires.Modifier; 31 import java.net.URI; 32 import java.util.ArrayDeque; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Collection; 36 import java.util.Deque; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.LinkedHashSet; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Optional; 43 import java.util.Set; 44 import java.util.stream.Collectors; 45 46 import jdk.internal.module.ModuleHashes; 47 import jdk.internal.module.ModuleReferenceImpl; 48 import jdk.internal.module.ModuleTarget; 49 50 /** 51 * The resolver used by {@link Configuration#resolve} and {@link 52 * Configuration#resolveAndBind}. 53 * 54 * @implNote The resolver is used at VM startup and so deliberately avoids 55 * using lambda and stream usages in code paths used during startup. 56 */ 57 58 final class Resolver { 59 60 private final ModuleFinder beforeFinder; 61 private final List<Configuration> parents; 62 private final ModuleFinder afterFinder; 63 private final PrintStream traceOutput; 64 65 // maps module name to module reference 66 private final Map<String, ModuleReference> nameToReference = new HashMap<>(); 67 68 // true if all automatic modules have been found 69 private boolean haveAllAutomaticModules; 70 71 // constraint on target platform 72 private String targetPlatform; 73 74 String targetPlatform() { return targetPlatform; } 75 76 /** 77 * @throws IllegalArgumentException if there are more than one parent and 78 * the constraints on the target platform conflict 79 */ 80 Resolver(ModuleFinder beforeFinder, 81 List<Configuration> parents, 82 ModuleFinder afterFinder, 83 PrintStream traceOutput) { 84 this.beforeFinder = beforeFinder; 85 this.parents = parents; 86 this.afterFinder = afterFinder; 87 this.traceOutput = traceOutput; 88 89 // record constraint on target platform, checking for conflicts 90 for (Configuration parent : parents) { 91 String value = parent.targetPlatform(); 92 if (value != null) { 93 if (targetPlatform == null) { 94 targetPlatform = value; 95 } else { 96 if (!value.equals(targetPlatform)) { 97 String msg = "Parents have conflicting constraints on target" + 98 " platform: " + targetPlatform + ", " + value; 99 throw new IllegalArgumentException(msg); 100 } 101 } 102 } 103 } 104 } 105 106 /** 107 * Resolves the given named modules. 108 * 109 * @throws ResolutionException 110 */ 111 Resolver resolve(Collection<String> roots) { 112 113 // create the visit stack to get us started 114 Deque<ModuleDescriptor> q = new ArrayDeque<>(); 115 for (String root : roots) { 116 117 // find root module 118 ModuleReference mref = findWithBeforeFinder(root); 119 if (mref == null) { 120 121 if (findInParent(root) != null) { 122 // in parent, nothing to do 123 continue; 124 } 125 126 mref = findWithAfterFinder(root); 127 if (mref == null) { 128 findFail("Module %s not found", root); 129 } 130 } 131 132 if (isTracing()) { 133 trace("root %s", nameAndInfo(mref)); 134 } 135 136 addFoundModule(mref); 137 q.push(mref.descriptor()); 138 } 139 140 resolve(q); 141 142 return this; 143 } 144 145 /** 146 * Resolve all modules in the given queue. On completion the queue will be 147 * empty and any resolved modules will be added to {@code nameToReference}. 148 * 149 * @return The set of module resolved by this invocation of resolve 150 */ 151 private Set<ModuleDescriptor> resolve(Deque<ModuleDescriptor> q) { 152 Set<ModuleDescriptor> resolved = new HashSet<>(); 153 154 while (!q.isEmpty()) { 155 ModuleDescriptor descriptor = q.poll(); 156 assert nameToReference.containsKey(descriptor.name()); 157 158 // if the module is an automatic module then all automatic 159 // modules need to be resolved 160 if (descriptor.isAutomatic() && !haveAllAutomaticModules) { 161 addFoundAutomaticModules().forEach(mref -> { 162 ModuleDescriptor other = mref.descriptor(); 163 q.offer(other); 164 if (isTracing()) { 165 trace("%s requires %s", descriptor.name(), nameAndInfo(mref)); 166 } 167 }); 168 haveAllAutomaticModules = true; 169 } 170 171 // process dependences 172 for (ModuleDescriptor.Requires requires : descriptor.requires()) { 173 174 // only required at compile-time 175 if (requires.modifiers().contains(Modifier.STATIC)) 176 continue; 177 178 String dn = requires.name(); 179 180 // find dependence 181 ModuleReference mref = findWithBeforeFinder(dn); 182 if (mref == null) { 183 184 if (findInParent(dn) != null) { 185 // dependence is in parent 186 continue; 187 } 188 189 mref = findWithAfterFinder(dn); 190 if (mref == null) { 191 findFail("Module %s not found, required by %s", 192 dn, descriptor.name()); 193 } 194 } 195 196 if (isTracing() && !dn.equals("java.base")) { 197 trace("%s requires %s", descriptor.name(), nameAndInfo(mref)); 198 } 199 200 if (!nameToReference.containsKey(dn)) { 201 addFoundModule(mref); 202 q.offer(mref.descriptor()); 203 } 204 205 } 206 207 resolved.add(descriptor); 208 } 209 210 return resolved; 211 } 212 213 /** 214 * Augments the set of resolved modules with modules induced by the 215 * service-use relation. 216 */ 217 Resolver bind() { 218 219 // Scan the finders for all available service provider modules. As 220 // java.base uses services then then module finders will be scanned 221 // anyway. 222 Map<String, Set<ModuleReference>> availableProviders = new HashMap<>(); 223 for (ModuleReference mref : findAll()) { 224 ModuleDescriptor descriptor = mref.descriptor(); 225 if (!descriptor.provides().isEmpty()) { 226 227 for (Provides provides : descriptor.provides()) { 228 String sn = provides.service(); 229 230 // computeIfAbsent 231 Set<ModuleReference> providers = availableProviders.get(sn); 232 if (providers == null) { 233 providers = new HashSet<>(); 234 availableProviders.put(sn, providers); 235 } 236 providers.add(mref); 237 } 238 239 } 240 } 241 242 // create the visit stack 243 Deque<ModuleDescriptor> q = new ArrayDeque<>(); 244 245 // the initial set of modules that may use services 246 Set<ModuleDescriptor> initialConsumers; 247 if (ModuleLayer.boot() == null) { 248 initialConsumers = new HashSet<>(); 249 } else { 250 initialConsumers = parents.stream() 251 .flatMap(Configuration::configurations) 252 .distinct() 253 .flatMap(c -> c.descriptors().stream()) 254 .collect(Collectors.toSet()); 255 } 256 for (ModuleReference mref : nameToReference.values()) { 257 initialConsumers.add(mref.descriptor()); 258 } 259 260 // Where there is a consumer of a service then resolve all modules 261 // that provide an implementation of that service 262 Set<ModuleDescriptor> candidateConsumers = initialConsumers; 263 do { 264 for (ModuleDescriptor descriptor : candidateConsumers) { 265 if (!descriptor.uses().isEmpty()) { 266 267 // the modules that provide at least one service 268 Set<ModuleDescriptor> modulesToBind = null; 269 if (isTracing()) { 270 modulesToBind = new HashSet<>(); 271 } 272 273 for (String service : descriptor.uses()) { 274 Set<ModuleReference> mrefs = availableProviders.get(service); 275 if (mrefs != null) { 276 for (ModuleReference mref : mrefs) { 277 ModuleDescriptor provider = mref.descriptor(); 278 if (!provider.equals(descriptor)) { 279 280 if (isTracing() && modulesToBind.add(provider)) { 281 trace("%s binds %s", descriptor.name(), 282 nameAndInfo(mref)); 283 } 284 285 String pn = provider.name(); 286 if (!nameToReference.containsKey(pn)) { 287 addFoundModule(mref); 288 q.push(provider); 289 } 290 } 291 } 292 } 293 } 294 } 295 } 296 297 candidateConsumers = resolve(q); 298 } while (!candidateConsumers.isEmpty()); 299 300 return this; 301 } 302 303 /** 304 * Add all automatic modules that have not already been found to the 305 * nameToReference map. 306 */ 307 private Set<ModuleReference> addFoundAutomaticModules() { 308 Set<ModuleReference> result = new HashSet<>(); 309 findAll().forEach(mref -> { 310 String mn = mref.descriptor().name(); 311 if (mref.descriptor().isAutomatic() && !nameToReference.containsKey(mn)) { 312 addFoundModule(mref); 313 result.add(mref); 314 } 315 }); 316 return result; 317 } 318 319 /** 320 * Add the module to the nameToReference map. Also check any constraints on 321 * the target platform with the constraints of other modules. 322 */ 323 private void addFoundModule(ModuleReference mref) { 324 String mn = mref.descriptor().name(); 325 326 if (mref instanceof ModuleReferenceImpl) { 327 ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget(); 328 if (target != null) 329 checkTargetPlatform(mn, target); 330 } 331 332 nameToReference.put(mn, mref); 333 } 334 335 /** 336 * Check that the module's constraints on the target platform does 337 * conflict with the constraint of other modules resolved so far. 338 */ 339 private void checkTargetPlatform(String mn, ModuleTarget target) { 340 String value = target.targetPlatform(); 341 if (value != null) { 342 if (targetPlatform == null) { 343 targetPlatform = value; 344 } else { 345 if (!value.equals(targetPlatform)) { 346 findFail("Module %s has constraints on target platform (%s)" 347 + " that conflict with other modules: %s", mn, 348 value, targetPlatform); 349 } 350 } 351 } 352 } 353 354 /** 355 * Execute post-resolution checks and returns the module graph of resolved 356 * modules as a map. 357 */ 358 Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) { 359 detectCycles(); 360 checkHashes(); 361 Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf); 362 checkExportSuppliers(graph); 363 return graph; 364 } 365 366 /** 367 * Checks the given module graph for cycles. 368 * 369 * For now the implementation is a simple depth first search on the 370 * dependency graph. We'll replace this later, maybe with Tarjan. 371 */ 372 private void detectCycles() { 373 visited = new HashSet<>(); 374 visitPath = new LinkedHashSet<>(); // preserve insertion order 375 for (ModuleReference mref : nameToReference.values()) { 376 visit(mref.descriptor()); 377 } 378 visited.clear(); 379 } 380 381 // the modules that were visited 382 private Set<ModuleDescriptor> visited; 383 384 // the modules in the current visit path 385 private Set<ModuleDescriptor> visitPath; 386 387 private void visit(ModuleDescriptor descriptor) { 388 if (!visited.contains(descriptor)) { 389 boolean added = visitPath.add(descriptor); 390 if (!added) { 391 resolveFail("Cycle detected: %s", cycleAsString(descriptor)); 392 } 393 for (ModuleDescriptor.Requires requires : descriptor.requires()) { 394 String dn = requires.name(); 395 396 ModuleReference mref = nameToReference.get(dn); 397 if (mref != null) { 398 ModuleDescriptor other = mref.descriptor(); 399 if (other != descriptor) { 400 // dependency is in this configuration 401 visit(other); 402 } 403 } 404 } 405 visitPath.remove(descriptor); 406 visited.add(descriptor); 407 } 408 } 409 410 /** 411 * Returns a String with a list of the modules in a detected cycle. 412 */ 413 private String cycleAsString(ModuleDescriptor descriptor) { 414 List<ModuleDescriptor> list = new ArrayList<>(visitPath); 415 list.add(descriptor); 416 int index = list.indexOf(descriptor); 417 return list.stream() 418 .skip(index) 419 .map(ModuleDescriptor::name) 420 .collect(Collectors.joining(" -> ")); 421 } 422 423 424 /** 425 * Checks the hashes in the module descriptor to ensure that they match 426 * any recorded hashes. 427 */ 428 private void checkHashes() { 429 for (ModuleReference mref : nameToReference.values()) { 430 431 // get the recorded hashes, if any 432 if (!(mref instanceof ModuleReferenceImpl)) 433 continue; 434 ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes(); 435 if (hashes == null) 436 continue; 437 438 ModuleDescriptor descriptor = mref.descriptor(); 439 String algorithm = hashes.algorithm(); 440 for (String dn : hashes.names()) { 441 ModuleReference mref2 = nameToReference.get(dn); 442 if (mref2 == null) { 443 ResolvedModule resolvedModule = findInParent(dn); 444 if (resolvedModule != null) 445 mref2 = resolvedModule.reference(); 446 } 447 if (mref2 == null) 448 continue; 449 450 if (!(mref2 instanceof ModuleReferenceImpl)) { 451 findFail("Unable to compute the hash of module %s", dn); 452 } 453 454 ModuleReferenceImpl other = (ModuleReferenceImpl)mref2; 455 if (other != null) { 456 byte[] recordedHash = hashes.hashFor(dn); 457 byte[] actualHash = other.computeHash(algorithm); 458 if (actualHash == null) 459 findFail("Unable to compute the hash of module %s", dn); 460 if (!Arrays.equals(recordedHash, actualHash)) { 461 findFail("Hash of %s (%s) differs to expected hash (%s)" + 462 " recorded in %s", dn, toHexString(actualHash), 463 toHexString(recordedHash), descriptor.name()); 464 } 465 } 466 } 467 468 } 469 } 470 471 private static String toHexString(byte[] ba) { 472 StringBuilder sb = new StringBuilder(ba.length * 2); 473 for (byte b: ba) { 474 sb.append(String.format("%02x", b & 0xff)); 475 } 476 return sb.toString(); 477 } 478 479 480 /** 481 * Computes the readability graph for the modules in the given Configuration. 482 * 483 * The readability graph is created by propagating "requires" through the 484 * "requires transitive" edges of the module dependence graph. So if the 485 * module dependence graph has m1 requires m2 && m2 requires transitive m3 486 * then the resulting readability graph will contain m1 reads m2, m1 reads m3, 487 * and m2 reads m3. 488 */ 489 private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) { 490 491 // initial capacity of maps to avoid resizing 492 int capacity = 1 + (4 * nameToReference.size())/ 3; 493 494 // the "reads" graph starts as a module dependence graph and 495 // is iteratively updated to be the readability graph 496 Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity); 497 498 // the "requires transitive" graph, contains requires transitive edges only 499 Map<ResolvedModule, Set<ResolvedModule>> g2; 500 501 // need "requires transitive" from the modules in parent configurations 502 // as there may be selected modules that have a dependency on modules in 503 // the parent configuration. 504 if (ModuleLayer.boot() == null) { 505 g2 = new HashMap<>(capacity); 506 } else { 507 g2 = parents.stream() 508 .flatMap(Configuration::configurations) 509 .distinct() 510 .flatMap(c -> 511 c.modules().stream().flatMap(m1 -> 512 m1.descriptor().requires().stream() 513 .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE)) 514 .flatMap(r -> { 515 Optional<ResolvedModule> m2 = c.findModule(r.name()); 516 assert m2.isPresent() 517 || r.modifiers().contains(Modifier.STATIC); 518 return m2.stream(); 519 }) 520 .map(m2 -> Map.entry(m1, m2)) 521 ) 522 ) 523 // stream of m1->m2 524 .collect(Collectors.groupingBy(Map.Entry::getKey, 525 HashMap::new, 526 Collectors.mapping(Map.Entry::getValue, Collectors.toSet()) 527 )); 528 } 529 530 // populate g1 and g2 with the dependences from the selected modules 531 532 Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity); 533 534 for (ModuleReference mref : nameToReference.values()) { 535 ModuleDescriptor descriptor = mref.descriptor(); 536 String name = descriptor.name(); 537 538 ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref); 539 540 Set<ResolvedModule> reads = new HashSet<>(); 541 Set<ResolvedModule> requiresTransitive = new HashSet<>(); 542 543 for (ModuleDescriptor.Requires requires : descriptor.requires()) { 544 String dn = requires.name(); 545 546 ResolvedModule m2 = null; 547 ModuleReference mref2 = nameToReference.get(dn); 548 if (mref2 != null) { 549 // same configuration 550 m2 = computeIfAbsent(nameToResolved, dn, cf, mref2); 551 } else { 552 // parent configuration 553 m2 = findInParent(dn); 554 if (m2 == null) { 555 assert requires.modifiers().contains(Modifier.STATIC); 556 continue; 557 } 558 } 559 560 // m1 requires m2 => m1 reads m2 561 reads.add(m2); 562 563 // m1 requires transitive m2 564 if (requires.modifiers().contains(Modifier.TRANSITIVE)) { 565 requiresTransitive.add(m2); 566 } 567 568 } 569 570 // automatic modules read all selected modules and all modules 571 // in parent configurations 572 if (descriptor.isAutomatic()) { 573 574 // reads all selected modules 575 // `requires transitive` all selected automatic modules 576 for (ModuleReference mref2 : nameToReference.values()) { 577 ModuleDescriptor descriptor2 = mref2.descriptor(); 578 String name2 = descriptor2.name(); 579 580 if (!name.equals(name2)) { 581 ResolvedModule m2 582 = computeIfAbsent(nameToResolved, name2, cf, mref2); 583 reads.add(m2); 584 if (descriptor2.isAutomatic()) 585 requiresTransitive.add(m2); 586 } 587 } 588 589 // reads all modules in parent configurations 590 // `requires transitive` all automatic modules in parent 591 // configurations 592 for (Configuration parent : parents) { 593 parent.configurations() 594 .map(Configuration::modules) 595 .flatMap(Set::stream) 596 .forEach(m -> { 597 reads.add(m); 598 if (m.reference().descriptor().isAutomatic()) 599 requiresTransitive.add(m); 600 }); 601 } 602 } 603 604 g1.put(m1, reads); 605 g2.put(m1, requiresTransitive); 606 } 607 608 // Iteratively update g1 until there are no more requires transitive 609 // to propagate 610 boolean changed; 611 List<ResolvedModule> toAdd = new ArrayList<>(); 612 do { 613 changed = false; 614 for (Set<ResolvedModule> m1Reads : g1.values()) { 615 for (ResolvedModule m2 : m1Reads) { 616 Set<ResolvedModule> m2RequiresTransitive = g2.get(m2); 617 if (m2RequiresTransitive != null) { 618 for (ResolvedModule m3 : m2RequiresTransitive) { 619 if (!m1Reads.contains(m3)) { 620 // m1 reads m2, m2 requires transitive m3 621 // => need to add m1 reads m3 622 toAdd.add(m3); 623 } 624 } 625 } 626 } 627 if (!toAdd.isEmpty()) { 628 m1Reads.addAll(toAdd); 629 toAdd.clear(); 630 changed = true; 631 } 632 } 633 } while (changed); 634 635 return g1; 636 } 637 638 /** 639 * Equivalent to 640 * <pre>{@code 641 * map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref)) 642 * </pre>} 643 */ 644 private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map, 645 String name, 646 Configuration cf, 647 ModuleReference mref) 648 { 649 ResolvedModule m = map.get(name); 650 if (m == null) { 651 m = new ResolvedModule(cf, mref); 652 map.put(name, m); 653 } 654 return m; 655 } 656 657 658 /** 659 * Checks the readability graph to ensure that 660 * <ol> 661 * <li><p> A module does not read two or more modules with the same name. 662 * This includes the case where a module reads another another with the 663 * same name as itself. </p></li> 664 * <li><p> Two or more modules in the configuration don't export the same 665 * package to a module that reads both. This includes the case where a 666 * module {@code M} containing package {@code p} reads another module 667 * that exports {@code p} to {@code M}. </p></li> 668 * <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}" 669 * or "{@code provides p.S with ...}" but package {@code p} is neither 670 * in module {@code M} nor exported to {@code M} by any module that 671 * {@code M} reads. </p></li> 672 * </ol> 673 */ 674 private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) { 675 676 for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) { 677 ModuleDescriptor descriptor1 = e.getKey().descriptor(); 678 String name1 = descriptor1.name(); 679 680 // the names of the modules that are read (including self) 681 Set<String> names = new HashSet<>(); 682 names.add(name1); 683 684 // the map of packages that are local or exported to descriptor1 685 Map<String, ModuleDescriptor> packageToExporter = new HashMap<>(); 686 687 // local packages 688 Set<String> packages = descriptor1.packages(); 689 for (String pn : packages) { 690 packageToExporter.put(pn, descriptor1); 691 } 692 693 // descriptor1 reads descriptor2 694 Set<ResolvedModule> reads = e.getValue(); 695 for (ResolvedModule endpoint : reads) { 696 ModuleDescriptor descriptor2 = endpoint.descriptor(); 697 698 String name2 = descriptor2.name(); 699 if (descriptor2 != descriptor1 && !names.add(name2)) { 700 if (name2.equals(name1)) { 701 resolveFail("Module %s reads another module named %s", 702 name1, name1); 703 } else{ 704 resolveFail("Module %s reads more than one module named %s", 705 name1, name2); 706 } 707 } 708 709 if (descriptor2.isAutomatic()) { 710 // automatic modules read self and export all packages 711 if (descriptor2 != descriptor1) { 712 for (String source : descriptor2.packages()) { 713 ModuleDescriptor supplier 714 = packageToExporter.putIfAbsent(source, descriptor2); 715 716 // descriptor2 and 'supplier' export source to descriptor1 717 if (supplier != null) { 718 failTwoSuppliers(descriptor1, source, descriptor2, supplier); 719 } 720 } 721 722 } 723 } else { 724 for (ModuleDescriptor.Exports export : descriptor2.exports()) { 725 if (export.isQualified()) { 726 if (!export.targets().contains(descriptor1.name())) 727 continue; 728 } 729 730 // source is exported by descriptor2 731 String source = export.source(); 732 ModuleDescriptor supplier 733 = packageToExporter.putIfAbsent(source, descriptor2); 734 735 // descriptor2 and 'supplier' export source to descriptor1 736 if (supplier != null) { 737 failTwoSuppliers(descriptor1, source, descriptor2, supplier); 738 } 739 } 740 741 } 742 } 743 744 // uses/provides checks not applicable to automatic modules 745 if (!descriptor1.isAutomatic()) { 746 747 // uses S 748 for (String service : descriptor1.uses()) { 749 String pn = packageName(service); 750 if (!packageToExporter.containsKey(pn)) { 751 resolveFail("Module %s does not read a module that exports %s", 752 descriptor1.name(), pn); 753 } 754 } 755 756 // provides S 757 for (ModuleDescriptor.Provides provides : descriptor1.provides()) { 758 String pn = packageName(provides.service()); 759 if (!packageToExporter.containsKey(pn)) { 760 resolveFail("Module %s does not read a module that exports %s", 761 descriptor1.name(), pn); 762 } 763 } 764 765 } 766 767 } 768 769 } 770 771 /** 772 * Fail because a module in the configuration exports the same package to 773 * a module that reads both. This includes the case where a module M 774 * containing a package p reads another module that exports p to at least 775 * module M. 776 */ 777 private void failTwoSuppliers(ModuleDescriptor descriptor, 778 String source, 779 ModuleDescriptor supplier1, 780 ModuleDescriptor supplier2) { 781 782 if (supplier2 == descriptor) { 783 ModuleDescriptor tmp = supplier1; 784 supplier1 = supplier2; 785 supplier2 = tmp; 786 } 787 788 if (supplier1 == descriptor) { 789 resolveFail("Module %s contains package %s" 790 + ", module %s exports package %s to %s", 791 descriptor.name(), 792 source, 793 supplier2.name(), 794 source, 795 descriptor.name()); 796 } else { 797 resolveFail("Modules %s and %s export package %s to module %s", 798 supplier1.name(), 799 supplier2.name(), 800 source, 801 descriptor.name()); 802 } 803 804 } 805 806 807 /** 808 * Find a module of the given name in the parent configurations 809 */ 810 private ResolvedModule findInParent(String mn) { 811 for (Configuration parent : parents) { 812 Optional<ResolvedModule> om = parent.findModule(mn); 813 if (om.isPresent()) 814 return om.get(); 815 } 816 return null; 817 } 818 819 820 /** 821 * Invokes the beforeFinder to find method to find the given module. 822 */ 823 private ModuleReference findWithBeforeFinder(String mn) { 824 825 return beforeFinder.find(mn).orElse(null); 826 827 } 828 829 /** 830 * Invokes the afterFinder to find method to find the given module. 831 */ 832 private ModuleReference findWithAfterFinder(String mn) { 833 return afterFinder.find(mn).orElse(null); 834 } 835 836 /** 837 * Returns the set of all modules that are observable with the before 838 * and after ModuleFinders. 839 */ 840 private Set<ModuleReference> findAll() { 841 Set<ModuleReference> beforeModules = beforeFinder.findAll(); 842 Set<ModuleReference> afterModules = afterFinder.findAll(); 843 844 if (afterModules.isEmpty()) 845 return beforeModules; 846 847 if (beforeModules.isEmpty() 848 && parents.size() == 1 849 && parents.get(0) == Configuration.empty()) 850 return afterModules; 851 852 Set<ModuleReference> result = new HashSet<>(beforeModules); 853 for (ModuleReference mref : afterModules) { 854 String name = mref.descriptor().name(); 855 if (!beforeFinder.find(name).isPresent() 856 && findInParent(name) == null) { 857 result.add(mref); 858 } 859 } 860 861 return result; 862 } 863 864 /** 865 * Returns the package name 866 */ 867 private static String packageName(String cn) { 868 int index = cn.lastIndexOf("."); 869 return (index == -1) ? "" : cn.substring(0, index); 870 } 871 872 /** 873 * Throw FindException with the given format string and arguments 874 */ 875 private static void findFail(String fmt, Object ... args) { 876 String msg = String.format(fmt, args); 877 throw new FindException(msg); 878 } 879 880 /** 881 * Throw ResolutionException with the given format string and arguments 882 */ 883 private static void resolveFail(String fmt, Object ... args) { 884 String msg = String.format(fmt, args); 885 throw new ResolutionException(msg); 886 } 887 888 /** 889 * Tracing support 890 */ 891 892 private boolean isTracing() { 893 return traceOutput != null; 894 } 895 896 private void trace(String fmt, Object ... args) { 897 if (traceOutput != null) { 898 traceOutput.format(fmt, args); 899 traceOutput.println(); 900 } 901 } 902 903 private String nameAndInfo(ModuleReference mref) { 904 ModuleDescriptor descriptor = mref.descriptor(); 905 StringBuilder sb = new StringBuilder(descriptor.name()); 906 mref.location().ifPresent(uri -> sb.append(" " + uri)); 907 if (descriptor.isAutomatic()) 908 sb.append(" automatic"); 909 return sb.toString(); 910 } 911 }