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