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