1 /*
   2  * Copyright (c) 2013, 2016, 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.lang.module.ModuleDescriptor.Requires.Modifier;
  29 import java.lang.reflect.Layer;
  30 import java.util.ArrayDeque;
  31 import java.util.ArrayList;
  32 import java.util.Collection;
  33 import java.util.Deque;
  34 import java.util.HashMap;
  35 import java.util.HashSet;
  36 import java.util.LinkedHashSet;
  37 import java.util.List;
  38 import java.util.Map;
  39 import java.util.Map.Entry;
  40 import java.util.Objects;
  41 import java.util.Optional;
  42 import java.util.Set;
  43 import java.util.StringJoiner;
  44 import java.util.stream.Collectors;
  45 
  46 import jdk.internal.module.Hasher;
  47 
  48 /**
  49  * The resolver used by {@link Configuration#resolveRequires} and
  50  * {@link Configuration#resolveRequiresAndUses}.
  51  */
  52 
  53 final class Resolver {
  54 
  55     private final ModuleFinder beforeFinder;
  56     private final Configuration parent;
  57     private final ModuleFinder afterFinder;
  58 
  59     // maps module name to module reference
  60     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
  61 
  62 
  63     Resolver(ModuleFinder beforeFinder,
  64              Configuration parent,
  65              ModuleFinder afterFinder) {
  66         this.beforeFinder = beforeFinder;
  67         this.parent = parent;
  68         this.afterFinder = afterFinder;
  69     }
  70 
  71 
  72     /**
  73      * Resolves the given named modules.
  74      *
  75      * @throws ResolutionException
  76      */
  77     Resolver resolveRequires(Collection<String> roots) {
  78 
  79         long start = trace_start("Resolve");
  80 
  81         // create the visit stack to get us started
  82         Deque<ModuleDescriptor> q = new ArrayDeque<>();
  83         for (String root : roots) {
  84 
  85             // find root module
  86             ModuleReference mref = findWithBeforeFinder(root);
  87             if (mref == null) {
  88                 if (parent.findModule(root).isPresent()) {
  89                     // in parent, nothing to do
  90                     continue;
  91                 }
  92                 mref = findWithAfterFinder(root);
  93                 if (mref == null) {
  94                     fail("Module %s not found", root);
  95                 }
  96             }
  97 
  98             if (TRACE) {
  99                 trace("Root module %s located", root);
 100                 if (mref.location().isPresent())
 101                     trace("  (%s)", mref.location().get());
 102             }
 103 
 104             assert mref.descriptor().name().equals(root);
 105             nameToReference.put(root, mref);
 106             q.push(mref.descriptor());
 107         }
 108 
 109         resolve(q);
 110 
 111         if (TRACE) {
 112             long duration = System.currentTimeMillis() - start;
 113             Set<String> names = nameToReference.keySet();
 114             trace("Resolver completed in %s ms", duration);
 115             names.stream().sorted().forEach(name -> trace("  %s", name));
 116         }
 117 
 118         return this;
 119     }
 120 
 121     /**
 122      * Resolve all modules in the given queue. On completion the queue will be
 123      * empty and any resolved modules will be added to {@code nameToReference}.
 124      *
 125      * @return The set of module resolved by this invocation of resolve
 126      */
 127     private Set<ModuleDescriptor> resolve(Deque<ModuleDescriptor> q) {
 128         Set<ModuleDescriptor> resolved = new HashSet<>();
 129 
 130         while (!q.isEmpty()) {
 131             ModuleDescriptor descriptor = q.poll();
 132             assert nameToReference.containsKey(descriptor.name());
 133 
 134             // process dependences
 135             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 136                 String dn = requires.name();
 137 
 138                 // find dependence
 139                 ModuleReference mref = findWithBeforeFinder(dn);
 140                 if (mref == null) {
 141                     if (parent.findModule(dn).isPresent())
 142                         continue;
 143 
 144                     mref = findWithAfterFinder(dn);
 145                     if (mref == null) {
 146                         fail("Module %s not found, required by %s",
 147                                 dn, descriptor.name());
 148                     }
 149                 }
 150 
 151                 if (!nameToReference.containsKey(dn)) {
 152                     nameToReference.put(dn, mref);
 153                     q.offer(mref.descriptor());
 154                     resolved.add(mref.descriptor());
 155 
 156                     if (TRACE) {
 157                         trace("Module %s located, required by %s",
 158                                 dn, descriptor.name());
 159                         if (mref.location().isPresent())
 160                             trace("  (%s)", mref.location().get());
 161                     }
 162                 }
 163 
 164             }
 165 
 166             resolved.add(descriptor);
 167         }
 168 
 169         return resolved;
 170     }
 171 
 172     /**
 173      * Augments the set of resolved modules with modules induced by the
 174      * service-use relation.
 175      */
 176     Resolver resolveUses() {
 177 
 178         long start = trace_start("Bind");
 179 
 180         // Scan the finders for all available service provider modules. As
 181         // java.base uses services then then module finders will be scanned
 182         // anyway.
 183         Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
 184         for (ModuleReference mref : findAll()) {
 185             ModuleDescriptor descriptor = mref.descriptor();
 186             if (!descriptor.provides().isEmpty()) {
 187 
 188                 for (String sn : descriptor.provides().keySet()) {
 189                     // computeIfAbsent
 190                     Set<ModuleReference> providers = availableProviders.get(sn);
 191                     if (providers == null) {
 192                         providers = new HashSet<>();
 193                         availableProviders.put(sn, providers);
 194                     }
 195                     providers.add(mref);
 196                 }
 197 
 198             }
 199         }
 200 
 201         // create the visit stack
 202         Deque<ModuleDescriptor> q = new ArrayDeque<>();
 203 
 204         // the initial set of modules that may use services
 205         Set<ModuleDescriptor> candidateConsumers = new HashSet<>();
 206         Configuration p = parent;
 207         while (p != null) {
 208             candidateConsumers.addAll(p.descriptors());
 209             p = p.parent().orElse(null);
 210         }
 211         for (ModuleReference mref : nameToReference.values()) {
 212             candidateConsumers.add(mref.descriptor());
 213         }
 214 
 215 
 216         // Where there is a consumer of a service then resolve all modules
 217         // that provide an implementation of that service
 218         do {
 219             for (ModuleDescriptor descriptor : candidateConsumers) {
 220                 if (!descriptor.uses().isEmpty()) {
 221                     for (String service : descriptor.uses()) {
 222                         Set<ModuleReference> mrefs = availableProviders.get(service);
 223                         if (mrefs != null) {
 224                             for (ModuleReference mref : mrefs) {
 225                                 ModuleDescriptor provider = mref.descriptor();
 226                                 if (!provider.equals(descriptor)) {
 227 
 228                                     trace("Module %s provides %s, used by %s",
 229                                             provider.name(), service, descriptor.name());
 230 
 231                                     String pn = provider.name();
 232                                     if (!nameToReference.containsKey(pn)) {
 233 
 234                                         if (TRACE && mref.location().isPresent())
 235                                             trace("  (%s)", mref.location().get());
 236 
 237                                         nameToReference.put(pn, mref);
 238                                         q.push(provider);
 239                                     }
 240                                 }
 241                             }
 242                         }
 243                     }
 244                 }
 245             }
 246 
 247             candidateConsumers = resolve(q);
 248 
 249         } while (!candidateConsumers.isEmpty());
 250 
 251 
 252         if (TRACE) {
 253             long duration = System.currentTimeMillis() - start;
 254             Set<String> names = nameToReference.keySet();
 255             trace("Bind completed in %s ms", duration);
 256             names.stream().sorted().forEach(name -> trace("  %s", name));
 257         }
 258 
 259         return this;
 260     }
 261 
 262 
 263     /**
 264      * Execute post-resolution checks and returns the module graph of resolved
 265      * modules as {@code Map}. The resolved modules will be in the given
 266      * configuration.
 267      */
 268     Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) {
 269 
 270         detectCycles();
 271 
 272         checkPlatformConstraints();
 273 
 274         checkHashes();
 275 
 276         Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
 277 
 278         checkExportSuppliers(graph);
 279 
 280         return graph;
 281     }
 282 
 283 
 284     /**
 285      * Checks the given module graph for cycles.
 286      *
 287      * For now the implementation is a simple depth first search on the
 288      * dependency graph. We'll replace this later, maybe with Tarjan.
 289      */
 290     private void detectCycles() {
 291         visited = new HashSet<>();
 292         visitPath = new LinkedHashSet<>(); // preserve insertion order
 293         for (ModuleReference mref : nameToReference.values()) {
 294             visit(mref.descriptor());
 295         }
 296         visited.clear();
 297     }
 298 
 299     // the modules that were visited
 300     private Set<ModuleDescriptor> visited;
 301 
 302     // the modules in the current visit path
 303     private Set<ModuleDescriptor> visitPath;
 304 
 305     private void visit(ModuleDescriptor descriptor) {
 306         if (!visited.contains(descriptor)) {
 307             boolean added = visitPath.add(descriptor);
 308             if (!added) {
 309                 throw new ResolutionException("Cycle detected: " +
 310                         cycleAsString(descriptor));
 311             }
 312             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 313                 String dn = requires.name();
 314 
 315                 ModuleReference mref = nameToReference.get(dn);
 316                 if (mref != null) {
 317                     ModuleDescriptor other = mref.descriptor();
 318                     if (other != descriptor) {
 319                         // dependency is in this configuration
 320                         visit(other);
 321                     }
 322                 }
 323             }
 324             visitPath.remove(descriptor);
 325             visited.add(descriptor);
 326         }
 327     }
 328 
 329     /**
 330      * Returns a String with a list of the modules in a detected cycle.
 331      */
 332     private String cycleAsString(ModuleDescriptor descriptor) {
 333         List<ModuleDescriptor> list = new ArrayList<>(visitPath);
 334         list.add(descriptor);
 335         int index = list.indexOf(descriptor);
 336         return list.stream()
 337                 .skip(index)
 338                 .map(ModuleDescriptor::name)
 339                 .collect(Collectors.joining(" -> "));
 340     }
 341 
 342 
 343     /**
 344      * If there are platform specific modules then check that the OS name,
 345      * architecture and version match.
 346      *
 347      * @apiNote This method does not currently check if the OS matches
 348      *          platform specific modules in parent configurations.
 349      */
 350     private void checkPlatformConstraints() {
 351 
 352         // first module encountered that is platform specific
 353         String savedModuleName = null;
 354         String savedOsName = null;
 355         String savedOsArch = null;
 356         String savedOsVersion = null;
 357 
 358         for (ModuleReference mref : nameToReference.values()) {
 359             ModuleDescriptor descriptor = mref.descriptor();
 360 
 361             String osName = descriptor.osName().orElse(null);
 362             String osArch = descriptor.osArch().orElse(null);
 363             String osVersion = descriptor.osVersion().orElse(null);
 364 
 365             if (osName != null || osArch != null || osVersion != null) {
 366 
 367                 if (savedModuleName == null) {
 368 
 369                     savedModuleName = descriptor.name();
 370                     savedOsName = osName;
 371                     savedOsArch = osArch;
 372                     savedOsVersion = osVersion;
 373 
 374                 } else {
 375 
 376                     boolean matches = platformMatches(osName, savedOsName)
 377                             && platformMatches(osArch, savedOsArch)
 378                             && platformMatches(osVersion, savedOsVersion);
 379 
 380                     if (!matches) {
 381                         String s1 = platformAsString(savedOsName,
 382                                                      savedOsArch,
 383                                                      savedOsVersion);
 384 
 385                         String s2 = platformAsString(osName, osArch, osVersion);
 386                         fail("Mismatching constraints on target platform: "
 387                                 + savedModuleName + ": " + s1
 388                                 + ", " + descriptor.name() + ": " + s2);
 389                     }
 390 
 391                 }
 392 
 393             }
 394         }
 395 
 396     }
 397 
 398     /**
 399      * Returns true if the s1 and s2 are equal or one of them is null.
 400      */
 401     private boolean platformMatches(String s1, String s2) {
 402         if (s1 == null || s2 == null)
 403             return true;
 404         else
 405             return Objects.equals(s1, s2);
 406     }
 407 
 408     /**
 409      * Return a string that encodes the OS name/arch/version.
 410      */
 411     private String platformAsString(String osName,
 412                                     String osArch,
 413                                     String osVersion) {
 414 
 415         return new StringJoiner("-")
 416                 .add(Objects.toString(osName, "*"))
 417                 .add(Objects.toString(osArch, "*"))
 418                 .add(Objects.toString(osVersion, "*"))
 419                 .toString();
 420 
 421     }
 422 
 423 
 424     /**
 425      * Checks the hashes in the module descriptor to ensure that they match
 426      * the hash of the dependency's module reference.
 427      */
 428     private void checkHashes() {
 429 
 430         for (ModuleReference mref : nameToReference.values()) {
 431             ModuleDescriptor descriptor = mref.descriptor();
 432 
 433             // get map of module names to hash
 434             Optional<Hasher.DependencyHashes> ohashes = descriptor.hashes();
 435             if (!ohashes.isPresent())
 436                 continue;
 437             Hasher.DependencyHashes hashes = ohashes.get();
 438 
 439             // check dependences
 440             for (ModuleDescriptor.Requires d : descriptor.requires()) {
 441                 String dn = d.name();
 442                 String recordedHash = hashes.hashFor(dn);
 443 
 444                 if (recordedHash != null) {
 445 
 446                     ModuleReference other = nameToReference.get(dn);
 447                     if (other == null) {
 448                         other = parent.findModule(dn)
 449                                 .map(ResolvedModule::reference)
 450                                 .orElse(null);
 451                     }
 452                     if (other == null)
 453                         throw new InternalError(dn + " not found");
 454 
 455                     String actualHash = other.computeHash(hashes.algorithm());
 456                     if (actualHash == null)
 457                         fail("Unable to compute the hash of module %s", dn);
 458 
 459                     if (!recordedHash.equals(actualHash)) {
 460                         fail("Hash of %s (%s) differs to expected hash (%s)",
 461                                 dn, actualHash, recordedHash);
 462                     }
 463 
 464                 }
 465 
 466             }
 467         }
 468 
 469     }
 470 
 471 
 472     /**
 473      * Computes and sets the readability graph for the modules in the given
 474      * Resolution object.
 475      *
 476      * The readability graph is created by propagating "requires" through the
 477      * "public requires" edges of the module dependence graph. So if the module
 478      * dependence graph has m1 requires m2 && m2 requires public m3 then the
 479      * resulting readability graph will contain m1 reads m2, m1
 480      * reads m3, and m2 reads m3.
 481      *
 482      * TODO: Use a more efficient algorithm, maybe cache the requires public
 483      *       in parent configurations.
 484      */
 485     private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
 486 
 487         // the "reads" graph starts as a module dependence graph and
 488         // is iteratively updated to be the readability graph
 489         Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>();
 490 
 491         // the "requires public" graph, contains requires public edges only
 492         Map<ResolvedModule, Set<ResolvedModule>> g2 = new HashMap<>();
 493 
 494 
 495         // need "requires public" from the modules in parent configurations as
 496         // there may be selected modules that have a dependency on modules in
 497         // the parent configuration.
 498 
 499         Configuration p = parent;
 500         while (p != null) {
 501             for (ModuleDescriptor descriptor : p.descriptors()) {
 502                 ResolvedModule x = p.findModule(descriptor.name()).orElse(null);
 503                 if (x == null)
 504                     throw new InternalError();
 505                 for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 506                     if (requires.modifiers().contains(Modifier.PUBLIC)) {
 507                         String dn = requires.name();
 508                         ResolvedModule y = p.findModule(dn).orElse(null);
 509                         if (y == null)
 510                             throw new InternalError(dn + " not found");
 511                         g2.computeIfAbsent(x, k -> new HashSet<>()).add(y);
 512                     }
 513                 }
 514             }
 515 
 516             p = p.parent().orElse(null);
 517         }
 518 
 519         // populate g1 and g2 with the dependences from the selected modules
 520         for (ModuleReference mref : nameToReference.values()) {
 521             ModuleDescriptor descriptor = mref.descriptor();
 522             ResolvedModule x = new ResolvedModule(cf, mref);
 523 
 524             Set<ResolvedModule> reads = new HashSet<>();
 525             g1.put(x, reads);
 526 
 527             Set<ResolvedModule> requiresPublic = new HashSet<>();
 528             g2.put(x, requiresPublic);
 529 
 530             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
 531                 String dn = requires.name();
 532 
 533                 ResolvedModule y;
 534                 ModuleReference other = nameToReference.get(dn);
 535                 if (other != null) {
 536                     y = new ResolvedModule(cf, other);  // cache?
 537                 } else {
 538                     y = parent.findModule(dn).orElse(null);
 539                     if (y == null)
 540                         throw new InternalError("unable to find " + dn);
 541                 }
 542 
 543                 // m requires other => m reads other
 544                 reads.add(y);
 545 
 546                 // m requires public other
 547                 if (requires.modifiers().contains(Modifier.PUBLIC)) {
 548                     requiresPublic.add(y);
 549                 }
 550 
 551             }
 552 
 553             // automatic modules reads all selected modules and all modules
 554             // in parent configurations
 555             if (descriptor.isAutomatic()) {
 556                 String name = descriptor.name();
 557 
 558                 // reads all selected modules
 559                 // requires public` all selected automatic modules
 560                 for (ModuleReference mref2 : nameToReference.values()) {
 561                     ModuleDescriptor descriptor2 = mref2.descriptor();
 562                     if (!name.equals(descriptor2.name())) {
 563                         ResolvedModule m = new ResolvedModule(cf, mref2);
 564                         reads.add(m);
 565                         if (descriptor2.isAutomatic())
 566                             requiresPublic.add(m);
 567                     }
 568                 }
 569 
 570                 // reads all modules in parent configurations
 571                 // `requires public` all automatic modules in parent configurations
 572                 p = parent;
 573                 while (p != null) {
 574                     for (ResolvedModule m : p.modules()) {
 575                         reads.add(m);
 576                         if (m.reference().descriptor().isAutomatic())
 577                             requiresPublic.add(m);
 578                     }
 579                     p = p.parent().orElse(null);
 580                 }
 581 
 582             }
 583 
 584         }
 585 
 586         // Iteratively update g1 until there are no more requires public to propagate
 587         boolean changed;
 588         Map<ResolvedModule, Set<ResolvedModule>> changes = new HashMap<>();
 589         do {
 590             changed = false;
 591             for (Entry<ResolvedModule, Set<ResolvedModule>> entry : g1.entrySet()) {
 592 
 593                 ResolvedModule m1 = entry.getKey();
 594                 Set<ResolvedModule> m1Reads = entry.getValue();
 595 
 596                 for (ResolvedModule m2 : m1Reads) {
 597                     Set<ResolvedModule> m2RequiresPublic = g2.get(m2);
 598                     if (m2RequiresPublic != null) {
 599                         for (ResolvedModule m3 : m2RequiresPublic) {
 600                             if (!m1Reads.contains(m3)) {
 601 
 602                                 // computeIfAbsent
 603                                 Set<ResolvedModule> s = changes.get(m1);
 604                                 if (s == null) {
 605                                     s = new HashSet<>();
 606                                     changes.put(m1, s);
 607                                 }
 608                                 s.add(m3);
 609                                 changed = true;
 610 
 611                             }
 612                         }
 613                     }
 614                 }
 615             }
 616 
 617             if (changed) {
 618                 for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e :
 619                         changes.entrySet()) {
 620                     ResolvedModule m = e.getKey();
 621                     g1.get(m).addAll(e.getValue());
 622                 }
 623                 changes.clear();
 624             }
 625 
 626         } while (changed);
 627 
 628 
 629         return g1;
 630     }
 631 
 632 
 633     /**
 634      * Checks the readability graph to ensure that no two modules export the
 635      * same package to a module. This includes the case where module M has
 636      * a local package P and M reads another module that exports P to M.
 637      * Also checks the uses/provides of module M to ensure that it reads a
 638      * module that exports the package of the service type to M.
 639      */
 640     private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
 641 
 642         for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
 643             ModuleDescriptor descriptor1 = e.getKey().descriptor();
 644 
 645             // the map of packages that are local or exported to descriptor1
 646             Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
 647 
 648             // local packages
 649             Set<String> packages = descriptor1.packages();
 650             for (String pn : packages) {
 651                 packageToExporter.put(pn, descriptor1);
 652             }
 653 
 654             // descriptor1 reads descriptor2
 655             Set<ResolvedModule> reads = e.getValue();
 656             for (ResolvedModule endpoint : reads) {
 657                 ModuleDescriptor descriptor2 = endpoint.descriptor();
 658 
 659                 for (ModuleDescriptor.Exports export : descriptor2.exports()) {
 660 
 661                     if (export.isQualified()) {
 662                         if (!export.targets().contains(descriptor1.name()))
 663                             continue;
 664                     }
 665 
 666                     // source is exported to descriptor2
 667                     String source = export.source();
 668                     ModuleDescriptor other
 669                             = packageToExporter.put(source, descriptor2);
 670 
 671                     if (other != null && other != descriptor2) {
 672                         // package might be local to descriptor1
 673                         if (other == descriptor1) {
 674                             fail("Module %s contains package %s"
 675                                  + ", module %s exports package %s to %s",
 676                                     descriptor1.name(),
 677                                     source,
 678                                     descriptor2.name(),
 679                                     source,
 680                                     descriptor1.name());
 681                         } else {
 682                             fail("Modules %s and %s export package %s to module %s",
 683                                     descriptor2.name(),
 684                                     other.name(),
 685                                     source,
 686                                     descriptor1.name());
 687                         }
 688 
 689                     }
 690                 }
 691             }
 692 
 693             // uses S
 694             for (String service : descriptor1.uses()) {
 695                 String pn = packageName(service);
 696                 if (!packageToExporter.containsKey(pn)) {
 697                     fail("Module %s does not read a module that exports %s",
 698                             descriptor1.name(), pn);
 699                 }
 700             }
 701 
 702             // provides S
 703             for (Map.Entry<String, ModuleDescriptor.Provides> entry :
 704                     descriptor1.provides().entrySet()) {
 705                 String service = entry.getKey();
 706                 ModuleDescriptor.Provides provides = entry.getValue();
 707 
 708                 String pn = packageName(service);
 709                 if (!packageToExporter.containsKey(pn)) {
 710                     fail("Module %s does not read a module that exports %s",
 711                             descriptor1.name(), pn);
 712                 }
 713 
 714                 for (String provider : provides.providers()) {
 715                     if (!packages.contains(packageName(provider))) {
 716                         fail("Provider %s not in module %s",
 717                                 provider, descriptor1.name());
 718                     }
 719                 }
 720             }
 721 
 722         }
 723 
 724     }
 725 
 726 
 727     /**
 728      * Invokes the beforeFinder to find method to find the given module.
 729      */
 730     private ModuleReference findWithBeforeFinder(String mn) {
 731         try {
 732             return beforeFinder.find(mn).orElse(null);
 733         } catch (FindException e) {
 734             // unwrap
 735             throw new ResolutionException(e.getMessage(), e.getCause());
 736         }
 737     }
 738 
 739     /**
 740      * Invokes the afterFinder to find method to find the given module.
 741      */
 742     private ModuleReference findWithAfterFinder(String mn) {
 743         try {
 744             return afterFinder.find(mn).orElse(null);
 745         } catch (FindException e) {
 746             // unwrap
 747             throw new ResolutionException(e.getMessage(), e.getCause());
 748         }
 749     }
 750 
 751     /**
 752      * Returns the set of all modules that are observable with the before
 753      * and after ModuleFinders.
 754      */
 755     private Set<ModuleReference> findAll() {
 756         try {
 757 
 758             Set<ModuleReference> beforeModules = beforeFinder.findAll();
 759             Set<ModuleReference> afterModules = afterFinder.findAll();
 760 
 761             if (afterModules.isEmpty())
 762                 return beforeModules;
 763 
 764             if (beforeModules.isEmpty() && parent == Configuration.empty())
 765                 return afterModules;
 766 
 767             Set<ModuleReference> result = new HashSet<>(beforeModules);
 768             for (ModuleReference mref : afterModules) {
 769                 String name = mref.descriptor().name();
 770                 if (!beforeFinder.find(name).isPresent()
 771                         && !parent.findModule(name).isPresent())
 772                     result.add(mref);
 773             }
 774 
 775             return result;
 776 
 777         } catch (FindException e) {
 778             // unwrap
 779             throw new ResolutionException(e.getMessage(), e.getCause());
 780         }
 781     }
 782 
 783     /**
 784      * Returns the package name
 785      */
 786     private static String packageName(String cn) {
 787         int index = cn.lastIndexOf(".");
 788         return (index == -1) ? "" : cn.substring(0, index);
 789     }
 790 
 791     /**
 792      * Throw ResolutionException with the given format string and arguments
 793      */
 794     private static void fail(String fmt, Object ... args) {
 795         String msg = String.format(fmt, args);
 796         throw new ResolutionException(msg);
 797     }
 798 
 799 
 800     /**
 801      * Tracing support, limited to boot layer for now.
 802      */
 803 
 804     private final static boolean TRACE
 805         = Boolean.getBoolean("jdk.launcher.traceResolver")
 806             && (Layer.boot() == null);
 807 
 808     private String op;
 809 
 810     private long trace_start(String op) {
 811         this.op = op;
 812         return System.currentTimeMillis();
 813     }
 814 
 815     private void trace(String fmt, Object ... args) {
 816         if (TRACE) {
 817             System.out.print("[" + op + "] ");
 818             System.out.format(fmt, args);
 819             System.out.println();
 820         }
 821     }
 822 
 823 }