1 /* 2 * Copyright (c) 2014, 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.util.ArrayDeque; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.Deque; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.Objects; 40 import java.util.Optional; 41 import java.util.Set; 42 import java.util.stream.Collectors; 43 import java.util.stream.Stream; 44 45 import jdk.internal.module.ModuleReferenceImpl; 46 import jdk.internal.module.ModuleTarget; 47 48 /** 49 * A configuration that is the result of <a href="package-summary.html#resolution"> 50 * resolution</a> or resolution with 51 * <a href="{@docRoot}/java/lang/module/Configuration.html#service-binding">service binding</a>. 52 * 53 * <p> A configuration encapsulates the <em>readability graph</em> that is the 54 * output of resolution. A readability graph is a directed graph whose vertices 55 * are of type {@link ResolvedModule} and the edges represent the readability 56 * amongst the modules. {@code Configuration} defines the {@link #modules() 57 * modules()} method to get the set of resolved modules in the graph. {@code 58 * ResolvedModule} defines the {@link ResolvedModule#reads() reads()} method to 59 * get the set of modules that a resolved module reads. The modules that are 60 * read may be in the same configuration or may be in {@link #parents() parent} 61 * configurations. </p> 62 * 63 * <p> Configuration defines the {@link #resolve(ModuleFinder,List,ModuleFinder,Collection) 64 * resolve} method to resolve a collection of root modules, and the {@link 65 * #resolveAndBind(ModuleFinder,List,ModuleFinder,Collection) resolveAndBind} 66 * method to do resolution with service binding. There are instance and 67 * static variants of both methods. The instance methods create a configuration 68 * with the receiver as the parent configuration. The static methods are for 69 * more advanced cases where there can be more than one parent configuration. </p> 70 * 71 * <p> Each {@link java.lang.ModuleLayer layer} of modules in the Java virtual 72 * machine is created from a configuration. The configuration for the {@link 73 * java.lang.ModuleLayer#boot() boot} layer is obtained by invoking {@code 74 * ModuleLayer.boot().configuration()}. The configuration for the boot layer 75 * will often be the parent when creating new configurations. </p> 76 * 77 * <h3> Example </h3> 78 * 79 * <p> The following example uses the {@link 80 * #resolve(ModuleFinder,ModuleFinder,Collection) resolve} method to resolve a 81 * module named <em>myapp</em> with the configuration for the boot layer as the 82 * parent configuration. It prints the name of each resolved module and the 83 * names of the modules that each module reads. </p> 84 * 85 * <pre>{@code 86 * ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3); 87 * 88 * Configuration parent = ModuleLayer.boot().configuration(); 89 * 90 * Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("myapp")); 91 * cf.modules().forEach(m -> { 92 * System.out.format("%s -> %s%n", 93 * m.name(), 94 * m.reads().stream() 95 * .map(ResolvedModule::name) 96 * .collect(Collectors.joining(", "))); 97 * }); 98 * }</pre> 99 * 100 * @since 9 101 * @spec JPMS 102 * @see java.lang.ModuleLayer 103 */ 104 public final class Configuration { 105 106 // @see Configuration#empty() 107 private static final Configuration EMPTY_CONFIGURATION = new Configuration(); 108 109 // parent configurations, in search order 110 private final List<Configuration> parents; 111 112 private final Map<ResolvedModule, Set<ResolvedModule>> graph; 113 private final Set<ResolvedModule> modules; 114 private final Map<String, ResolvedModule> nameToModule; 115 116 // constraint on target platform 117 private final String targetPlatform; 118 119 String targetPlatform() { return targetPlatform; } 120 121 private Configuration() { 122 this.parents = Collections.emptyList(); 123 this.graph = Collections.emptyMap(); 124 this.modules = Collections.emptySet(); 125 this.nameToModule = Collections.emptyMap(); 126 this.targetPlatform = null; 127 } 128 129 private Configuration(List<Configuration> parents, Resolver resolver) { 130 Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this); 131 132 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 133 Entry<String, ResolvedModule>[] nameEntries 134 = (Entry<String, ResolvedModule>[])new Entry[g.size()]; 135 ResolvedModule[] moduleArray = new ResolvedModule[g.size()]; 136 int i = 0; 137 for (ResolvedModule resolvedModule : g.keySet()) { 138 moduleArray[i] = resolvedModule; 139 nameEntries[i] = Map.entry(resolvedModule.name(), resolvedModule); 140 i++; 141 } 142 143 this.parents = Collections.unmodifiableList(parents); 144 this.graph = g; 145 this.modules = Set.of(moduleArray); 146 this.nameToModule = Map.ofEntries(nameEntries); 147 148 this.targetPlatform = resolver.targetPlatform(); 149 } 150 151 /** 152 * Creates the Configuration for the boot layer from a pre-generated 153 * readability graph. 154 * 155 * @apiNote This method is coded for startup performance. 156 */ 157 Configuration(ModuleFinder finder, Map<String, Set<String>> map) { 158 int moduleCount = map.size(); 159 160 // create map of name -> ResolvedModule 161 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 162 Entry<String, ResolvedModule>[] nameEntries 163 = (Entry<String, ResolvedModule>[])new Entry[moduleCount]; 164 ResolvedModule[] moduleArray = new ResolvedModule[moduleCount]; 165 String targetPlatform = null; 166 int i = 0; 167 for (String name : map.keySet()) { 168 ModuleReference mref = finder.find(name).orElse(null); 169 assert mref != null; 170 171 if (targetPlatform == null && mref instanceof ModuleReferenceImpl) { 172 ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget(); 173 if (target != null) { 174 targetPlatform = target.targetPlatform(); 175 } 176 } 177 178 ResolvedModule resolvedModule = new ResolvedModule(this, mref); 179 moduleArray[i] = resolvedModule; 180 nameEntries[i] = Map.entry(name, resolvedModule); 181 i++; 182 } 183 Map<String, ResolvedModule> nameToModule = Map.ofEntries(nameEntries); 184 185 // create entries for readability graph 186 @SuppressWarnings(value = {"rawtypes", "unchecked"}) 187 Entry<ResolvedModule, Set<ResolvedModule>>[] moduleEntries 188 = (Entry<ResolvedModule, Set<ResolvedModule>>[])new Entry[moduleCount]; 189 i = 0; 190 for (ResolvedModule resolvedModule : moduleArray) { 191 Set<String> names = map.get(resolvedModule.name()); 192 ResolvedModule[] readsArray = new ResolvedModule[names.size()]; 193 int j = 0; 194 for (String name : names) { 195 readsArray[j++] = nameToModule.get(name); 196 } 197 moduleEntries[i++] = Map.entry(resolvedModule, Set.of(readsArray)); 198 } 199 200 this.parents = List.of(empty()); 201 this.graph = Map.ofEntries(moduleEntries); 202 this.modules = Set.of(moduleArray); 203 this.nameToModule = nameToModule; 204 this.targetPlatform = targetPlatform; 205 } 206 207 /** 208 * Resolves a collection of root modules, with this configuration as its 209 * parent, to create a new configuration. This method works exactly as 210 * specified by the static {@link 211 * #resolve(ModuleFinder,List,ModuleFinder,Collection) resolve} 212 * method when invoked with this configuration as the parent. In other words, 213 * if this configuration is {@code cf} then this method is equivalent to 214 * invoking: 215 * <pre> {@code 216 * Configuration.resolve(before, List.of(cf), after, roots); 217 * }</pre> 218 * 219 * @param before 220 * The <em>before</em> module finder to find modules 221 * @param after 222 * The <em>after</em> module finder to locate modules when not 223 * located by the {@code before} module finder or in parent 224 * configurations 225 * @param roots 226 * The possibly-empty collection of module names of the modules 227 * to resolve 228 * 229 * @return The configuration that is the result of resolving the given 230 * root modules 231 * 232 * @throws FindException 233 * If resolution fails for any of the observability-related reasons 234 * specified by the static {@code resolve} method 235 * @throws ResolutionException 236 * If resolution fails any of the consistency checks specified by 237 * the static {@code resolve} method 238 * @throws SecurityException 239 * If locating a module is denied by the security manager 240 */ 241 public Configuration resolve(ModuleFinder before, 242 ModuleFinder after, 243 Collection<String> roots) 244 { 245 return resolve(before, List.of(this), after, roots); 246 } 247 248 249 /** 250 * Resolves a collection of root modules, with service binding, and with 251 * this configuration as its parent, to create a new configuration. 252 * This method works exactly as specified by the static {@link 253 * #resolveAndBind(ModuleFinder,List,ModuleFinder,Collection) 254 * resolveAndBind} method when invoked with this configuration 255 * as the parent. In other words, if this configuration is {@code cf} then 256 * this method is equivalent to invoking: 257 * <pre> {@code 258 * Configuration.resolveAndBind(before, List.of(cf), after, roots); 259 * }</pre> 260 * 261 * 262 * @param before 263 * The <em>before</em> module finder to find modules 264 * @param after 265 * The <em>after</em> module finder to locate modules when not 266 * located by the {@code before} module finder or in parent 267 * configurations 268 * @param roots 269 * The possibly-empty collection of module names of the modules 270 * to resolve 271 * 272 * @return The configuration that is the result of resolving, with service 273 * binding, the given root modules 274 * 275 * @throws FindException 276 * If resolution fails for any of the observability-related reasons 277 * specified by the static {@code resolve} method 278 * @throws ResolutionException 279 * If resolution fails any of the consistency checks specified by 280 * the static {@code resolve} method 281 * @throws SecurityException 282 * If locating a module is denied by the security manager 283 */ 284 public Configuration resolveAndBind(ModuleFinder before, 285 ModuleFinder after, 286 Collection<String> roots) 287 { 288 return resolveAndBind(before, List.of(this), after, roots); 289 } 290 291 292 /** 293 * Resolves a collection of root modules, with service binding, and with 294 * the empty configuration as its parent. 295 * 296 * This method is used to create the configuration for the boot layer. 297 */ 298 static Configuration resolveAndBind(ModuleFinder finder, 299 Collection<String> roots, 300 PrintStream traceOutput) 301 { 302 List<Configuration> parents = List.of(empty()); 303 Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput); 304 resolver.resolve(roots).bind(); 305 return new Configuration(parents, resolver); 306 } 307 308 /** 309 * Resolves a collection of root modules to create a configuration. 310 * 311 * <p> Each root module is located using the given {@code before} module 312 * finder. If a module is not found then it is located in the parent 313 * configuration as if by invoking the {@link #findModule(String) 314 * findModule} method on each parent in iteration order. If not found then 315 * the module is located using the given {@code after} module finder. The 316 * same search order is used to locate transitive dependences. Root modules 317 * or dependences that are located in a parent configuration are resolved 318 * no further and are not included in the resulting configuration. </p> 319 * 320 * <p> When all modules have been enumerated then a readability graph 321 * is computed, and in conjunction with the module exports and service use, 322 * checked for consistency. </p> 323 * 324 * <p> Resolution may fail with {@code FindException} for the following 325 * <em>observability-related</em> reasons: </p> 326 * 327 * <ul> 328 * 329 * <li><p> A root module, or a direct or transitive dependency, is not 330 * found. </p></li> 331 * 332 * <li><p> An error occurs when attempting to find a module. 333 * Possible errors include I/O errors, errors detected parsing a module 334 * descriptor ({@code module-info.class}) or two versions of the same 335 * module are found in the same directory. </p></li> 336 * 337 * </ul> 338 * 339 * <p> Resolution may fail with {@code ResolutionException} if any of the 340 * following consistency checks fail: </p> 341 * 342 * <ul> 343 * 344 * <li><p> A cycle is detected, say where module {@code m1} requires 345 * module {@code m2} and {@code m2} requires {@code m1}. </p></li> 346 * 347 * <li><p> A module reads two or more modules with the same name. This 348 * includes the case where a module reads another with the same name as 349 * itself. </p></li> 350 * 351 * <li><p> Two or more modules in the configuration export the same 352 * package to a module that reads both. This includes the case where a 353 * module {@code M} containing package {@code p} reads another module 354 * that exports {@code p} to {@code M}. </p></li> 355 * 356 * <li><p> A module {@code M} declares that it "{@code uses p.S}" or 357 * "{@code provides p.S with ...}" but package {@code p} is neither in 358 * module {@code M} nor exported to {@code M} by any module that 359 * {@code M} reads. </p></li> 360 * 361 * </ul> 362 * 363 * @implNote In the implementation then observability of modules may depend 364 * on referential integrity or other checks that ensure different builds of 365 * tightly coupled modules or modules for specific operating systems or 366 * architectures are not combined in the same configuration. 367 * 368 * @param before 369 * The <em>before</em> module finder to find modules 370 * @param parents 371 * The list parent configurations in search order 372 * @param after 373 * The <em>after</em> module finder to locate modules when not 374 * located by the {@code before} module finder or in parent 375 * configurations 376 * @param roots 377 * The possibly-empty collection of module names of the modules 378 * to resolve 379 * 380 * @return The configuration that is the result of resolving the given 381 * root modules 382 * 383 * @throws FindException 384 * If resolution fails for any of observability-related reasons 385 * specified above 386 * @throws ResolutionException 387 * If resolution fails for any of the consistency checks specified 388 * above 389 * @throws IllegalArgumentException 390 * If the list of parents is empty, or the list has two or more 391 * parents with modules for different target operating systems, 392 * architectures, or versions 393 * 394 * @throws SecurityException 395 * If locating a module is denied by the security manager 396 */ 397 public static Configuration resolve(ModuleFinder before, 398 List<Configuration> parents, 399 ModuleFinder after, 400 Collection<String> roots) 401 { 402 Objects.requireNonNull(before); 403 Objects.requireNonNull(after); 404 Objects.requireNonNull(roots); 405 406 List<Configuration> parentList = new ArrayList<>(parents); 407 if (parentList.isEmpty()) 408 throw new IllegalArgumentException("'parents' is empty"); 409 410 Resolver resolver = new Resolver(before, parentList, after, null); 411 resolver.resolve(roots); 412 413 return new Configuration(parentList, resolver); 414 } 415 416 /** 417 * Resolves a collection of root modules, with service binding, to create 418 * configuration. 419 * 420 * <p> This method works exactly as specified by {@link 421 * #resolve(ModuleFinder,List,ModuleFinder,Collection) 422 * resolve} except that the graph of resolved modules is augmented 423 * with modules induced by the service-use dependence relation. </p> 424 * 425 * <p><a id="service-binding"></a>More specifically, the root modules are 426 * resolved as if by calling {@code resolve}. The resolved modules, and 427 * all modules in the parent configurations, with {@link ModuleDescriptor#uses() 428 * service dependences} are then examined. All modules found by the given 429 * module finders that {@link ModuleDescriptor#provides() provide} an 430 * implementation of one or more of the service types are added to the 431 * module graph and then resolved as if by calling the {@code 432 * resolve} method. Adding modules to the module graph may introduce new 433 * service-use dependences and so the process works iteratively until no 434 * more modules are added. </p> 435 * 436 * <p> As service binding involves resolution then it may fail with {@code 437 * FindException} or {@code ResolutionException} for exactly the same 438 * reasons specified in {@code resolve}. </p> 439 * 440 * @param before 441 * The <em>before</em> module finder to find modules 442 * @param parents 443 * The list parent configurations in search order 444 * @param after 445 * The <em>after</em> module finder to locate modules when not 446 * located by the {@code before} module finder or in parent 447 * configurations 448 * @param roots 449 * The possibly-empty collection of module names of the modules 450 * to resolve 451 * 452 * @return The configuration that is the result of resolving, with service 453 * binding, the given root modules 454 * 455 * @throws FindException 456 * If resolution fails for any of the observability-related reasons 457 * specified by the static {@code resolve} method 458 * @throws ResolutionException 459 * If resolution fails any of the consistency checks specified by 460 * the static {@code resolve} method 461 * @throws IllegalArgumentException 462 * If the list of parents is empty, or the list has two or more 463 * parents with modules for different target operating systems, 464 * architectures, or versions 465 * @throws SecurityException 466 * If locating a module is denied by the security manager 467 */ 468 public static Configuration resolveAndBind(ModuleFinder before, 469 List<Configuration> parents, 470 ModuleFinder after, 471 Collection<String> roots) 472 { 473 Objects.requireNonNull(before); 474 Objects.requireNonNull(after); 475 Objects.requireNonNull(roots); 476 477 List<Configuration> parentList = new ArrayList<>(parents); 478 if (parentList.isEmpty()) 479 throw new IllegalArgumentException("'parents' is empty"); 480 481 Resolver resolver = new Resolver(before, parentList, after, null); 482 resolver.resolve(roots).bind(); 483 484 return new Configuration(parentList, resolver); 485 } 486 487 488 /** 489 * Returns the <em>empty</em> configuration. There are no modules in the 490 * empty configuration. It has no parents. 491 * 492 * @return The empty configuration 493 */ 494 public static Configuration empty() { 495 return EMPTY_CONFIGURATION; 496 } 497 498 499 /** 500 * Returns an unmodifiable list of this configuration's parents, in search 501 * order. If this is the {@linkplain #empty empty configuration} then an 502 * empty list is returned. 503 * 504 * @return A possibly-empty unmodifiable list of this parent configurations 505 */ 506 public List<Configuration> parents() { 507 return parents; 508 } 509 510 511 /** 512 * Returns an immutable set of the resolved modules in this configuration. 513 * 514 * @return A possibly-empty unmodifiable set of the resolved modules 515 * in this configuration 516 */ 517 public Set<ResolvedModule> modules() { 518 return modules; 519 } 520 521 522 /** 523 * Finds a resolved module in this configuration, or if not in this 524 * configuration, the {@linkplain #parents() parent} configurations. 525 * Finding a module in parent configurations is equivalent to invoking 526 * {@code findModule} on each parent, in search order, until the module 527 * is found or all parents have been searched. In a <em>tree of 528 * configurations</em> then this is equivalent to a depth-first search. 529 * 530 * @param name 531 * The module name of the resolved module to find 532 * 533 * @return The resolved module with the given name or an empty {@code 534 * Optional} if there isn't a module with this name in this 535 * configuration or any parent configurations 536 */ 537 public Optional<ResolvedModule> findModule(String name) { 538 Objects.requireNonNull(name); 539 ResolvedModule m = nameToModule.get(name); 540 if (m != null) 541 return Optional.of(m); 542 543 if (!parents.isEmpty()) { 544 return configurations() 545 .skip(1) // skip this configuration 546 .map(cf -> cf.nameToModule.get(name)) 547 .filter(Objects::nonNull) 548 .findFirst(); 549 } 550 551 return Optional.empty(); 552 } 553 554 555 Set<ModuleDescriptor> descriptors() { 556 if (modules.isEmpty()) { 557 return Collections.emptySet(); 558 } else { 559 return modules.stream() 560 .map(ResolvedModule::reference) 561 .map(ModuleReference::descriptor) 562 .collect(Collectors.toSet()); 563 } 564 } 565 566 Set<ResolvedModule> reads(ResolvedModule m) { 567 return Collections.unmodifiableSet(graph.get(m)); 568 } 569 570 /** 571 * Returns an ordered stream of configurations. The first element is this 572 * configuration, the remaining elements are the parent configurations 573 * in DFS order. 574 * 575 * @implNote For now, the assumption is that the number of elements will 576 * be very low and so this method does not use a specialized spliterator. 577 */ 578 Stream<Configuration> configurations() { 579 List<Configuration> allConfigurations = this.allConfigurations; 580 if (allConfigurations == null) { 581 allConfigurations = new ArrayList<>(); 582 Set<Configuration> visited = new HashSet<>(); 583 Deque<Configuration> stack = new ArrayDeque<>(); 584 visited.add(this); 585 stack.push(this); 586 while (!stack.isEmpty()) { 587 Configuration layer = stack.pop(); 588 allConfigurations.add(layer); 589 590 // push in reverse order 591 for (int i = layer.parents.size() - 1; i >= 0; i--) { 592 Configuration parent = layer.parents.get(i); 593 if (!visited.contains(parent)) { 594 visited.add(parent); 595 stack.push(parent); 596 } 597 } 598 } 599 this.allConfigurations = Collections.unmodifiableList(allConfigurations); 600 } 601 return allConfigurations.stream(); 602 } 603 604 private volatile List<Configuration> allConfigurations; 605 606 607 /** 608 * Returns a string describing this configuration. 609 * 610 * @return A possibly empty string describing this configuration 611 */ 612 @Override 613 public String toString() { 614 return modules().stream() 615 .map(ResolvedModule::name) 616 .collect(Collectors.joining(", ")); 617 } 618 }