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