diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
index a5768dfb4a..229232582e 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java
@@ -29,10 +29,23 @@ import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
/**
- * This annotation will mark your class as a Mojo (ie. goal in a Maven plugin).
- * The mojo can be annotated with {@code jakarta.inject.*} annotations.
+ * This annotation will mark your class as a Mojo, which is the implementation of a goal in a Maven plugin.
+ *
+ * The mojo can be annotated with {@code org.apache.maven.api.di.*} annotations to
+ * control the lifecycle of the mojo itself, and to inject other beans.
+ *
+ *
+ * The mojo class can also be injected with an {@link Execute} annotation to specify a
+ * forked lifecycle.
+ *
+ *
* The {@link Parameter} annotation can be added on fields to inject data
* from the plugin configuration or from other components.
+ *
+ *
+ * Fields can also be annotated with the {@link Resolution} annotation to be injected
+ * with the dependency collection or resolution result for the project.
+ *
*
* @since 4.0.0
*/
@@ -81,4 +94,22 @@ public @interface Mojo {
*/
@Nonnull
String configurator() default "";
+
+ /**
+ * Indicates whether dependency collection will be
+ * required when executing the Mojo.
+ * If not set, it will be inferred from the fields
+ * annotated with the {@link Resolution} annotation.
+ */
+ @Nonnull
+ boolean dependencyCollection() default false;
+
+ /**
+ * Comma separated list of path scopes that will be
+ * required for dependency resolution.
+ * If not set, it will be inferred from the fields
+ * annotated with the {@link Resolution} annotation.
+ */
+ @Nonnull
+ String dependencyResolutionPathScopes() default "";
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java
new file mode 100644
index 0000000000..6e745a0dd4
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Resolution.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.plugin.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.maven.api.annotations.Experimental;
+
+/**
+ * Indicates that a given field will be injected with the result of
+ * a dependency collection or resolution request. Whether a collection
+ * or resolution request is performed is controlled by the {@link #pathScope()}
+ * field, the injected field type and the {@link #requestType()}.
+ *
+ * If the {@code requestType} is not set explicitly, it will be inferred
+ * from the {@code pathScope} and the injected field type. If the type
+ * is {@link org.apache.maven.api.Node Node} and {@code pathScope == ""},
+ * then the dependencies will be collected.
+ * If the type is {@link org.apache.maven.api.Node Node} or
+ * {@code List<}{@link org.apache.maven.api.Node Node}{@code >},
+ * and {@code pathScope != ""}, the dependencies will be flattened.
+ * Else the dependencies will be resolved and {@code pathScope} must be non empty,
+ * and the field type can be {@link org.apache.maven.api.Node Node},
+ * {@code List<}{@link org.apache.maven.api.Node Node}{@code >},
+ * {@link org.apache.maven.api.services.DependencyResolverResult DependencyResolverResult},
+ * {@code List<}{@link java.nio.file.Path Path}{@code >},
+ * {@code Map<}{@link org.apache.maven.api.PathType PathType}{@code , List<}{@link java.nio.file.Path Path}{@code >>},
+ * or {@code Map<}{@link org.apache.maven.api.Dependency Dependency}{@code , }{@link java.nio.file.Path Path}{@code >}.
+ *
+ * @since 4.0.0
+ */
+@Experimental
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Resolution {
+
+ /**
+ * The id of a {@link org.apache.maven.api.PathScope} enum value.
+ * If specified, a dependency resolution request will be issued,
+ * else a dependency collection request will be done.
+ *
+ * @return the id of the path scope
+ */
+ String pathScope() default "";
+
+ /**
+ * The request type, in case the default one is not correct.
+ * Valid values are {@code collect}, {@code flatten}, or {@code resolve}.
+ *
+ * @return the request type
+ */
+ String requestType() default "";
+}
diff --git a/api/maven-api-plugin/src/main/mdo/plugin.mdo b/api/maven-api-plugin/src/main/mdo/plugin.mdo
index 38bc1f917e..6d63d099af 100644
--- a/api/maven-api-plugin/src/main/mdo/plugin.mdo
+++ b/api/maven-api-plugin/src/main/mdo/plugin.mdo
@@ -397,6 +397,15 @@ under the License.
*
+
+ resolutions
+ 2.0.0+
+
+
+ Resolution
+ *
+
+
requirements
1.0.0/1.1.0
@@ -580,5 +589,34 @@ under the License.
+
+
+ Resolution
+ 2.0.0+
+ Dependency collection or resolution injection.
+
+
+ field
+ false
+ 2.0.0+
+ String
+ the name of the field to be injected
+
+
+ pathScope
+ false
+ 2.0.0+
+ String
+ pathScope used to flatten dependencies
+
+
+ requestType
+ false
+ 2.0.0+
+ String
+ either {@code collect}, {@code flatten}, or {@code resolve}
+
+
+
diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 5c250082a1..44864cea23 100644
--- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -24,15 +24,27 @@ import javax.inject.Singleton;
import java.io.*;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.*;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import org.apache.maven.RepositoryUtils;
+import org.apache.maven.api.Dependency;
+import org.apache.maven.api.Node;
+import org.apache.maven.api.PathScope;
+import org.apache.maven.api.PathType;
import org.apache.maven.api.Project;
import org.apache.maven.api.Session;
+import org.apache.maven.api.plugin.descriptor.Resolution;
+import org.apache.maven.api.services.DependencyResolver;
+import org.apache.maven.api.services.DependencyResolverResult;
+import org.apache.maven.api.services.PathScopeRegistry;
import org.apache.maven.api.services.ProjectManager;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.artifact.Artifact;
@@ -575,6 +587,78 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
pomConfiguration,
expressionEvaluator);
+ for (Resolution resolution : mojoDescriptor.getMojoDescriptorV4().getResolutions()) {
+ Field field = null;
+ for (Class> clazz = mojo.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+ try {
+ field = clazz.getDeclaredField(resolution.getField());
+ break;
+ } catch (NoSuchFieldException e) {
+ // continue
+ }
+ }
+ if (field == null) {
+ throw new PluginConfigurationException(
+ pluginDescriptor,
+ "Unable to find field '" + resolution.getField() + "' annotated with @Resolution");
+ }
+ field.setAccessible(true);
+ String pathScope = resolution.getPathScope();
+ Object result = null;
+ if (pathScope != null && !pathScope.isEmpty()) {
+ // resolution
+ PathScope ps = sessionV4.getService(PathScopeRegistry.class).require(pathScope);
+ DependencyResolverResult res =
+ sessionV4.getService(DependencyResolver.class).resolve(sessionV4, project, ps);
+ if (field.getType() == DependencyResolverResult.class) {
+ result = res;
+ } else if (field.getType() == Node.class) {
+ result = res.getRoot();
+ } else if (field.getType() == List.class && field.getGenericType() instanceof ParameterizedType pt) {
+ Type t = pt.getActualTypeArguments()[0];
+ if (t == Node.class) {
+ result = res.getNodes();
+ } else if (t == Path.class) {
+ result = res.getPaths();
+ }
+ } else if (field.getType() == Map.class && field.getGenericType() instanceof ParameterizedType pt) {
+ Type k = pt.getActualTypeArguments()[0];
+ Type v = pt.getActualTypeArguments()[1];
+ if (k == PathType.class
+ && v instanceof ParameterizedType ptv
+ && ptv.getRawType() == List.class
+ && ptv.getActualTypeArguments()[0] == Path.class) {
+ result = res.getDispatchedPaths();
+ } else if (k == Dependency.class && v == Path.class) {
+ result = res.getDependencies();
+ }
+ }
+ } else {
+ // collection
+ DependencyResolverResult res =
+ sessionV4.getService(DependencyResolver.class).collect(sessionV4, project);
+ if (field.getType() == DependencyResolverResult.class) {
+ result = res;
+ } else if (field.getType() == Node.class) {
+ result = res.getRoot();
+ }
+ }
+ if (result == null) {
+ throw new PluginConfigurationException(
+ pluginDescriptor,
+ "Unable to inject field '" + resolution.getField()
+ + "' annotated with @Dependencies. Unsupported type " + field.getGenericType());
+ }
+ try {
+ field.set(mojo, result);
+ } catch (IllegalAccessException e) {
+ throw new PluginConfigurationException(
+ pluginDescriptor,
+ "Unable to inject field '" + resolution.getField() + "' annotated with @Dependencies",
+ e);
+ }
+ }
+
return mojo;
}
@@ -730,6 +814,7 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
validateParameters(mojoDescriptor, configuration, expressionEvaluator);
}
}
+
} catch (ComponentConfigurationException e) {
String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
if (e.getFailedConfiguration() != null) {
diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java
index 4c067effac..750ebddd66 100644
--- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java
+++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java
@@ -166,7 +166,6 @@ public class MojoDescriptor extends ComponentDescriptor implements Cloneab
this.setProjectRequired(md.isProjectRequired());
this.setSince(md.getSince());
this.setThreadSafe(true);
- this.setV4Api(true);
this.setImplementation(md.getImplementation());
try {
this.setParameters(md.getParameters().stream().map(Parameter::new).collect(Collectors.toList()));
@@ -174,6 +173,7 @@ public class MojoDescriptor extends ComponentDescriptor implements Cloneab
throw new IllegalArgumentException(e);
}
this.mojoDescriptorV4 = md;
+ this.v4Api = true;
}
// ----------------------------------------------------------------------
//
@@ -622,10 +622,6 @@ public class MojoDescriptor extends ComponentDescriptor implements Cloneab
return v4Api;
}
- public void setV4Api(boolean v4Api) {
- this.v4Api = v4Api;
- }
-
/**
* Creates a shallow copy of this mojo descriptor.
*/
diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
index 8685cebf96..b29b96cd8e 100644
--- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
+++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptorBuilder.java
@@ -385,12 +385,6 @@ public class PluginDescriptorBuilder {
mojo.setThreadSafe(Boolean.parseBoolean(threadSafe));
}
- String v4Api = c.getChild("v4Api").getValue();
-
- if (v4Api != null) {
- mojo.setV4Api(Boolean.parseBoolean(v4Api));
- }
-
// ----------------------------------------------------------------------
// Configuration
// ----------------------------------------------------------------------