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