Skip to content

Commit 736dfec

Browse files
committed
[MNG-8134] Add a @Dependencies annotation to mojos to inject project dependencies collection / resolution result
1 parent 33010af commit 736dfec

File tree

6 files changed

+201
-13
lines changed

6 files changed

+201
-13
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.plugin.annotations;
20+
21+
import java.lang.annotation.Documented;
22+
import java.lang.annotation.ElementType;
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.RetentionPolicy;
25+
import java.lang.annotation.Target;
26+
27+
import org.apache.maven.api.annotations.Experimental;
28+
29+
/**
30+
* Indicates that a given field will be injected with the result of
31+
* a dependency collection or resolution request. Whether a collection
32+
* or resolution request is performed is controlled by the {@link #pathScope()}
33+
* field.
34+
* <p>
35+
* If a collection request is to be done, the type of the field annotated with
36+
* this annotation can be either <ul>
37+
* <li>a {@link org.apache.maven.api.services.DependencyCollectorResult DependencyCollectorResult},</li>
38+
* <li>or a {@link org.apache.maven.api.Node Node} object.</li>
39+
* </ul>
40+
* <p>
41+
* If a resolution request is to be done, the type of the annotated field can be either <ul>
42+
* <li>a {@link org.apache.maven.api.services.DependencyResolverResult DependencyResolverResult},</li>
43+
* <li>a {@code List<}{@link org.apache.maven.api.Node Node}{@code >},</li>
44+
* <li>a {@code List<}{@link java.nio.file.Path Path}{@code >},</li>
45+
* <li>a {@code Map<}{@link org.apache.maven.api.PathType PathType}{@code , List<}{@link java.nio.file.Path Path}{@code >>},</li>
46+
* <li>or a {@code Map<}{@link org.apache.maven.api.Dependency Dependency}{@code , }{@link java.nio.file.Path Path}{@code >}.</li>
47+
* </ul>
48+
*
49+
* @since 4.0.0
50+
*/
51+
@Experimental
52+
@Documented
53+
@Retention(RetentionPolicy.RUNTIME)
54+
@Target(ElementType.FIELD)
55+
public @interface Dependencies {
56+
57+
/**
58+
* The id of a {@link org.apache.maven.api.PathScope} enum value.
59+
* If specified, a dependency resolution request will be issued,
60+
* else a dependency collection request will be done.
61+
*
62+
* @return the id of the path scope
63+
*/
64+
String pathScope() default "";
65+
}

api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,23 @@
2929
import org.apache.maven.api.annotations.Nonnull;
3030

3131
/**
32-
* This annotation will mark your class as a Mojo (ie. goal in a Maven plugin).
33-
* The mojo can be annotated with {@code jakarta.inject.*} annotations.
32+
* This annotation will mark your class as a Mojo, which is the implementation of a goal in a Maven plugin.
33+
* <p>
34+
* The mojo can be annotated with {@code org.apache.maven.api.di.*} annotations to
35+
* control the lifecycle of the mojo itself, and to inject other beans.
36+
* </p>
37+
* <p>
38+
* The mojo class can also be injected with an {@link Execute} annotation to specify a
39+
* forked lifecycle.
40+
* </p>
41+
* <p>
3442
* The {@link Parameter} annotation can be added on fields to inject data
3543
* from the plugin configuration or from other components.
44+
* </p>
45+
* <p>
46+
* Fields can also be annotated with the {@link Dependencies} annotation to be injected
47+
* with the dependency collection or resolution result for the project.
48+
* </p>
3649
*
3750
* @since 4.0.0
3851
*/

api/maven-api-plugin/src/main/mdo/plugin.mdo

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,15 @@ under the License.
397397
<multiplicity>*</multiplicity>
398398
</association>
399399
</field>
400+
<field xdoc.separator="blank">
401+
<name>dependencies</name>
402+
<version>2.0.0+</version>
403+
<description></description>
404+
<association>
405+
<type>Dependencies</type>
406+
<multiplicity>*</multiplicity>
407+
</association>
408+
</field>
400409
<field xdoc.separator="blank">
401410
<name>requirements</name>
402411
<version>1.0.0/1.1.0</version>
@@ -580,5 +589,27 @@ under the License.
580589
</field>
581590
</fields>
582591
</class>
592+
593+
<class xdoc.anchorName="dependencies">
594+
<name>Dependencies</name>
595+
<version>2.0.0+</version>
596+
<description>Dependency collection or resolution injection.</description>
597+
<fields>
598+
<field>
599+
<name>field</name>
600+
<required>false</required>
601+
<version>2.0.0+</version>
602+
<type>String</type>
603+
<description>the name of the field to be injected</description>
604+
</field>
605+
<field>
606+
<name>pathScope</name>
607+
<required>false</required>
608+
<version>2.0.0+</version>
609+
<type>String</type>
610+
<description>pathScope to resolve dependencies, else will collect dependencies</description>
611+
</field>
612+
</fields>
613+
</class>
583614
</classes>
584615
</model>

maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,29 @@
2424

2525
import java.io.*;
2626
import java.lang.annotation.Annotation;
27+
import java.lang.reflect.Field;
28+
import java.lang.reflect.ParameterizedType;
29+
import java.lang.reflect.Type;
2730
import java.nio.file.Files;
31+
import java.nio.file.Path;
2832
import java.util.*;
2933
import java.util.jar.JarFile;
3034
import java.util.stream.Collectors;
3135
import java.util.zip.ZipEntry;
3236

3337
import org.apache.maven.RepositoryUtils;
38+
import org.apache.maven.api.Dependency;
39+
import org.apache.maven.api.Node;
40+
import org.apache.maven.api.PathScope;
41+
import org.apache.maven.api.PathType;
3442
import org.apache.maven.api.Project;
3543
import org.apache.maven.api.Session;
44+
import org.apache.maven.api.plugin.descriptor.Dependencies;
45+
import org.apache.maven.api.services.DependencyCollector;
46+
import org.apache.maven.api.services.DependencyCollectorResult;
47+
import org.apache.maven.api.services.DependencyResolver;
48+
import org.apache.maven.api.services.DependencyResolverResult;
49+
import org.apache.maven.api.services.PathScopeRegistry;
3650
import org.apache.maven.api.services.ProjectManager;
3751
import org.apache.maven.api.xml.XmlNode;
3852
import org.apache.maven.artifact.Artifact;
@@ -575,6 +589,80 @@ private <T> T loadV4Mojo(
575589
pomConfiguration,
576590
expressionEvaluator);
577591

592+
for (Dependencies dependencies : mojoDescriptor.getMojoDescriptorV4().getDependencies()) {
593+
Field field = null;
594+
for (Class<?> clazz = mojo.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
595+
try {
596+
field = clazz.getDeclaredField(dependencies.getField());
597+
break;
598+
} catch (NoSuchFieldException e) {
599+
// continue
600+
}
601+
}
602+
if (field == null) {
603+
throw new PluginConfigurationException(
604+
pluginDescriptor,
605+
"Unable to find field '" + dependencies.getField() + "' annotated with @Dependencies");
606+
}
607+
field.setAccessible(true);
608+
String pathScope = dependencies.getPathScope();
609+
Object result = null;
610+
if (pathScope != null && !pathScope.isEmpty()) {
611+
// resolution
612+
PathScope ps = sessionV4.getService(PathScopeRegistry.class).require(pathScope);
613+
DependencyResolverResult res =
614+
sessionV4.getService(DependencyResolver.class).resolve(sessionV4, project, ps);
615+
if (field.getType() == DependencyResolverResult.class) {
616+
result = res;
617+
} else if (field.getType() == DependencyCollectorResult.class) {
618+
result = res;
619+
} else if (field.getType() == Node.class) {
620+
result = res.getRoot();
621+
} else if (field.getType() == List.class && field.getGenericType() instanceof ParameterizedType pt) {
622+
Type t = pt.getActualTypeArguments()[0];
623+
if (t == Node.class) {
624+
result = res.getNodes();
625+
} else if (t == Path.class) {
626+
result = res.getPaths();
627+
}
628+
} else if (field.getType() == Map.class && field.getGenericType() instanceof ParameterizedType pt) {
629+
Type k = pt.getActualTypeArguments()[0];
630+
Type v = pt.getActualTypeArguments()[1];
631+
if (k == PathType.class
632+
&& v instanceof ParameterizedType ptv
633+
&& ptv.getRawType() == List.class
634+
&& ptv.getActualTypeArguments()[0] == Path.class) {
635+
result = res.getDispatchedPaths();
636+
} else if (k == Dependency.class && v == Path.class) {
637+
result = res.getDependencies();
638+
}
639+
}
640+
} else {
641+
// collection
642+
DependencyCollectorResult res =
643+
sessionV4.getService(DependencyCollector.class).collect(sessionV4, project);
644+
if (field.getType() == DependencyCollectorResult.class) {
645+
result = res;
646+
} else if (field.getType() == Node.class) {
647+
result = res.getRoot();
648+
}
649+
}
650+
if (result == null) {
651+
throw new PluginConfigurationException(
652+
pluginDescriptor,
653+
"Unable to inject field '" + dependencies.getField()
654+
+ "' annotated with @Dependencies. Unsupported type " + field.getGenericType());
655+
}
656+
try {
657+
field.set(mojo, result);
658+
} catch (IllegalAccessException e) {
659+
throw new PluginConfigurationException(
660+
pluginDescriptor,
661+
"Unable to inject field '" + dependencies.getField() + "' annotated with @Dependencies",
662+
e);
663+
}
664+
}
665+
578666
return mojo;
579667
}
580668

@@ -730,6 +818,7 @@ private void populateMojoExecutionFields(
730818
validateParameters(mojoDescriptor, configuration, expressionEvaluator);
731819
}
732820
}
821+
733822
} catch (ComponentConfigurationException e) {
734823
String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
735824
if (e.getFailedConfiguration() != null) {

maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ public MojoDescriptor(PluginDescriptor pd, org.apache.maven.api.plugin.descripto
166166
this.setProjectRequired(md.isProjectRequired());
167167
this.setSince(md.getSince());
168168
this.setThreadSafe(true);
169-
this.setV4Api(true);
170169
this.setImplementation(md.getImplementation());
171170
try {
172171
this.setParameters(md.getParameters().stream().map(Parameter::new).collect(Collectors.toList()));
173172
} catch (DuplicateParameterException e) {
174173
throw new IllegalArgumentException(e);
175174
}
176175
this.mojoDescriptorV4 = md;
176+
this.v4Api = true;
177177
}
178178
// ----------------------------------------------------------------------
179179
//
@@ -622,10 +622,6 @@ public boolean isV4Api() {
622622
return v4Api;
623623
}
624624

625-
public void setV4Api(boolean v4Api) {
626-
this.v4Api = v4Api;
627-
}
628-
629625
/**
630626
* Creates a shallow copy of this mojo descriptor.
631627
*/

maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,6 @@ public MojoDescriptor buildComponentDescriptor(PlexusConfiguration c, PluginDesc
385385
mojo.setThreadSafe(Boolean.parseBoolean(threadSafe));
386386
}
387387

388-
String v4Api = c.getChild("v4Api").getValue();
389-
390-
if (v4Api != null) {
391-
mojo.setV4Api(Boolean.parseBoolean(v4Api));
392-
}
393-
394388
// ----------------------------------------------------------------------
395389
// Configuration
396390
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)