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 26package java.lang.module; 27 28import java.io.PrintStream; 29import java.lang.module.ModuleDescriptor.Provides; 30import java.lang.module.ModuleDescriptor.Requires.Modifier; 31import java.net.URI; 32import java.util.ArrayDeque; 33import java.util.ArrayList; 34import java.util.Arrays; 35import java.util.Collection; 36import java.util.Deque; 37import java.util.HashMap; 38import java.util.HashSet; 39import java.util.LinkedHashSet; 40import java.util.List; 41import java.util.Map; 42import java.util.Optional; 43import java.util.Set; 44import java.util.stream.Collectors; 45 46import jdk.internal.module.ModuleHashes; 47import jdk.internal.module.ModuleReferenceImpl; 48import 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 58final 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 {@code Map}. The resolved modules will be in the given 357 * configuration. 358 * 359 * @param check {@true} to execute the post resolution checks 360 */ 361 Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf, 362 boolean check) 363 { 364 if (check) { 365 detectCycles(); 366 checkHashes(); 367 } 368 369 Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf); 370 371 if (check) { 372 checkExportSuppliers(graph); 373 } 374 375 return graph; 376 } 377 378 /** 379 * Checks the given module graph for cycles. 380 * 381 * For now the implementation is a simple depth first search on the 382 * dependency graph. We'll replace this later, maybe with Tarjan. 383 */ 384 private void detectCycles() { 385 visited = new HashSet<>(); 386 visitPath = new LinkedHashSet<>(); // preserve insertion order 387 for (ModuleReference mref : nameToReference.values()) { 388 visit(mref.descriptor()); 389 } 390 visited.clear(); 391 } 392 393 // the modules that were visited 394 private Set<ModuleDescriptor> visited; 395 396 // the modules in the current visit path 397 private Set<ModuleDescriptor> visitPath; 398 399 private void visit(ModuleDescriptor descriptor) { 400 if (!visited.contains(descriptor)) { 401 boolean added = visitPath.add(descriptor); 402 if (!added) { 403 resolveFail("Cycle detected: %s", cycleAsString(descriptor)); 404 } 405 for (ModuleDescriptor.Requires requires : descriptor.requires()) { 406 String dn = requires.name(); 407 408 ModuleReference mref = nameToReference.get(dn); 409 if (mref != null) { 410 ModuleDescriptor other = mref.descriptor(); 411 if (other != descriptor) { 412 // dependency is in this configuration 413 visit(other); 414 } 415 } 416 } 417 visitPath.remove(descriptor); 418 visited.add(descriptor); 419 } 420 } 421 422 /** 423 * Returns a String with a list of the modules in a detected cycle. 424 */ 425 private String cycleAsString(ModuleDescriptor descriptor) { 426 List<ModuleDescriptor> list = new ArrayList<>(visitPath); 427 list.add(descriptor); 428 int index = list.indexOf(descriptor); 429 return list.stream() 430 .skip(index) 431 .map(ModuleDescriptor::name) 432 .collect(Collectors.joining(" -> ")); 433 } 434 435 436 /** 437 * Checks the hashes in the module descriptor to ensure that they match 438 * any recorded hashes. 439 */ 440 private void checkHashes() { 441 for (ModuleReference mref : nameToReference.values()) { 442 443 // get the recorded hashes, if any 444 if (!(mref instanceof ModuleReferenceImpl)) 445 continue; 446 ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes(); 447 if (hashes == null) 448 continue; 449 450 ModuleDescriptor descriptor = mref.descriptor(); 451 String algorithm = hashes.algorithm(); 452 for (String dn : hashes.names()) { 453 ModuleReference mref2 = nameToReference.get(dn); 454 if (mref2 == null) { 455 ResolvedModule resolvedModule = findInParent(dn); 456 if (resolvedModule != null) 457 mref2 = resolvedModule.reference(); 458 } 459 if (mref2 == null) 460 continue; 461 462 if (!(mref2 instanceof ModuleReferenceImpl)) { 463 findFail("Unable to compute the hash of module %s", dn); 464 } 465 466 ModuleReferenceImpl other = (ModuleReferenceImpl)mref2; 467 if (other != null) { 468 byte[] recordedHash = hashes.hashFor(dn); 469 byte[] actualHash = other.computeHash(algorithm); 470 if (actualHash == null) 471 findFail("Unable to compute the hash of module %s", dn); 472 if (!Arrays.equals(recordedHash, actualHash)) { 473 findFail("Hash of %s (%s) differs to expected hash (%s)" + 474 " recorded in %s", dn, toHexString(actualHash), 475 toHexString(recordedHash), descriptor.name()); 476 } 477 } 478 } 479 480 } 481 } 482 483 private static String toHexString(byte[] ba) { 484 StringBuilder sb = new StringBuilder(ba.length * 2); 485 for (byte b: ba) { 486 sb.append(String.format("%02x", b & 0xff)); 487 } 488 return sb.toString(); 489 } 490 491 492 /** 493 * Computes the readability graph for the modules in the given Configuration. 494 * 495 * The readability graph is created by propagating "requires" through the 496 * "requires transitive" edges of the module dependence graph. So if the 497 * module dependence graph has m1 requires m2 && m2 requires transitive m3 498 * then the resulting readability graph will contain m1 reads m2, m1 reads m3, 499 * and m2 reads m3. 500 */ 501 private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) { 502 503 // initial capacity of maps to avoid resizing 504 int capacity = 1 + (4 * nameToReference.size())/ 3; 505 506 // the "reads" graph starts as a module dependence graph and 507 // is iteratively updated to be the readability graph 508 Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity); 509 510 // the "requires transitive" graph, contains requires transitive edges only 511 Map<ResolvedModule, Set<ResolvedModule>> g2; 512 513 // need "requires transitive" from the modules in parent configurations 514 // as there may be selected modules that have a dependency on modules in 515 // the parent configuration. 516 if (ModuleLayer.boot() == null) { 517 g2 = new HashMap<>(capacity); 518 } else { 519 g2 = parents.stream() 520 .flatMap(Configuration::configurations) 521 .distinct() 522 .flatMap(c -> 523 c.modules().stream().flatMap(m1 -> 524 m1.descriptor().requires().stream() 525 .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE)) 526 .flatMap(r -> { 527 Optional<ResolvedModule> m2 = c.findModule(r.name()); 528 assert m2.isPresent() 529 || r.modifiers().contains(Modifier.STATIC); 530 return m2.stream(); 531 }) 532 .map(m2 -> Map.entry(m1, m2)) 533 ) 534 ) 535 // stream of m1->m2 536 .collect(Collectors.groupingBy(Map.Entry::getKey, 537 HashMap::new, 538 Collectors.mapping(Map.Entry::getValue, Collectors.toSet()) 539 )); 540 } 541 542 // populate g1 and g2 with the dependences from the selected modules 543 544 Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity); 545 546 for (ModuleReference mref : nameToReference.values()) { 547 ModuleDescriptor descriptor = mref.descriptor(); 548 String name = descriptor.name(); 549 550 ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref); 551 552 Set<ResolvedModule> reads = new HashSet<>(); 553 Set<ResolvedModule> requiresTransitive = new HashSet<>(); 554 555 for (ModuleDescriptor.Requires requires : descriptor.requires()) { 556 String dn = requires.name(); 557 558 ResolvedModule m2 = null; 559 ModuleReference mref2 = nameToReference.get(dn); 560 if (mref2 != null) { 561 // same configuration 562 m2 = computeIfAbsent(nameToResolved, dn, cf, mref2); 563 } else { 564 // parent configuration 565 m2 = findInParent(dn); 566 if (m2 == null) { 567 assert requires.modifiers().contains(Modifier.STATIC); 568 continue; 569 } 570 } 571 572 // m1 requires m2 => m1 reads m2 573 reads.add(m2); 574 575 // m1 requires transitive m2 576 if (requires.modifiers().contains(Modifier.TRANSITIVE)) { 577 requiresTransitive.add(m2); 578 } 579 580 } 581 582 // automatic modules read all selected modules and all modules 583 // in parent configurations 584 if (descriptor.isAutomatic()) { 585 586 // reads all selected modules 587 // `requires transitive` all selected automatic modules 588 for (ModuleReference mref2 : nameToReference.values()) { 589 ModuleDescriptor descriptor2 = mref2.descriptor(); 590 String name2 = descriptor2.name(); 591 592 if (!name.equals(name2)) { 593 ResolvedModule m2 594 = computeIfAbsent(nameToResolved, name2, cf, mref2); 595 reads.add(m2); 596 if (descriptor2.isAutomatic()) 597 requiresTransitive.add(m2); 598 } 599 } 600 601 // reads all modules in parent configurations 602 // `requires transitive` all automatic modules in parent 603 // configurations 604 for (Configuration parent : parents) { 605 parent.configurations() 606 .map(Configuration::modules) 607 .flatMap(Set::stream) 608 .forEach(m -> { 609 reads.add(m); 610 if (m.reference().descriptor().isAutomatic()) 611 requiresTransitive.add(m); 612 }); 613 } 614 } 615 616 g1.put(m1, reads); 617 g2.put(m1, requiresTransitive); 618 } 619 620 // Iteratively update g1 until there are no more requires transitive 621 // to propagate 622 boolean changed; 623 List<ResolvedModule> toAdd = new ArrayList<>(); 624 do { 625 changed = false; 626 for (Set<ResolvedModule> m1Reads : g1.values()) { 627 for (ResolvedModule m2 : m1Reads) { 628 Set<ResolvedModule> m2RequiresTransitive = g2.get(m2); 629 if (m2RequiresTransitive != null) { 630 for (ResolvedModule m3 : m2RequiresTransitive) { 631 if (!m1Reads.contains(m3)) { 632 // m1 reads m2, m2 requires transitive m3 633 // => need to add m1 reads m3 634 toAdd.add(m3); 635 } 636 } 637 } 638 } 639 if (!toAdd.isEmpty()) { 640 m1Reads.addAll(toAdd); 641 toAdd.clear(); 642 changed = true; 643 } 644 } 645 } while (changed); 646 647 return g1; 648 } 649 650 /** 651 * Equivalent to 652 * <pre>{@code 653 * map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref)) 654 * </pre>} 655 */ 656 private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map, 657 String name, 658 Configuration cf, 659 ModuleReference mref) 660 { 661 ResolvedModule m = map.get(name); 662 if (m == null) { 663 m = new ResolvedModule(cf, mref); 664 map.put(name, m); 665 } 666 return m; 667 } 668 669 670 /** 671 * Checks the readability graph to ensure that 672 * <ol> 673 * <li><p> A module does not read two or more modules with the same name. 674 * This includes the case where a module reads another another with the 675 * same name as itself. </p></li> 676 * <li><p> Two or more modules in the configuration don't export the same 677 * package to a module that reads both. This includes the case where a 678 * module {@code M} containing package {@code p} reads another module 679 * that exports {@code p} to {@code M}. </p></li> 680 * <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}" 681 * or "{@code provides p.S with ...}" but package {@code p} is neither 682 * in module {@code M} nor exported to {@code M} by any module that 683 * {@code M} reads. </p></li> 684 * </ol> 685 */ 686 private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) { 687 688 for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) { 689 ModuleDescriptor descriptor1 = e.getKey().descriptor(); 690 String name1 = descriptor1.name(); 691 692 // the names of the modules that are read (including self) 693 Set<String> names = new HashSet<>(); 694 names.add(name1); 695 696 // the map of packages that are local or exported to descriptor1 697 Map<String, ModuleDescriptor> packageToExporter = new HashMap<>(); 698 699 // local packages 700 Set<String> packages = descriptor1.packages(); 701 for (String pn : packages) { 702 packageToExporter.put(pn, descriptor1); 703 } 704 705 // descriptor1 reads descriptor2 706 Set<ResolvedModule> reads = e.getValue(); 707 for (ResolvedModule endpoint : reads) { 708 ModuleDescriptor descriptor2 = endpoint.descriptor(); 709 710 String name2 = descriptor2.name(); 711 if (descriptor2 != descriptor1 && !names.add(name2)) { 712 if (name2.equals(name1)) { 713 resolveFail("Module %s reads another module named %s", 714 name1, name1); 715 } else{ 716 resolveFail("Module %s reads more than one module named %s", 717 name1, name2); 718 } 719 } 720 721 if (descriptor2.isAutomatic()) { 722 // automatic modules read self and export all packages 723 if (descriptor2 != descriptor1) { 724 for (String source : descriptor2.packages()) { 725 ModuleDescriptor supplier 726 = packageToExporter.putIfAbsent(source, descriptor2); 727 728 // descriptor2 and 'supplier' export source to descriptor1 729 if (supplier != null) { 730 failTwoSuppliers(descriptor1, source, descriptor2, supplier); 731 } 732 } 733 734 } 735 } else { 736 for (ModuleDescriptor.Exports export : descriptor2.exports()) { 737 if (export.isQualified()) { 738 if (!export.targets().contains(descriptor1.name())) 739 continue; 740 } 741 742 // source is exported by descriptor2 743 String source = export.source(); 744 ModuleDescriptor supplier 745 = packageToExporter.putIfAbsent(source, descriptor2); 746 747 // descriptor2 and 'supplier' export source to descriptor1 748 if (supplier != null) { 749 failTwoSuppliers(descriptor1, source, descriptor2, supplier); 750 } 751 } 752 753 } 754 } 755 756 // uses/provides checks not applicable to automatic modules 757 if (!descriptor1.isAutomatic()) { 758 759 // uses S 760 for (String service : descriptor1.uses()) { 761 String pn = packageName(service); 762 if (!packageToExporter.containsKey(pn)) { 763 resolveFail("Module %s does not read a module that exports %s", 764 descriptor1.name(), pn); 765 } 766 } 767 768 // provides S 769 for (ModuleDescriptor.Provides provides : descriptor1.provides()) { 770 String pn = packageName(provides.service()); 771 if (!packageToExporter.containsKey(pn)) { 772 resolveFail("Module %s does not read a module that exports %s", 773 descriptor1.name(), pn); 774 } 775 } 776 777 } 778 779 } 780 781 } 782 783 /** 784 * Fail because a module in the configuration exports the same package to 785 * a module that reads both. This includes the case where a module M 786 * containing a package p reads another module that exports p to at least 787 * module M. 788 */ 789 private void failTwoSuppliers(ModuleDescriptor descriptor, 790 String source, 791 ModuleDescriptor supplier1, 792 ModuleDescriptor supplier2) { 793 794 if (supplier2 == descriptor) { 795 ModuleDescriptor tmp = supplier1; 796 supplier1 = supplier2; 797 supplier2 = tmp; 798 } 799 800 if (supplier1 == descriptor) { 801 resolveFail("Module %s contains package %s" 802 + ", module %s exports package %s to %s", 803 descriptor.name(), 804 source, 805 supplier2.name(), 806 source, 807 descriptor.name()); 808 } else { 809 resolveFail("Modules %s and %s export package %s to module %s", 810 supplier1.name(), 811 supplier2.name(), 812 source, 813 descriptor.name()); 814 } 815 816 } 817 818 819 /** 820 * Find a module of the given name in the parent configurations 821 */ 822 private ResolvedModule findInParent(String mn) { 823 for (Configuration parent : parents) { 824 Optional<ResolvedModule> om = parent.findModule(mn); 825 if (om.isPresent()) 826 return om.get(); 827 } 828 return null; 829 } 830 831 832 /** 833 * Invokes the beforeFinder to find method to find the given module. 834 */ 835 private ModuleReference findWithBeforeFinder(String mn) { 836 837 return beforeFinder.find(mn).orElse(null); 838 839 } 840 841 /** 842 * Invokes the afterFinder to find method to find the given module. 843 */ 844 private ModuleReference findWithAfterFinder(String mn) { 845 return afterFinder.find(mn).orElse(null); 846 } 847 848 /** 849 * Returns the set of all modules that are observable with the before 850 * and after ModuleFinders. 851 */ 852 private Set<ModuleReference> findAll() { 853 Set<ModuleReference> beforeModules = beforeFinder.findAll(); 854 Set<ModuleReference> afterModules = afterFinder.findAll(); 855 856 if (afterModules.isEmpty()) 857 return beforeModules; 858 859 if (beforeModules.isEmpty() 860 && parents.size() == 1 861 && parents.get(0) == Configuration.empty()) 862 return afterModules; 863 864 Set<ModuleReference> result = new HashSet<>(beforeModules); 865 for (ModuleReference mref : afterModules) { 866 String name = mref.descriptor().name(); 867 if (!beforeFinder.find(name).isPresent() 868 && findInParent(name) == null) { 869 result.add(mref); 870 } 871 } 872 873 return result; 874 } 875 876 /** 877 * Returns the package name 878 */ 879 private static String packageName(String cn) { 880 int index = cn.lastIndexOf("."); 881 return (index == -1) ? "" : cn.substring(0, index); 882 } 883 884 /** 885 * Throw FindException with the given format string and arguments 886 */ 887 private static void findFail(String fmt, Object ... args) { 888 String msg = String.format(fmt, args); 889 throw new FindException(msg); 890 } 891 892 /** 893 * Throw ResolutionException with the given format string and arguments 894 */ 895 private static void resolveFail(String fmt, Object ... args) { 896 String msg = String.format(fmt, args); 897 throw new ResolutionException(msg); 898 } 899 900 /** 901 * Tracing support 902 */ 903 904 private boolean isTracing() { 905 return traceOutput != null; 906 } 907 908 private void trace(String fmt, Object ... args) { 909 if (traceOutput != null) { 910 traceOutput.format(fmt, args); 911 traceOutput.println(); 912 } 913 } 914 915 private String nameAndInfo(ModuleReference mref) { 916 ModuleDescriptor descriptor = mref.descriptor(); 917 StringBuilder sb = new StringBuilder(descriptor.name()); 918 mref.location().ifPresent(uri -> sb.append(" " + uri)); 919 if (descriptor.isAutomatic()) 920 sb.append(" automatic"); 921 return sb.toString(); 922 } 923} 924