-
Notifications
You must be signed in to change notification settings - Fork 659
/
ModuleFinder.java
371 lines (355 loc) · 17.3 KB
/
ModuleFinder.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
/*
* Copyright (c) 2014, 2017, 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.nio.file.Path;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import jdk.internal.module.ModulePath;
import jdk.internal.module.SystemModuleFinders;
/**
* A finder of modules. A {@code ModuleFinder} is used to find modules during
* <a href="package-summary.html#resolution">resolution</a> or
* <a href="Configuration.html#service-binding">service binding</a>.
*
* <p> A {@code ModuleFinder} can only find one module with a given name. A
* {@code ModuleFinder} that finds modules in a sequence of directories, for
* example, will locate the first occurrence of a module of a given name and
* will ignore other modules of that name that appear in directories later in
* the sequence. </p>
*
* <p> Example usage: </p>
*
* <pre>{@code
* Path dir1, dir2, dir3;
*
* ModuleFinder finder = ModuleFinder.of(dir1, dir2, dir3);
*
* Optional<ModuleReference> omref = finder.find("jdk.foo");
* omref.ifPresent(mref -> ... );
*
* }</pre>
*
* <p> The {@link #find(String) find} and {@link #findAll() findAll} methods
* defined here can fail for several reasons. These include I/O errors, errors
* detected parsing a module descriptor ({@code module-info.class}), or in the
* case of {@code ModuleFinder} returned by {@link #of ModuleFinder.of}, that
* two or more modules with the same name are found in a directory.
* When an error is detected then these methods throw {@link FindException
* FindException} with an appropriate {@link Throwable#getCause cause}.
* The behavior of a {@code ModuleFinder} after a {@code FindException} is
* thrown is undefined. For example, invoking {@code find} after an exception
* is thrown may or may not scan the same modules that lead to the exception.
* It is recommended that a module finder be discarded after an exception is
* thrown. </p>
*
* <p> A {@code ModuleFinder} is not required to be thread safe. </p>
*
* @spec JPMS
* @since 9
*/
// 模块查找器
public interface ModuleFinder {
/**
* Finds a reference to a module of a given name.
*
* <p> A {@code ModuleFinder} provides a consistent view of the
* modules that it locates. If {@code find} is invoked several times to
* locate the same module (by name) then it will return the same result
* each time. If a module is located then it is guaranteed to be a member
* of the set of modules returned by the {@link #findAll() findAll}
* method. </p>
*
* @param name The name of the module to find
*
* @return A reference to a module with the given name or an empty
* {@code Optional} if not found
*
* @throws FindException If an error occurs finding the module
* @throws SecurityException If denied by the security manager
*/
// 查找指定名称的模块,返回模块引用
Optional<ModuleReference> find(String name);
/**
* Returns the set of all module references that this finder can locate.
*
* <p> A {@code ModuleFinder} provides a consistent view of the modules
* that it locates. If {@link #findAll() findAll} is invoked several times
* then it will return the same (equals) result each time. For each {@code
* ModuleReference} element in the returned set then it is guaranteed that
* {@link #find find} will locate the {@code ModuleReference} if invoked
* to find that module. </p>
*
* @return The set of all module references that this finder locates
*
* @throws FindException If an error occurs finding all modules
* @throws SecurityException If denied by the security manager
* @apiNote This is important to have for methods such as {@link
* Configuration#resolveAndBind resolveAndBind} that need to scan the
* module path to find modules that provide a specific service.
*/
// 查找所有模块,返回其模块引用的集合
Set<ModuleReference> findAll();
/**
* Returns a module finder that locates the <em>system modules</em>. The
* system modules are the modules in the Java run-time image.
* The module finder will always find {@code java.base}.
*
* <p> If there is a security manager set then its {@link
* SecurityManager#checkPermission(Permission) checkPermission} method is
* invoked to check that the caller has been granted
* {@link RuntimePermission RuntimePermission("accessSystemModules")}
* to access the system modules. </p>
*
* @return A {@code ModuleFinder} that locates the system modules
*
* @throws SecurityException If denied by the security manager
*/
// 返回一个系统模块查找器
static ModuleFinder ofSystem() {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
sm.checkPermission(new RuntimePermission("accessSystemModules"));
PrivilegedAction<ModuleFinder> pa = SystemModuleFinders::ofSystem;
return AccessController.doPrivileged(pa);
}
return SystemModuleFinders.ofSystem();
}
/**
* Returns a module finder that locates modules on the file system by
* searching a sequence of directories and/or packaged modules.
*
* Each element in the given array is one of:
* <ol>
* <li><p> A path to a directory of modules.</p></li>
* <li><p> A path to the <em>top-level</em> directory of an
* <em>exploded module</em>. </p></li>
* <li><p> A path to a <em>packaged module</em>. </p></li>
* </ol>
*
* The module finder locates modules by searching each directory, exploded
* module, or packaged module in array index order. It finds the first
* occurrence of a module with a given name and ignores other modules of
* that name that appear later in the sequence.
*
* <p> If an element is a path to a directory of modules then each entry in
* the directory is a packaged module or the top-level directory of an
* exploded module. It is an error if a directory contains more than one
* module with the same name. If an element is a path to a directory, and
* that directory contains a file named {@code module-info.class}, then the
* directory is treated as an exploded module rather than a directory of
* modules. </p>
*
* <p id="automatic-modules"> The module finder returned by this method
* supports modules packaged as JAR files. A JAR file with a {@code
* module-info.class} in its top-level directory, or in a versioned entry
* in a {@linkplain java.util.jar.JarFile#isMultiRelease() multi-release}
* JAR file, is a modular JAR file and thus defines an <em>explicit</em>
* module. A JAR file that does not have a {@code module-info.class} in its
* top-level directory defines an <em>automatic module</em>, as follows:
* </p>
*
* <ul>
*
* <li><p> If the JAR file has the attribute "{@code Automatic-Module-Name}"
* in its main manifest then its value is the {@linkplain
* ModuleDescriptor#name() module name}. The module name is otherwise
* derived from the name of the JAR file. </p></li>
*
* <li><p> The {@link ModuleDescriptor#version() version}, and the
* module name when the attribute "{@code Automatic-Module-Name}" is not
* present, are derived from the file name of the JAR file as follows: </p>
*
* <ul>
*
* <li><p> The "{@code .jar}" suffix is removed. </p></li>
*
* <li><p> If the name matches the regular expression {@code
* "-(\\d+(\\.|$))"} then the module name will be derived from the
* subsequence preceding the hyphen of the first occurrence. The
* subsequence after the hyphen is parsed as a {@link
* ModuleDescriptor.Version Version} and ignored if it cannot be
* parsed as a {@code Version}. </p></li>
*
* <li><p> All non-alphanumeric characters ({@code [^A-Za-z0-9]})
* in the module name are replaced with a dot ({@code "."}), all
* repeating dots are replaced with one dot, and all leading and
* trailing dots are removed. </p></li>
*
* <li><p> As an example, a JAR file named "{@code foo-bar.jar}" will
* derive a module name "{@code foo.bar}" and no version. A JAR file
* named "{@code foo-bar-1.2.3-SNAPSHOT.jar}" will derive a module
* name "{@code foo.bar}" and "{@code 1.2.3-SNAPSHOT}" as the version.
* </p></li>
*
* </ul></li>
*
* <li><p> The set of packages in the module is derived from the
* non-directory entries in the JAR file that have names ending in
* "{@code .class}". A candidate package name is derived from the name
* using the characters up to, but not including, the last forward slash.
* All remaining forward slashes are replaced with dot ({@code "."}). If
* the resulting string is a legal package name then it is assumed to be
* a package name. For example, if the JAR file contains the entry
* "{@code p/q/Foo.class}" then the package name derived is
* "{@code p.q}".</p></li>
*
* <li><p> The contents of entries starting with {@code
* META-INF/services/} are assumed to be service configuration files
* (see {@link java.util.ServiceLoader}). If the name of a file
* (that follows {@code META-INF/services/}) is a legal class name
* then it is assumed to be the fully-qualified class name of a service
* type. The entries in the file are assumed to be the fully-qualified
* class names of provider classes. </p></li>
*
* <li><p> If the JAR file has a {@code Main-Class} attribute in its
* main manifest, its value is a legal class name, and its package is
* in the set of packages derived for the module, then the value is the
* module {@linkplain ModuleDescriptor#mainClass() main class}. </p></li>
*
* </ul>
*
* <p> If a {@code ModuleDescriptor} cannot be created (by means of the
* {@link ModuleDescriptor.Builder ModuleDescriptor.Builder} API) for an
* automatic module then {@code FindException} is thrown. This can arise
* when the value of the "{@code Automatic-Module-Name}" attribute is not a
* legal module name, a legal module name cannot be derived from the file
* name of the JAR file, where the JAR file contains a {@code .class} in
* the top-level directory of the JAR file, where an entry in a service
* configuration file is not a legal class name or its package name is not
* in the set of packages derived for the module. </p>
*
* <p> In addition to JAR files, an implementation may also support modules
* that are packaged in other implementation specific module formats. If
* an element in the array specified to this method is a path to a directory
* of modules then entries in the directory that not recognized as modules
* are ignored. If an element in the array is a path to a packaged module
* that is not recognized then a {@code FindException} is thrown when the
* file is encountered. Paths to files that do not exist are always ignored.
* </p>
*
* <p> As with automatic modules, the contents of a packaged or exploded
* module may need to be <em>scanned</em> in order to determine the packages
* in the module. Whether {@linkplain java.nio.file.Files#isHidden(Path)
* hidden files} are ignored or not is implementation specific and therefore
* not specified. If a {@code .class} file (other than {@code
* module-info.class}) is found in the top-level directory then it is
* assumed to be a class in the unnamed package and so {@code FindException}
* is thrown. </p>
*
* <p> Finders created by this method are lazy and do not eagerly check
* that the given file paths are directories or packaged modules.
* Consequently, the {@code find} or {@code findAll} methods will only
* fail if invoking these methods results in searching a directory or
* packaged module and an error is encountered. </p>
*
* @param entries A possibly-empty array of paths to directories of modules
* or paths to packaged or exploded modules
*
* @return A {@code ModuleFinder} that locates modules on the file system
*/
// 构造模块查找器,用于查找指定路径下的模块
static ModuleFinder of(Path... entries) {
// special case zero entries
if(entries.length == 0) {
// 空的模块查找器
return new ModuleFinder() {
@Override
public Optional<ModuleReference> find(String name) {
Objects.requireNonNull(name);
return Optional.empty();
}
@Override
public Set<ModuleReference> findAll() {
return Collections.emptySet();
}
};
}
return ModulePath.of(entries);
}
/**
* Returns a module finder that is composed from a sequence of zero or more
* module finders. The {@link #find(String) find} method of the resulting
* module finder will locate a module by invoking the {@code find} method
* of each module finder, in array index order, until either the module is
* found or all module finders have been searched. The {@link #findAll()
* findAll} method of the resulting module finder will return a set of
* modules that includes all modules located by the first module finder.
* The set of modules will include all modules located by the second or
* subsequent module finder that are not located by previous module finders
* in the sequence.
*
* <p> When locating modules then any exceptions or errors thrown by the
* {@code find} or {@code findAll} methods of the underlying module finders
* will be propagated to the caller of the resulting module finder's
* {@code find} or {@code findAll} methods. </p>
*
* @param finders The array of module finders
*
* @return A {@code ModuleFinder} that composes a sequence of module finders
*/
// 构造模块查找器,该模块查找器由参数中指定的一系列模块查找器组成
static ModuleFinder compose(ModuleFinder... finders) {
// copy the list and check for nulls
final List<ModuleFinder> finderList = List.of(finders);
return new ModuleFinder() {
private final Map<String, ModuleReference> nameToModule = new HashMap<>();
private Set<ModuleReference> allModules;
@Override
public Optional<ModuleReference> find(String name) {
// cached?
ModuleReference mref = nameToModule.get(name);
if(mref != null)
return Optional.of(mref);
Optional<ModuleReference> omref = finderList.stream().map(f -> f.find(name)).flatMap(Optional::stream).findFirst();
omref.ifPresent(m -> nameToModule.put(name, m));
return omref;
}
@Override
public Set<ModuleReference> findAll() {
if(allModules != null)
return allModules;
// seed with modules already found
Set<ModuleReference> result = new HashSet<>(nameToModule.values());
finderList.stream().flatMap(f -> f.findAll().stream()).forEach(mref -> {
String name = mref.descriptor().name();
if(nameToModule.putIfAbsent(name, mref) == null) {
result.add(mref);
}
});
allModules = Collections.unmodifiableSet(result);
return allModules;
}
};
}
}