1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.plugin;
20
21 import javax.inject.Inject;
22
23 import java.io.File;
24 import java.net.URI;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Set;
30
31 import org.apache.maven.artifact.Artifact;
32 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
33 import org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
34 import org.apache.maven.execution.MavenSession;
35 import org.apache.maven.plugin.MojoExecutionException;
36 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
37 import org.apache.maven.plugin.descriptor.PluginDescriptor;
38 import org.apache.maven.plugins.annotations.LifecyclePhase;
39 import org.apache.maven.plugins.annotations.Mojo;
40 import org.apache.maven.plugins.annotations.Parameter;
41 import org.apache.maven.plugins.annotations.ResolutionScope;
42 import org.apache.maven.project.MavenProject;
43 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
44 import org.apache.maven.tools.plugin.PluginDescriptorHelper;
45 import org.apache.maven.tools.plugin.PluginToolsRequest;
46 import org.apache.maven.tools.plugin.extractor.ExtractionException;
47 import org.apache.maven.tools.plugin.generator.GeneratorException;
48 import org.apache.maven.tools.plugin.generator.GeneratorUtils;
49 import org.apache.maven.tools.plugin.generator.PluginDescriptorFilesGenerator;
50 import org.apache.maven.tools.plugin.scanner.MojoScanner;
51 import org.codehaus.plexus.component.repository.ComponentDependency;
52 import org.codehaus.plexus.util.ReaderFactory;
53 import org.sonatype.plexus.build.incremental.BuildContext;
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 @Mojo(
70 name = "descriptor",
71 defaultPhase = LifecyclePhase.PROCESS_CLASSES,
72 requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
73 threadSafe = true)
74 public class DescriptorGeneratorMojo extends AbstractGeneratorMojo {
75 private static final String VALUE_AUTO = "auto";
76
77
78
79
80 @Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/maven", readonly = true)
81 private File outputDirectory;
82
83
84
85
86
87
88 @Parameter(property = "encoding", defaultValue = "${project.build.sourceEncoding}")
89 private String encoding;
90
91
92
93
94
95
96 @Parameter(defaultValue = "false")
97 private boolean skipDescriptor;
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 @Parameter
131 private Set<String> extractors;
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 @Parameter
164 private Set<String> excludedScanDirectories = Collections.emptySet();
165
166
167
168
169
170
171
172
173 @Parameter(property = "maven.plugin.skipErrorNoDescriptorsFound", defaultValue = "false")
174 private boolean skipErrorNoDescriptorsFound;
175
176
177
178
179
180
181
182 @Parameter(defaultValue = "true", property = "maven.plugin.checkExpectedProvidedScope")
183 private boolean checkExpectedProvidedScope = true;
184
185
186
187
188
189
190
191 @Parameter
192 private List<String> expectedProvidedScopeGroupIds = Collections.singletonList("org.apache.maven");
193
194
195
196
197
198
199
200
201 @Parameter
202 private List<String> expectedProvidedScopeExclusions = Arrays.asList(
203 "org.apache.maven:maven-archiver", "org.apache.maven:maven-jxr", "org.apache.maven:plexus-utils");
204
205
206
207
208
209
210
211
212
213 @Parameter
214 private List<String> mojoDependencies = null;
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233 @Parameter(property = "externalJavadocBaseUrls", alias = "links")
234 protected List<URI> externalJavadocBaseUrls;
235
236
237
238
239
240
241
242
243
244
245 @Parameter(property = "internalJavadocBaseUrl")
246 protected URI internalJavadocBaseUrl;
247
248
249
250
251
252
253
254
255 @Parameter(property = "internalJavadocVersion", defaultValue = "${java.version}")
256 protected String internalJavadocVersion;
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 @Parameter(defaultValue = VALUE_AUTO)
273 String requiredJavaVersion;
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 @Parameter(defaultValue = VALUE_AUTO)
292 String requiredMavenVersion;
293
294 private final MavenSession mavenSession;
295
296
297
298
299 private final MojoScanner mojoScanner;
300
301 protected final BuildContext buildContext;
302
303 @Inject
304 public DescriptorGeneratorMojo(
305 MavenProject project, MavenSession mavenSession, MojoScanner mojoScanner, BuildContext buildContext) {
306 super(project);
307 this.mavenSession = mavenSession;
308 this.mojoScanner = mojoScanner;
309 this.buildContext = buildContext;
310 }
311
312 public void generate() throws MojoExecutionException {
313
314 if (!"maven-plugin".equalsIgnoreCase(project.getArtifactId())
315 && project.getArtifactId().toLowerCase().startsWith("maven-")
316 && project.getArtifactId().toLowerCase().endsWith("-plugin")
317 && !"org.apache.maven.plugins".equals(project.getGroupId())) {
318 getLog().warn(LS + LS + "Artifact Ids of the format maven-___-plugin are reserved for" + LS
319 + "plugins in the Group Id org.apache.maven.plugins" + LS
320 + "Please change your artifactId to the format ___-maven-plugin" + LS
321 + "In the future this error will break the build." + LS + LS);
322 }
323
324 if (skipDescriptor) {
325 getLog().warn("Execution skipped");
326 return;
327 }
328
329 if (checkExpectedProvidedScope) {
330 Set<Artifact> wrongScopedArtifacts = dependenciesNotInProvidedScope();
331 if (!wrongScopedArtifacts.isEmpty()) {
332 StringBuilder message = new StringBuilder(
333 LS + LS + "Some dependencies of Maven Plugins are expected to be in provided scope." + LS
334 + "Please make sure that dependencies listed below declared in POM" + LS
335 + "have set '<scope>provided</scope>' as well." + LS + LS
336 + "The following dependencies are in wrong scope:" + LS);
337 for (Artifact artifact : wrongScopedArtifacts) {
338 message.append(" * ").append(artifact).append(LS);
339 }
340 message.append(LS).append(LS);
341
342 getLog().warn(message.toString());
343 }
344 }
345
346 mojoScanner.setActiveExtractors(extractors);
347
348
349 PluginDescriptor pluginDescriptor = new PluginDescriptor();
350
351 pluginDescriptor.setGroupId(project.getGroupId());
352
353 pluginDescriptor.setArtifactId(project.getArtifactId());
354
355 pluginDescriptor.setVersion(project.getVersion());
356
357 pluginDescriptor.setGoalPrefix(goalPrefix);
358
359 pluginDescriptor.setName(project.getName());
360
361 pluginDescriptor.setDescription(project.getDescription());
362
363 if (encoding == null || encoding.length() < 1) {
364 getLog().warn("Using platform encoding (" + ReaderFactory.FILE_ENCODING
365 + " actually) to read mojo source files, i.e. build is platform dependent!");
366 } else {
367 getLog().info("Using '" + encoding + "' encoding to read mojo source files.");
368 }
369
370 if (internalJavadocBaseUrl != null && !internalJavadocBaseUrl.getPath().endsWith("/")) {
371 throw new MojoExecutionException("Given parameter 'internalJavadocBaseUrl' must end with a slash but is '"
372 + internalJavadocBaseUrl + "'");
373 }
374 try {
375 List<ComponentDependency> deps = GeneratorUtils.toComponentDependencies(project.getArtifacts());
376 pluginDescriptor.setDependencies(deps);
377
378 PluginToolsRequest request = new DefaultPluginToolsRequest(project, pluginDescriptor);
379 request.setEncoding(encoding);
380 request.setSkipErrorNoDescriptorsFound(skipErrorNoDescriptorsFound);
381 request.setDependencies(filterMojoDependencies());
382 request.setRepoSession(mavenSession.getRepositorySession());
383 request.setInternalJavadocBaseUrl(internalJavadocBaseUrl);
384 request.setInternalJavadocVersion(internalJavadocVersion);
385 request.setExternalJavadocBaseUrls(externalJavadocBaseUrls);
386 request.setSettings(mavenSession.getSettings());
387 request.setExcludedScanDirectories(excludedScanDirectories);
388
389 mojoScanner.populatePluginDescriptor(request);
390 request.setPluginDescriptor(extendPluginDescriptor(request));
391
392 outputDirectory.mkdirs();
393
394 ValidateComponentRequirement validateComponentRequirement = new ValidateComponentRequirement();
395 validateComponentRequirement.validate(request.getPluginDescriptor(), getLog());
396
397 PluginDescriptorFilesGenerator pluginDescriptorGenerator = new PluginDescriptorFilesGenerator();
398 pluginDescriptorGenerator.execute(outputDirectory, request);
399
400 buildContext.refresh(outputDirectory);
401 } catch (GeneratorException e) {
402 throw new MojoExecutionException("Error writing plugin descriptor", e);
403 } catch (InvalidPluginDescriptorException | ExtractionException e) {
404 throw new MojoExecutionException(
405 "Error extracting plugin descriptor: '" + e.getLocalizedMessage() + "'", e);
406 } catch (LinkageError e) {
407 throw new MojoExecutionException(
408 "The API of the mojo scanner is not compatible with this plugin version."
409 + " Please check the plugin dependencies configured"
410 + " in the POM and ensure the versions match.",
411 e);
412 }
413 }
414
415 private PluginDescriptor extendPluginDescriptor(PluginToolsRequest request) {
416 PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
417 pluginDescriptor.setRequiredMavenVersion(getRequiredMavenVersion(request));
418 return PluginDescriptorHelper.setRequiredJavaVersion(pluginDescriptor, getRequiredJavaVersion(request));
419 }
420
421 private String getRequiredMavenVersion(PluginToolsRequest request) {
422 if (!VALUE_AUTO.equals(requiredMavenVersion)) {
423 return requiredMavenVersion;
424 }
425 getLog().debug("Trying to derive Maven version automatically from project prerequisites...");
426 String requiredMavenVersion =
427 project.getPrerequisites() != null ? project.getPrerequisites().getMaven() : null;
428 if (requiredMavenVersion == null) {
429 getLog().debug("Trying to derive Maven version automatically from referenced Maven Plugin API artifact "
430 + "version...");
431 requiredMavenVersion = request.getUsedMavenApiVersion();
432 }
433 if (requiredMavenVersion == null) {
434 getLog().warn("Cannot determine the required Maven version automatically, it is recommended to "
435 + "configure some explicit value manually.");
436 }
437 return requiredMavenVersion;
438 }
439
440 private String getRequiredJavaVersion(PluginToolsRequest request) {
441 if (!VALUE_AUTO.equals(requiredJavaVersion)) {
442 return requiredJavaVersion;
443 }
444 String minRequiredJavaVersion = request.getRequiredJavaVersion();
445 if (minRequiredJavaVersion == null) {
446 getLog().warn("Cannot determine the minimally required Java version automatically, it is recommended to "
447 + "configure some explicit value manually.");
448 return null;
449 }
450
451 return minRequiredJavaVersion;
452 }
453
454
455
456
457 private Set<Artifact> dependenciesNotInProvidedScope() {
458 LinkedHashSet<Artifact> wrongScopedDependencies = new LinkedHashSet<>();
459
460 for (Artifact dependency : project.getArtifacts()) {
461 String ga = dependency.getGroupId() + ":" + dependency.getArtifactId();
462 if (expectedProvidedScopeGroupIds.contains(dependency.getGroupId())
463 && !expectedProvidedScopeExclusions.contains(ga)
464 && !Artifact.SCOPE_PROVIDED.equals(dependency.getScope())) {
465 wrongScopedDependencies.add(dependency);
466 }
467 }
468
469 return wrongScopedDependencies;
470 }
471
472
473
474
475
476
477
478
479 private Set<Artifact> filterMojoDependencies() {
480 Set<Artifact> filteredArtifacts;
481 if (mojoDependencies == null) {
482 filteredArtifacts = new LinkedHashSet<>(project.getArtifacts());
483 } else if (mojoDependencies.isEmpty()) {
484 filteredArtifacts = null;
485 } else {
486 filteredArtifacts = new LinkedHashSet<>();
487
488 ArtifactFilter filter = new IncludesArtifactFilter(mojoDependencies);
489
490 for (Artifact artifact : project.getArtifacts()) {
491 if (filter.include(artifact)) {
492 filteredArtifacts.add(artifact);
493 }
494 }
495 }
496
497 return filteredArtifacts;
498 }
499 }