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