mirror of https://github.com/apache/maven.git
[MNG-8134] Add a @Resolution annotation to mojos to inject project dependencies collection / resolution result
This commit is contained in:
parent
8d483f922b
commit
b852f74084
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* The mojo class can also be injected with an {@link Execute} annotation to specify a
|
||||
* forked lifecycle.
|
||||
* </p>
|
||||
* <p>
|
||||
* The {@link Parameter} annotation can be added on fields to inject data
|
||||
* from the plugin configuration or from other components.
|
||||
* </p>
|
||||
* <p>
|
||||
* Fields can also be annotated with the {@link Resolution} annotation to be injected
|
||||
* with the dependency collection or resolution result for the project.
|
||||
* </p>
|
||||
*
|
||||
* @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 "";
|
||||
}
|
||||
|
|
|
@ -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()}.
|
||||
* <p>
|
||||
* 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 <i>collected</i>.
|
||||
* 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 <i>flattened</i>.
|
||||
* Else the dependencies will be <i>resolved</i> 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 "";
|
||||
}
|
|
@ -397,6 +397,15 @@ under the License.
|
|||
<multiplicity>*</multiplicity>
|
||||
</association>
|
||||
</field>
|
||||
<field xdoc.separator="blank">
|
||||
<name>resolutions</name>
|
||||
<version>2.0.0+</version>
|
||||
<description></description>
|
||||
<association>
|
||||
<type>Resolution</type>
|
||||
<multiplicity>*</multiplicity>
|
||||
</association>
|
||||
</field>
|
||||
<field xdoc.separator="blank">
|
||||
<name>requirements</name>
|
||||
<version>1.0.0/1.1.0</version>
|
||||
|
@ -580,5 +589,34 @@ under the License.
|
|||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
|
||||
<class xdoc.anchorName="resolution">
|
||||
<name>Resolution</name>
|
||||
<version>2.0.0+</version>
|
||||
<description>Dependency collection or resolution injection.</description>
|
||||
<fields>
|
||||
<field>
|
||||
<name>field</name>
|
||||
<required>false</required>
|
||||
<version>2.0.0+</version>
|
||||
<type>String</type>
|
||||
<description>the name of the field to be injected</description>
|
||||
</field>
|
||||
<field>
|
||||
<name>pathScope</name>
|
||||
<required>false</required>
|
||||
<version>2.0.0+</version>
|
||||
<type>String</type>
|
||||
<description>pathScope used to flatten dependencies</description>
|
||||
</field>
|
||||
<field>
|
||||
<name>requestType</name>
|
||||
<required>false</required>
|
||||
<version>2.0.0+</version>
|
||||
<type>String</type>
|
||||
<description>either {@code collect}, {@code flatten}, or {@code resolve}</description>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
</model>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -166,7 +166,6 @@ public class MojoDescriptor extends ComponentDescriptor<Mojo> 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<Mojo> implements Cloneab
|
|||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
this.mojoDescriptorV4 = md;
|
||||
this.v4Api = true;
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
//
|
||||
|
@ -622,10 +622,6 @@ public class MojoDescriptor extends ComponentDescriptor<Mojo> implements Cloneab
|
|||
return v4Api;
|
||||
}
|
||||
|
||||
public void setV4Api(boolean v4Api) {
|
||||
this.v4Api = v4Api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of this mojo descriptor.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
// ----------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue