1 package org.codehaus.plexus.languages.java.jpms;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.LinkedHashMap;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.Set;
37
38 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides;
39
40
41
42
43
44
45
46 @Named
47 @Singleton
48 public class LocationManager {
49 private SourceModuleInfoParser sourceParser;
50
51 private ManifestModuleNameExtractor manifestModuleNameExtractor;
52
53 public LocationManager() {
54 this.sourceParser = new SourceModuleInfoParser();
55 this.manifestModuleNameExtractor = new ManifestModuleNameExtractor();
56 }
57
58 LocationManager(SourceModuleInfoParser sourceParser) {
59 this.sourceParser = sourceParser;
60 this.manifestModuleNameExtractor = new ManifestModuleNameExtractor();
61 }
62
63
64
65
66
67
68 public ResolvePathResult parseModuleDescriptor(Path descriptorPath) throws IOException {
69 JavaModuleDescriptor moduleDescriptor;
70 if (descriptorPath.endsWith("module-info.java")) {
71 moduleDescriptor = sourceParser.fromSourcePath(descriptorPath);
72 } else {
73 throw new IOException("Invalid path to module descriptor: " + descriptorPath);
74 }
75 return new ResolvePathResult()
76 .setModuleDescriptor(moduleDescriptor)
77 .setModuleNameSource(ModuleNameSource.MODULEDESCRIPTOR);
78 }
79
80
81
82
83
84
85 public ResolvePathResult parseModuleDescriptor(File descriptorPath) throws IOException {
86 return parseModuleDescriptor(descriptorPath.toPath());
87 }
88
89
90
91
92
93
94 public ResolvePathResult parseModuleDescriptor(String descriptorPath) throws IOException {
95 return parseModuleDescriptor(Paths.get(descriptorPath));
96 }
97
98
99
100
101
102
103
104
105 public <T> ResolvePathResult resolvePath(final ResolvePathRequest<T> request) throws IOException {
106 ModuleNameExtractor filenameExtractor = new ModuleNameExtractor() {
107 MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor(request.getJdkHome());
108
109 @Override
110 public String extract(Path file) throws IOException {
111 if (request.getJdkHome() != null) {
112 return extractor
113 .extract(Collections.singletonMap(file, file))
114 .get(file);
115 } else {
116 return CmdModuleNameExtractor.getModuleName(file);
117 }
118 }
119 };
120
121 return resolvePath(
122 request.toPath(request.getPathElement()),
123 filenameExtractor,
124 getBinaryModuleInfoParser(request.getJdkHome()));
125 }
126
127
128
129
130
131
132
133
134
135 public <T> ResolvePathsResult<T> resolvePaths(final ResolvePathsRequest<T> request) throws IOException {
136 final ResolvePathsResult<T> result = request.createResult();
137
138 Map<T, JavaModuleDescriptor> pathElements =
139 new LinkedHashMap<>(request.getPathElements().size());
140
141 final ModuleInfoParser binaryParser = getBinaryModuleInfoParser(request.getJdkHome());
142
143 JavaModuleDescriptor mainModuleDescriptor = getMainModuleDescriptor(request, binaryParser);
144
145 result.setMainModuleDescriptor(mainModuleDescriptor);
146
147
148 Map<String, Set<String>> availableProviders = new HashMap<>();
149
150 if (mainModuleDescriptor != null && request.isIncludeAllProviders()) {
151 collectProviders(mainModuleDescriptor, availableProviders);
152 }
153
154 Map<String, JavaModuleDescriptor> availableNamedModules = new HashMap<>();
155
156 Map<String, ModuleNameSource> moduleNameSources = new HashMap<>();
157
158 final Map<T, Path> filenameAutoModules = new HashMap<>();
159
160
161 for (final T t : request.getPathElements()) {
162 JavaModuleDescriptor moduleDescriptor;
163 ModuleNameSource source;
164
165 ModuleNameExtractor nameExtractor = new ModuleNameExtractor() {
166 @Override
167 public String extract(Path path) throws IOException {
168 if (request.getJdkHome() != null) {
169 filenameAutoModules.put(t, path);
170 } else {
171 return CmdModuleNameExtractor.getModuleName(path);
172 }
173 return null;
174 }
175 };
176
177 try {
178 ResolvePathResult resolvedPath = resolvePath(request.toPath(t), nameExtractor, binaryParser);
179
180 moduleDescriptor = resolvedPath.getModuleDescriptor();
181
182 source = resolvedPath.getModuleNameSource();
183 } catch (Exception e) {
184 result.getPathExceptions().put(t, e);
185
186 pathElements.put(t, null);
187
188 continue;
189 }
190
191
192 if (moduleDescriptor != null && moduleNameSources.putIfAbsent(moduleDescriptor.name(), source) == null) {
193 availableNamedModules.put(moduleDescriptor.name(), moduleDescriptor);
194
195 if (request.isIncludeAllProviders()) {
196 collectProviders(moduleDescriptor, availableProviders);
197 }
198 }
199
200 pathElements.put(t, moduleDescriptor);
201 }
202 result.setPathElements(pathElements);
203
204 if (!filenameAutoModules.isEmpty()) {
205 MainClassModuleNameExtractor extractor = new MainClassModuleNameExtractor(request.getJdkHome());
206
207 Map<T, String> automodules = extractor.extract(filenameAutoModules);
208
209 for (Map.Entry<T, String> entry : automodules.entrySet()) {
210 String moduleName = entry.getValue();
211
212 if (moduleName != null) {
213 JavaModuleDescriptor moduleDescriptor =
214 JavaModuleDescriptor.newAutomaticModule(moduleName).build();
215
216 moduleNameSources.put(moduleDescriptor.name(), ModuleNameSource.FILENAME);
217
218 availableNamedModules.put(moduleDescriptor.name(), moduleDescriptor);
219
220 pathElements.put(entry.getKey(), moduleDescriptor);
221 }
222 }
223 }
224
225 Set<String> requiredNamedModules = new HashSet<>();
226
227 if (mainModuleDescriptor != null) {
228 requiredNamedModules.add(mainModuleDescriptor.name());
229
230 selectRequires(
231 mainModuleDescriptor,
232 Collections.unmodifiableMap(availableNamedModules),
233 Collections.unmodifiableMap(availableProviders),
234 requiredNamedModules,
235 true,
236 true,
237 request.isIncludeStatic());
238 }
239
240 for (String additionalModule : request.getAdditionalModules()) {
241 selectModule(
242 additionalModule,
243 Collections.unmodifiableMap(availableNamedModules),
244 Collections.unmodifiableMap(availableProviders),
245 requiredNamedModules,
246 true,
247 true,
248 request.isIncludeStatic());
249 }
250
251 Set<String> collectedModules = new HashSet<>(requiredNamedModules.size());
252
253 for (Entry<T, JavaModuleDescriptor> entry : pathElements.entrySet()) {
254 if (entry.getValue() != null
255 && requiredNamedModules.contains(entry.getValue().name())) {
256
257
258
259
260 if (collectedModules.add(entry.getValue().name())) {
261 result.getModulepathElements()
262 .put(
263 entry.getKey(),
264 moduleNameSources.get(entry.getValue().name()));
265 } else {
266 result.getPathExceptions()
267 .put(
268 entry.getKey(),
269 new IllegalStateException(
270 "Module '" + entry.getValue().name() + "' is already on the module path!"));
271 }
272 } else {
273 result.getClasspathElements().add(entry.getKey());
274 }
275 }
276
277 return result;
278 }
279
280
281
282
283
284
285
286
287 ModuleInfoParser getBinaryModuleInfoParser(final Path jdkHome) {
288 final ModuleInfoParser binaryParser;
289 if (jdkHome == null) {
290 binaryParser = new BinaryModuleInfoParser();
291 } else {
292 binaryParser = new AsmModuleInfoParser();
293 }
294 return binaryParser;
295 }
296
297 private <T> JavaModuleDescriptor getMainModuleDescriptor(
298 final ResolvePathsRequest<T> request, ModuleInfoParser binaryParser) throws IOException {
299 JavaModuleDescriptor mainModuleDescriptor;
300
301 Path descriptorPath = request.getMainModuleDescriptor();
302
303 if (descriptorPath != null) {
304 if (descriptorPath.endsWith("module-info.java")) {
305 mainModuleDescriptor = sourceParser.fromSourcePath(descriptorPath);
306 } else if (descriptorPath.endsWith("module-info.class")) {
307 mainModuleDescriptor = binaryParser.getModuleDescriptor(descriptorPath.getParent());
308 } else {
309 throw new IOException("Invalid path to module descriptor: " + descriptorPath);
310 }
311 } else {
312 mainModuleDescriptor = request.getModuleDescriptor();
313 }
314 return mainModuleDescriptor;
315 }
316
317 private ResolvePathResult resolvePath(
318 Path path, ModuleNameExtractor fileModulenameExtractor, ModuleInfoParser binaryParser) throws IOException {
319 ResolvePathResult result = new ResolvePathResult();
320
321 JavaModuleDescriptor moduleDescriptor = null;
322
323
324 if (Files.isRegularFile(path) && !path.getFileName().toString().endsWith(".jar")) {
325 throw new IllegalArgumentException(
326 "'" + path.toString() + "' not allowed on the path, only outputDirectories and jars are accepted");
327 }
328
329 if (Files.isRegularFile(path) || Files.exists(path.resolve("module-info.class"))) {
330 moduleDescriptor = binaryParser.getModuleDescriptor(path);
331 }
332
333 if (moduleDescriptor != null) {
334 result.setModuleNameSource(ModuleNameSource.MODULEDESCRIPTOR);
335 } else {
336 String moduleName = manifestModuleNameExtractor.extract(path);
337
338 if (moduleName != null) {
339 result.setModuleNameSource(ModuleNameSource.MANIFEST);
340 } else {
341 moduleName = fileModulenameExtractor.extract(path);
342
343 if (moduleName != null) {
344 result.setModuleNameSource(ModuleNameSource.FILENAME);
345 }
346 }
347
348 if (moduleName != null) {
349 moduleDescriptor =
350 JavaModuleDescriptor.newAutomaticModule(moduleName).build();
351 }
352 }
353 result.setModuleDescriptor(moduleDescriptor);
354
355 return result;
356 }
357
358 private void selectRequires(
359 JavaModuleDescriptor module,
360 Map<String, JavaModuleDescriptor> availableModules,
361 Map<String, Set<String>> availableProviders,
362 Set<String> namedModules,
363 boolean isRootModule,
364 boolean includeAsTransitive,
365 boolean includeStatic) {
366 for (JavaModuleDescriptor.JavaRequires requires : module.requires()) {
367
368 if (isRootModule
369 || includeStatic
370 || includeAsTransitive
371 || !requires.modifiers().contains(JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC)
372 || requires.modifiers().contains(JavaModuleDescriptor.JavaRequires.JavaModifier.TRANSITIVE)) {
373 selectModule(
374 requires.name(),
375 availableModules,
376 availableProviders,
377 namedModules,
378 false,
379 includeStatic,
380 includeStatic);
381 }
382 }
383
384 for (String uses : module.uses()) {
385 if (availableProviders.containsKey(uses)) {
386 for (String providerModule : availableProviders.get(uses)) {
387 JavaModuleDescriptor requiredModule = availableModules.get(providerModule);
388
389 if (requiredModule != null && namedModules.add(providerModule)) {
390 selectRequires(
391 requiredModule,
392 availableModules,
393 availableProviders,
394 namedModules,
395 false,
396 includeAsTransitive,
397 includeStatic);
398 }
399 }
400 }
401 }
402 }
403
404 private void selectModule(
405 String module,
406 Map<String, JavaModuleDescriptor> availableModules,
407 Map<String, Set<String>> availableProviders,
408 Set<String> namedModules,
409 boolean isRootModule,
410 boolean includeTransitive,
411 boolean includeStatic) {
412 JavaModuleDescriptor requiredModule = availableModules.get(module);
413
414 if (requiredModule != null && namedModules.add(module)) {
415 selectRequires(
416 requiredModule,
417 availableModules,
418 availableProviders,
419 namedModules,
420 false,
421 includeTransitive,
422 includeStatic);
423 }
424 }
425
426 private void collectProviders(JavaModuleDescriptor moduleDescriptor, Map<String, Set<String>> availableProviders) {
427 for (JavaProvides provides : moduleDescriptor.provides()) {
428
429 final String serviceClassName = provides.service().replace('$', '.');
430
431 Set<String> providingModules = availableProviders.get(serviceClassName);
432
433 if (providingModules == null) {
434 providingModules = new HashSet<>();
435
436 availableProviders.put(serviceClassName, providingModules);
437 }
438 providingModules.add(moduleDescriptor.name());
439 }
440 }
441 }