forked from kangjianwei/LearningJDK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathConfiguration.java
606 lines (540 loc) · 27.8 KB
/
Configuration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang.module;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.internal.module.ModuleReferenceImpl;
import jdk.internal.module.ModuleTarget;
/**
* A configuration that is the result of <a href="package-summary.html#resolution">
* resolution</a> or resolution with
* <a href="{@docRoot}/java.base/java/lang/module/Configuration.html#service-binding">service binding</a>.
*
* <p> A configuration encapsulates the <em>readability graph</em> that is the
* output of resolution. A readability graph is a directed graph whose vertices
* are of type {@link ResolvedModule} and the edges represent the readability
* amongst the modules. {@code Configuration} defines the {@link #modules()
* modules()} method to get the set of resolved modules in the graph. {@code
* ResolvedModule} defines the {@link ResolvedModule#reads() reads()} method to
* get the set of modules that a resolved module reads. The modules that are
* read may be in the same configuration or may be in {@link #parents() parent}
* configurations. </p>
*
* <p> Configuration defines the {@link #resolve(ModuleFinder, List, ModuleFinder, Collection)
* resolve} method to resolve a collection of root modules, and the {@link
* #resolveAndBind(ModuleFinder, List, ModuleFinder, Collection) resolveAndBind}
* method to do resolution with service binding. There are instance and
* static variants of both methods. The instance methods create a configuration
* with the receiver as the parent configuration. The static methods are for
* more advanced cases where there can be more than one parent configuration. </p>
*
* <p> Each {@link java.lang.ModuleLayer layer} of modules in the Java virtual
* machine is created from a configuration. The configuration for the {@link
* java.lang.ModuleLayer#boot() boot} layer is obtained by invoking {@code
* ModuleLayer.boot().configuration()}. The configuration for the boot layer
* will often be the parent when creating new configurations. </p>
*
* <h3> Example </h3>
*
* <p> The following example uses the {@link
* #resolve(ModuleFinder, ModuleFinder, Collection) resolve} method to resolve a
* module named <em>myapp</em> with the configuration for the boot layer as the
* parent configuration. It prints the name of each resolved module and the
* names of the modules that each module reads. </p>
*
* <pre>{@code
* ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3);
*
* Configuration parent = ModuleLayer.boot().configuration();
*
* Configuration cf = parent.resolve(finder, ModuleFinder.of(), Set.of("myapp"));
* cf.modules().forEach(m -> {
* System.out.format("%s -> %s%n",
* m.name(),
* m.reads().stream()
* .map(ResolvedModule::name)
* .collect(Collectors.joining(", ")));
* });
* }</pre>
*
* @spec JPMS
* @see java.lang.ModuleLayer
* @since 9
*/
// 模块依赖图,包含当前已解析的所有模块的信息
public final class Configuration {
// 空的模块图,通常用在空模块层中
private static final Configuration EMPTY_CONFIGURATION = new Configuration();
// 父模块图
private final List<Configuration> parents; // parent configurations, in search order
// 当前模块图以及父模块图的集合
private volatile List<Configuration> allConfigurations;
// 当前模块图中所有已解析(存在)的模块
private final Set<ResolvedModule> modules;
// key是已解析的模块,value是该模块的的依赖,可以包括可读依赖与服务依赖
private final Map<ResolvedModule, Set<ResolvedModule>> graph;
// key是已解析模块的名称,value是已解析模块的实例
private final Map<String, ResolvedModule> nameToModule;
// constraint on target platform
private final String targetPlatform;
/*▼ 构造器 ████████████████████████████████████████████████████████████████████████████████┓ */
// 构造一个空的模块依赖图
private Configuration() {
this.parents = Collections.emptyList();
this.graph = Collections.emptyMap();
this.modules = Collections.emptySet();
this.nameToModule = Collections.emptyMap();
this.targetPlatform = null;
}
// 基于当前模块依赖图来构造新的依赖图,父模块信息由parents给出
private Configuration(List<Configuration> parents, Resolver resolver) {
// 对当前模块图进行循环依赖、可读性等检查
Map<ResolvedModule, Set<ResolvedModule>> graph = resolver.finish(this);
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ResolvedModule>[] nameEntries = (Entry<String, ResolvedModule>[]) new Entry[graph.size()];
ResolvedModule[] moduleArray = new ResolvedModule[graph.size()];
int i = 0;
for(ResolvedModule resolvedModule : graph.keySet()) {
moduleArray[i] = resolvedModule;
nameEntries[i] = Map.entry(resolvedModule.name(), resolvedModule);
i++;
}
this.parents = Collections.unmodifiableList(parents);
this.graph = graph;
this.modules = Set.of(moduleArray);
this.nameToModule = Map.ofEntries(nameEntries);
this.targetPlatform = resolver.targetPlatform();
}
/**
* Creates the Configuration for the boot layer from a pre-generated readability graph.
*
* @apiNote This method is coded for startup performance.
*/
// 由指定的模块依赖集构造模块依赖图,父模块图为空集
Configuration(ModuleFinder finder, Map<String, Set<String>> map) {
int moduleCount = map.size();
// create map of name -> ResolvedModule
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<String, ResolvedModule>[] nameEntries = (Entry<String, ResolvedModule>[]) new Entry[moduleCount];
ResolvedModule[] moduleArray = new ResolvedModule[moduleCount];
String targetPlatform = null;
int i = 0;
for(String name : map.keySet()) {
ModuleReference mref = finder.find(name).orElse(null);
assert mref != null;
if(targetPlatform == null && mref instanceof ModuleReferenceImpl) {
ModuleTarget target = ((ModuleReferenceImpl) mref).moduleTarget();
if(target != null) {
targetPlatform = target.targetPlatform();
}
}
ResolvedModule resolvedModule = new ResolvedModule(this, mref);
moduleArray[i] = resolvedModule;
nameEntries[i] = Map.entry(name, resolvedModule);
i++;
}
Map<String, ResolvedModule> nameToModule = Map.ofEntries(nameEntries);
// create entries for readability graph
@SuppressWarnings(value = {"rawtypes", "unchecked"})
Entry<ResolvedModule, Set<ResolvedModule>>[] moduleEntries = (Entry<ResolvedModule, Set<ResolvedModule>>[]) new Entry[moduleCount];
i = 0;
for(ResolvedModule resolvedModule : moduleArray) {
Set<String> names = map.get(resolvedModule.name());
ResolvedModule[] readsArray = new ResolvedModule[names.size()];
int j = 0;
for(String name : names) {
readsArray[j++] = nameToModule.get(name);
}
moduleEntries[i++] = Map.entry(resolvedModule, Set.of(readsArray));
}
this.parents = List.of(empty());
this.graph = Map.ofEntries(moduleEntries);
this.modules = Set.of(moduleArray);
this.nameToModule = nameToModule;
this.targetPlatform = targetPlatform;
}
/*▲ 构造器 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ 定义模块图 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Resolves a collection of root modules, with this configuration as its
* parent, to create a new configuration. This method works exactly as
* specified by the static {@link
* #resolve(ModuleFinder, List, ModuleFinder, Collection) resolve}
* method when invoked with this configuration as the parent. In other words,
* if this configuration is {@code cf} then this method is equivalent to
* invoking:
* <pre> {@code
* Configuration.resolve(before, List.of(cf), after, roots);
* }</pre>
*
* @param before The <em>before</em> module finder to find modules
* @param after The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
* configurations
* @param roots The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving the given
* root modules
*
* @throws FindException If resolution fails for any of the observability-related reasons
* specified by the static {@code resolve} method
* @throws ResolutionException If resolution fails any of the consistency checks specified by
* the static {@code resolve} method
* @throws SecurityException If locating a module is denied by the security manager
*/
// 基于给定的待解析模块roots来构造模块依赖图(不会解析服务依赖),父模块依赖图为当前模块图
public Configuration resolve(ModuleFinder before, ModuleFinder after, Collection<String> roots) {
return resolve(before, List.of(this), after, roots);
}
/**
* Resolves a collection of root modules to create a configuration.
*
* <p> Each root module is located using the given {@code before} module
* finder. If a module is not found then it is located in the parent
* configuration as if by invoking the {@link #findModule(String)
* findModule} method on each parent in iteration order. If not found then
* the module is located using the given {@code after} module finder. The
* same search order is used to locate transitive dependences. Root modules
* or dependences that are located in a parent configuration are resolved
* no further and are not included in the resulting configuration. </p>
*
* <p> When all modules have been enumerated then a readability graph
* is computed, and in conjunction with the module exports and service use,
* checked for consistency. </p>
*
* <p> Resolution may fail with {@code FindException} for the following
* <em>observability-related</em> reasons: </p>
*
* <ul>
*
* <li><p> A root module, or a direct or transitive dependency, is not
* found. </p></li>
*
* <li><p> An error occurs when attempting to find a module.
* Possible errors include I/O errors, errors detected parsing a module
* descriptor ({@code module-info.class}) or two versions of the same
* module are found in the same directory. </p></li>
*
* </ul>
*
* <p> Resolution may fail with {@code ResolutionException} if any of the
* following consistency checks fail: </p>
*
* <ul>
*
* <li><p> A cycle is detected, say where module {@code m1} requires
* module {@code m2} and {@code m2} requires {@code m1}. </p></li>
*
* <li><p> A module reads two or more modules with the same name. This
* includes the case where a module reads another with the same name as
* itself. </p></li>
*
* <li><p> Two or more modules in the configuration export the same
* package to a module that reads both. This includes the case where a
* module {@code M} containing package {@code p} reads another module
* that exports {@code p} to {@code M}. </p></li>
*
* <li><p> A module {@code M} declares that it "{@code uses p.S}" or
* "{@code provides p.S with ...}" but package {@code p} is neither in
* module {@code M} nor exported to {@code M} by any module that
* {@code M} reads. </p></li>
*
* </ul>
*
* @param before The <em>before</em> module finder to find modules
* @param parents The list parent configurations in search order
* @param after The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
* configurations
* @param roots The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving the given
* root modules
*
* @throws FindException If resolution fails for any of observability-related reasons
* specified above
* @throws ResolutionException If resolution fails for any of the consistency checks specified
* above
* @throws IllegalArgumentException If the list of parents is empty, or the list has two or more
* parents with modules for different target operating systems,
* architectures, or versions
* @throws SecurityException If locating a module is denied by the security manager
* @implNote In the implementation then observability of modules may depend
* on referential integrity or other checks that ensure different builds of
* tightly coupled modules or modules for specific operating systems or
* architectures are not combined in the same configuration.
*/
// 基于给定的待解析模块roots来构造模块依赖图(不会解析服务依赖),父模块依赖图由parents给出
public static Configuration resolve(ModuleFinder before, List<Configuration> parents, ModuleFinder after, Collection<String> roots) {
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
List<Configuration> parentList = new ArrayList<>(parents);
if(parentList.isEmpty()) {
throw new IllegalArgumentException("'parents' is empty");
}
Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolve(roots); // 对roots中还未解析的模块描述符进行依赖解析,解析到的模块信息会存储到nameToReference中
return new Configuration(parentList, resolver);
}
/**
* Resolves a collection of root modules, with service binding, and with
* this configuration as its parent, to create a new configuration.
* This method works exactly as specified by the static {@link
* #resolveAndBind(ModuleFinder, List, ModuleFinder, Collection)
* resolveAndBind} method when invoked with this configuration
* as the parent. In other words, if this configuration is {@code cf} then
* this method is equivalent to invoking:
* <pre> {@code
* Configuration.resolveAndBind(before, List.of(cf), after, roots);
* }</pre>
*
* @param before The <em>before</em> module finder to find modules
* @param after The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
* configurations
* @param roots The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving, with service
* binding, the given root modules
*
* @throws FindException If resolution fails for any of the observability-related reasons
* specified by the static {@code resolve} method
* @throws ResolutionException If resolution fails any of the consistency checks specified by
* the static {@code resolve} method
* @throws SecurityException If locating a module is denied by the security manager
*/
// 基于给定的待解析模块roots来构造模块依赖图(需要解析服务依赖),父模块依赖图为当前模块图
public Configuration resolveAndBind(ModuleFinder before, ModuleFinder after, Collection<String> roots) {
return resolveAndBind(before, List.of(this), after, roots);
}
/**
* Resolves a collection of root modules, with service binding, to create
* configuration.
*
* <p> This method works exactly as specified by {@link
* #resolve(ModuleFinder, List, ModuleFinder, Collection)
* resolve} except that the graph of resolved modules is augmented
* with modules induced by the service-use dependence relation. </p>
*
* <p><a id="service-binding"></a>More specifically, the root modules are
* resolved as if by calling {@code resolve}. The resolved modules, and
* all modules in the parent configurations, with {@link ModuleDescriptor#uses()
* service dependences} are then examined. All modules found by the given
* module finders that {@link ModuleDescriptor#provides() provide} an
* implementation of one or more of the service types are added to the
* module graph and then resolved as if by calling the {@code
* resolve} method. Adding modules to the module graph may introduce new
* service-use dependences and so the process works iteratively until no
* more modules are added. </p>
*
* <p> As service binding involves resolution then it may fail with {@code
* FindException} or {@code ResolutionException} for exactly the same
* reasons specified in {@code resolve}. </p>
*
* @param before The <em>before</em> module finder to find modules
* @param parents The list parent configurations in search order
* @param after The <em>after</em> module finder to locate modules when not
* located by the {@code before} module finder or in parent
* configurations
* @param roots The possibly-empty collection of module names of the modules
* to resolve
*
* @return The configuration that is the result of resolving, with service
* binding, the given root modules
*
* @throws FindException If resolution fails for any of the observability-related reasons
* specified by the static {@code resolve} method
* @throws ResolutionException If resolution fails any of the consistency checks specified by
* the static {@code resolve} method
* @throws IllegalArgumentException If the list of parents is empty, or the list has two or more
* parents with modules for different target operating systems,
* architectures, or versions
* @throws SecurityException If locating a module is denied by the security manager
*/
// 基于给定的待解析模块roots来构造模块依赖图(需要解析服务依赖),父模块图由parents给出
public static Configuration resolveAndBind(ModuleFinder before, List<Configuration> parents, ModuleFinder after, Collection<String> roots) {
Objects.requireNonNull(before);
Objects.requireNonNull(after);
Objects.requireNonNull(roots);
List<Configuration> parentList = new ArrayList<>(parents);
if(parentList.isEmpty()) {
throw new IllegalArgumentException("'parents' is empty");
}
Resolver resolver = new Resolver(before, parentList, after, null);
resolver.resolve(roots) // 对roots中还未解析的模块描述符进行依赖解析,解析到的模块信息会存储到nameToReference中
.bind(); // 对已解析的模块,需要继续解析其依赖的服务,进一步完善模块依赖图
return new Configuration(parentList, resolver);
}
/**
* Resolves a collection of root modules, with service binding, and with the empty configuration as its parent.
*
* This method is used to create the configuration for the boot layer.
*/
// 基于给定的待解析模块roots来构造模块依赖图(需要解析服务依赖),父模块依赖图为空集
static Configuration resolveAndBind(ModuleFinder finder, Collection<String> roots, PrintStream traceOutput) {
List<Configuration> parentList = List.of(empty());
ModuleFinder after = ModuleFinder.of();
Resolver resolver = new Resolver(finder, parentList, after, traceOutput);
resolver.resolve(roots) // 对roots中还未解析的模块描述符进行依赖解析,解析到的模块信息会存储到nameToReference中
.bind(); // 对已解析的模块,需要继续解析其依赖的服务,进一步完善模块依赖图
return new Configuration(parentList, resolver);
}
/*▲ 定义模块图 ████████████████████████████████████████████████████████████████████████████████┛ */
/**
* Returns the <em>empty</em> configuration.
* There are no modules in the empty configuration. It has no parents.
*
* @return The empty configuration
*/
// 返回一个空模块图
public static Configuration empty() {
return EMPTY_CONFIGURATION;
}
/**
* Returns an unmodifiable list of this configuration's parents, in search
* order. If this is the {@linkplain #empty empty configuration} then an
* empty list is returned.
*
* @return A possibly-empty unmodifiable list of this parent configurations
*/
// 返回父模块图
public List<Configuration> parents() {
return parents;
}
/**
* Returns an immutable set of the resolved modules in this configuration.
*
* @return A possibly-empty unmodifiable set of the resolved modules
* in this configuration
*/
// 返回当前模块图中所有已解析(存在)的模块
public Set<ResolvedModule> modules() {
return modules;
}
/**
* Finds a resolved module in this configuration, or if not in this
* configuration, the {@linkplain #parents() parent} configurations.
* Finding a module in parent configurations is equivalent to invoking
* {@code findModule} on each parent, in search order, until the module
* is found or all parents have been searched. In a <em>tree of
* configurations</em> then this is equivalent to a depth-first search.
*
* @param name The module name of the resolved module to find
*
* @return The resolved module with the given name or an empty {@code
* Optional} if there isn't a module with this name in this
* configuration or any parent configurations
*/
// 在模块图中(包括父模块图)搜索指定名称的模块
public Optional<ResolvedModule> findModule(String name) {
Objects.requireNonNull(name);
ResolvedModule module = nameToModule.get(name);
if(module != null) {
return Optional.of(module);
}
if(!parents.isEmpty()) {
return configurations().skip(1) // skip this configuration
.map(cf -> cf.nameToModule.get(name)).filter(Objects::nonNull).findFirst();
}
return Optional.empty();
}
// 目标平台名称
String targetPlatform() {
return targetPlatform;
}
// 返回当前模块图中所有(模块的)模块描述符
Set<ModuleDescriptor> descriptors() {
if(modules.isEmpty()) {
return Collections.emptySet();
}
return modules.stream().map(ResolvedModule::reference).map(ModuleReference::descriptor).collect(Collectors.toSet());
}
// 获取指定模块依赖(requires)的模块
Set<ResolvedModule> reads(ResolvedModule module) {
return Collections.unmodifiableSet(graph.get(module));
}
/**
* Returns an ordered stream of configurations. The first element is this
* configuration, the remaining elements are the parent configurations
* in DFS order.
*
* @implNote For now, the assumption is that the number of elements will
* be very low and so this method does not use a specialized spliterator.
*/
// 返回当前模块图以及父模块图
Stream<Configuration> configurations() {
// 获取当前模块图以及父模块图的集合
List<Configuration> allConfigurations = this.allConfigurations;
if(allConfigurations != null) {
return allConfigurations.stream();
}
allConfigurations = new ArrayList<>();
Set<Configuration> visited = new HashSet<>();
Deque<Configuration> stack = new ArrayDeque<>();
visited.add(this);
stack.push(this);
while(!stack.isEmpty()) {
Configuration layer = stack.pop();
allConfigurations.add(layer);
// push in reverse order
for(int i = layer.parents.size() - 1; i >= 0; i--) {
Configuration parent = layer.parents.get(i);
if(!visited.contains(parent)) {
visited.add(parent);
stack.push(parent);
}
}
}
// 缓存
this.allConfigurations = Collections.unmodifiableList(allConfigurations);
return allConfigurations.stream();
}
/**
* Returns a string describing this configuration.
*
* @return A possibly empty string describing this configuration
*/
@Override
public String toString() {
return modules().stream().map(ResolvedModule::name).collect(Collectors.joining(", "));
}
}