You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/07/25 06:32:03 UTC
(maven-plugin-tools) 01/01: Wire up @After annotation scanning in the annotation-based descriptor extractor
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch wire-up-after-annotation-scanning-in-maven-plugin
in repository https://gitbox.apache.org/repos/asf/maven-plugin-tools.git
commit dfdae35dcb1b5aec399fae6a6f2e75c4539808bb
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sat Jul 25 08:31:50 2026 +0200
Wire up @After annotation scanning in the annotation-based descriptor extractor
The v4 API @After annotation (and its @Repeatable container @Afters)
was not recognized by the annotation scanner, so plugins using @After
had to hand-craft the <afterLink> descriptor entries. This commit adds
end-to-end support:
- MojoAnnotationsScanner: register @After / @Afters in CLASS_LEVEL_ANNOTATIONS
- AfterAnnotationContent: new datamodel POJO holding phase/type/scope
- MojoAnnotatedClass: add List<AfterAnnotationContent> field
- MojoAnnotationVisitor: track nested annotation visitors returned from
visitAnnotation() and array sub-visitors from visitArray(), so the
@Afters repeatable container can be unpacked
- DefaultMojoAnnotationsScanner.analyzeVisitors(): extract single @After
and @Afters container into AfterAnnotationContent objects
- ExtendedMojoDescriptor: add AfterLink inner class and addAfterLink()
- JavaAnnotationsMojoDescriptorExtractor.toMojoDescriptors(): walk the
class hierarchy collecting @After annotations and map them to AfterLink
- PluginDescriptorFilesGenerator: emit <afterLinks>/<afterLink> elements
in the v4 plugin descriptor
Complements apache/maven#12535 which adds the model (AfterLink), runtime
processing (BuildPlanExecutor.applyAfterLinks), and an IT that currently
hand-crafts the descriptor.
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../JavaAnnotationsMojoDescriptorExtractor.java | 35 ++++++++++
.../datamodel/AfterAnnotationContent.java | 80 ++++++++++++++++++++++
.../scanner/DefaultMojoAnnotationsScanner.java | 35 ++++++++++
.../annotations/scanner/MojoAnnotatedClass.java | 24 ++++++-
.../scanner/MojoAnnotationsScanner.java | 4 +-
.../scanner/visitors/MojoAnnotationVisitor.java | 47 ++++++++++++-
.../maven/tools/plugin/ExtendedMojoDescriptor.java | 64 +++++++++++++++++
.../generator/PluginDescriptorFilesGenerator.java | 26 +++++++
8 files changed, 310 insertions(+), 5 deletions(-)
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java
index 04347349..6df5e19a 100644
--- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/JavaAnnotationsMojoDescriptorExtractor.java
@@ -67,6 +67,7 @@ import org.apache.maven.tools.plugin.extractor.annotations.converter.ConverterCo
import org.apache.maven.tools.plugin.extractor.annotations.converter.JavaClassConverterContext;
import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocBlockTagsToXhtmlConverter;
import org.apache.maven.tools.plugin.extractor.annotations.converter.JavadocInlineTagsToXhtmlConverter;
+import org.apache.maven.tools.plugin.extractor.annotations.datamodel.AfterAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
@@ -729,6 +730,14 @@ public class JavaAnnotationsMojoDescriptorExtractor implements MojoDescriptorExt
}
}
+ // @After annotations — collect from class hierarchy
+ List<AfterAnnotationContent> afterAnnotations =
+ getAfterAnnotationsFromHierarchy(mojoAnnotatedClass, mojoAnnotatedClasses);
+ for (AfterAnnotationContent after : afterAnnotations) {
+ mojoDescriptor.addAfterLink(
+ new ExtendedMojoDescriptor.AfterLink(after.phase(), after.type(), after.scope()));
+ }
+
mojoDescriptor.setExecutionStrategy(mojo.executionStrategy());
// ???
// mojoDescriptor.alwaysExecute(mojo.a)
@@ -819,6 +828,32 @@ public class JavaAnnotationsMojoDescriptorExtractor implements MojoDescriptorExt
return findClassWithExecuteAnnotationInParentHierarchy(parent, mojoAnnotatedClasses);
}
+ /**
+ * Collects all {@code @After} annotations from the class hierarchy, starting from the
+ * most-derived class. Unlike {@code @Execute}, {@code @After} is repeatable, so
+ * multiple entries may exist on a single class and across the hierarchy.
+ */
+ protected List<AfterAnnotationContent> getAfterAnnotationsFromHierarchy(
+ MojoAnnotatedClass mojoAnnotatedClass, Map<String, MojoAnnotatedClass> mojoAnnotatedClasses) {
+ List<AfterAnnotationContent> result = new ArrayList<>();
+ collectAfterAnnotations(mojoAnnotatedClass, mojoAnnotatedClasses, result);
+ return result;
+ }
+
+ private void collectAfterAnnotations(
+ MojoAnnotatedClass mojoAnnotatedClass,
+ Map<String, MojoAnnotatedClass> mojoAnnotatedClasses,
+ List<AfterAnnotationContent> result) {
+ result.addAll(mojoAnnotatedClass.getAfterAnnotations());
+ String parentClassName = mojoAnnotatedClass.getParentClassName();
+ if (parentClassName != null && !parentClassName.isEmpty()) {
+ MojoAnnotatedClass parent = mojoAnnotatedClasses.get(parentClassName);
+ if (parent != null) {
+ collectAfterAnnotations(parent, mojoAnnotatedClasses, result);
+ }
+ }
+ }
+
protected Map<String, ParameterAnnotationContent> getParametersParentHierarchy(
MojoAnnotatedClass mojoAnnotatedClass, Map<String, MojoAnnotatedClass> mojoAnnotatedClasses) {
List<ParameterAnnotationContent> parameterAnnotationContents = new ArrayList<>();
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/AfterAnnotationContent.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/AfterAnnotationContent.java
new file mode 100644
index 00000000..cd5d81b9
--- /dev/null
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/AfterAnnotationContent.java
@@ -0,0 +1,80 @@
+/*
+ * 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.tools.plugin.extractor.annotations.datamodel;
+
+/**
+ * Holds scanned data from a {@code @After} annotation
+ * ({@code org.apache.maven.api.plugin.annotations.After}).
+ * <p>
+ * The {@code @After} annotation declares that the annotated mojo must run
+ * after another phase has completed. It carries three attributes:
+ * <ul>
+ * <li>{@code phase} – the phase this mojo must run after</li>
+ * <li>{@code type} – the scope type ({@code PROJECT}, {@code DEPENDENCIES}, {@code CHILDREN})</li>
+ * <li>{@code scope} – the dependency scope</li>
+ * </ul>
+ * <p>
+ * Setter method names match the annotation attribute names so that
+ * {@link org.apache.maven.tools.plugin.extractor.annotations.scanner.DefaultMojoAnnotationsScanner#populateAnnotationContent}
+ * can fill them via reflection.
+ *
+ * @since 4.0.0
+ */
+public class AfterAnnotationContent {
+ private String phase;
+
+ private String type;
+
+ private String scope;
+
+ public String phase() {
+ return this.phase;
+ }
+
+ public void phase(String phase) {
+ this.phase = phase;
+ }
+
+ public String type() {
+ return this.type;
+ }
+
+ public void type(String type) {
+ this.type = type;
+ }
+
+ public String scope() {
+ return this.scope;
+ }
+
+ public void scope(String scope) {
+ this.scope = scope;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AfterAnnotationContent");
+ sb.append("{phase='").append(phase).append('\'');
+ sb.append(", type='").append(type).append('\'');
+ sb.append(", scope='").append(scope).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java
index fad64132..e9d8dca3 100644
--- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScanner.java
@@ -26,6 +26,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -42,6 +43,7 @@ import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.tools.plugin.extractor.ExtractionException;
+import org.apache.maven.tools.plugin.extractor.annotations.datamodel.AfterAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
@@ -73,6 +75,8 @@ public class DefaultMojoAnnotationsScanner implements MojoAnnotationsScanner {
public static final String MOJO_V4 = MVN4_API + "Mojo";
public static final String EXECUTE_V4 = MVN4_API + "Execute";
public static final String PARAMETER_V4 = MVN4_API + "Parameter";
+ public static final String AFTER_V4 = MVN4_API + "After";
+ public static final String AFTERS_V4 = MVN4_API + "Afters";
public static final String MOJO_V3 = Mojo.class.getName();
public static final String EXECUTE_V3 = Execute.class.getName();
@@ -308,6 +312,37 @@ public class DefaultMojoAnnotationsScanner implements MojoAnnotationsScanner {
mojoAnnotatedClass.setExecute(executeAnnotationContent);
}
+ // @After annotation(s) — v4 API only
+ List<AfterAnnotationContent> afterAnnotations = new ArrayList<>();
+
+ // single @After
+ mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor(AFTER_V4);
+ if (mojoAnnotationVisitor != null) {
+ AfterAnnotationContent afterAnnotationContent = new AfterAnnotationContent();
+ populateAnnotationContent(afterAnnotationContent, mojoAnnotationVisitor);
+ afterAnnotations.add(afterAnnotationContent);
+ }
+
+ // @Afters repeatable container wraps multiple @After in its "value" array attribute
+ mojoAnnotationVisitor = mojoClassVisitor.getAnnotationVisitor(AFTERS_V4);
+ if (mojoAnnotationVisitor != null) {
+ // The @Afters annotation has a "value" array attribute; visitArray("value")
+ // creates a sub-visitor, and each nested @After is visited via visitAnnotation
+ // on that sub-visitor.
+ MojoAnnotationVisitor arrayVisitor = mojoAnnotationVisitor.getArrayVisitor("value");
+ if (arrayVisitor != null) {
+ for (MojoAnnotationVisitor nestedVisitor : arrayVisitor.getNestedAnnotationVisitors()) {
+ AfterAnnotationContent afterAnnotationContent = new AfterAnnotationContent();
+ populateAnnotationContent(afterAnnotationContent, nestedVisitor);
+ afterAnnotations.add(afterAnnotationContent);
+ }
+ }
+ }
+
+ if (!afterAnnotations.isEmpty()) {
+ mojoAnnotatedClass.setAfterAnnotations(afterAnnotations);
+ }
+
// @Parameter annotations
List<MojoParameterVisitor> mojoParameterVisitors =
mojoClassVisitor.findParameterVisitors(new HashSet<>(Arrays.asList(PARAMETER_V3, PARAMETER_V4)));
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotatedClass.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotatedClass.java
index f408d8dc..703abefc 100644
--- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotatedClass.java
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotatedClass.java
@@ -18,10 +18,13 @@
*/
package org.apache.maven.tools.plugin.extractor.annotations.scanner;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.maven.artifact.Artifact;
+import org.apache.maven.tools.plugin.extractor.annotations.datamodel.AfterAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ComponentAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.ExecuteAnnotationContent;
import org.apache.maven.tools.plugin.extractor.annotations.datamodel.MojoAnnotationContent;
@@ -42,6 +45,8 @@ public class MojoAnnotatedClass {
private ExecuteAnnotationContent execute;
+ private List<AfterAnnotationContent> afterAnnotations;
+
/**
* key is field name
*/
@@ -99,6 +104,18 @@ public class MojoAnnotatedClass {
return this;
}
+ public List<AfterAnnotationContent> getAfterAnnotations() {
+ if (this.afterAnnotations == null) {
+ this.afterAnnotations = new ArrayList<>();
+ }
+ return afterAnnotations;
+ }
+
+ public MojoAnnotatedClass setAfterAnnotations(List<AfterAnnotationContent> afterAnnotations) {
+ this.afterAnnotations = afterAnnotations;
+ return this;
+ }
+
public Map<String, ParameterAnnotationContent> getParameters() {
if (this.parameters == null) {
this.parameters = new HashMap<>();
@@ -141,7 +158,11 @@ public class MojoAnnotatedClass {
}
public boolean hasAnnotations() {
- return !(getComponents().isEmpty() && getParameters().isEmpty() && execute == null && mojo == null);
+ return !(getComponents().isEmpty()
+ && getParameters().isEmpty()
+ && execute == null
+ && mojo == null
+ && getAfterAnnotations().isEmpty());
}
public boolean isV4Api() {
@@ -161,6 +182,7 @@ public class MojoAnnotatedClass {
sb.append(", parentClassName='").append(parentClassName).append('\'');
sb.append(", mojo=").append(mojo);
sb.append(", execute=").append(execute);
+ sb.append(", afterAnnotations=").append(afterAnnotations);
sb.append(", parameters=").append(parameters);
sb.append(", components=").append(components);
sb.append(", v4api=").append(v4Api);
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java
index 9495f0b9..aced9059 100644
--- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/MojoAnnotationsScanner.java
@@ -44,7 +44,9 @@ public interface MojoAnnotationsScanner {
Execute.class.getName(),
Deprecated.class.getName(),
V4_API_ANNOTATIONS_PACKAGE + ".Mojo",
- V4_API_ANNOTATIONS_PACKAGE + ".Execute");
+ V4_API_ANNOTATIONS_PACKAGE + ".Execute",
+ V4_API_ANNOTATIONS_PACKAGE + ".After",
+ V4_API_ANNOTATIONS_PACKAGE + ".Afters");
List<String> FIELD_LEVEL_ANNOTATIONS = Arrays.asList(
Parameter.class.getName(),
diff --git a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java
index 9eb420da..2e535a07 100644
--- a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java
+++ b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/visitors/MojoAnnotationVisitor.java
@@ -18,11 +18,15 @@
*/
package org.apache.maven.tools.plugin.extractor.annotations.scanner.visitors;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
/**
* Visitor for annotations.
@@ -35,6 +39,10 @@ public class MojoAnnotationVisitor extends AnnotationVisitor {
private Map<String, Object> annotationValues = new HashMap<>();
+ private List<MojoAnnotationVisitor> nestedAnnotationVisitors;
+
+ private Map<String, MojoAnnotationVisitor> arrayVisitors;
+
MojoAnnotationVisitor(String annotationClassName) {
super(Opcodes.ASM9);
this.annotationClassName = annotationClassName;
@@ -56,11 +64,44 @@ public class MojoAnnotationVisitor extends AnnotationVisitor {
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
- return new MojoAnnotationVisitor(this.annotationClassName);
+ String className = desc != null ? Type.getType(desc).getClassName() : this.annotationClassName;
+ MojoAnnotationVisitor nested = new MojoAnnotationVisitor(className);
+ if (nestedAnnotationVisitors == null) {
+ nestedAnnotationVisitors = new ArrayList<>();
+ }
+ nestedAnnotationVisitors.add(nested);
+ return nested;
}
@Override
- public AnnotationVisitor visitArray(String s) {
- return new MojoAnnotationVisitor(this.annotationClassName);
+ public AnnotationVisitor visitArray(String name) {
+ MojoAnnotationVisitor arrayVisitor = new MojoAnnotationVisitor(this.annotationClassName);
+ if (this.arrayVisitors == null) {
+ this.arrayVisitors = new HashMap<>();
+ }
+ this.arrayVisitors.put(name, arrayVisitor);
+ return arrayVisitor;
+ }
+
+ /**
+ * Returns the sub-visitor created by {@link #visitArray(String)} for the given array attribute.
+ * The sub-visitor's {@link #getNestedAnnotationVisitors()} contains the annotation elements.
+ *
+ * @param name the array attribute name
+ * @return the array sub-visitor, or {@code null} if not visited
+ */
+ public MojoAnnotationVisitor getArrayVisitor(String name) {
+ return arrayVisitors != null ? arrayVisitors.get(name) : null;
+ }
+
+ /**
+ * Returns the nested annotation visitors collected by {@link #visitAnnotation(String, String)}
+ * and by array sub-visitors. Used to extract elements from repeatable annotation containers
+ * such as {@code @Afters}.
+ *
+ * @return list of nested annotation visitors, never {@code null}
+ */
+ public List<MojoAnnotationVisitor> getNestedAnnotationVisitors() {
+ return nestedAnnotationVisitors != null ? nestedAnnotationVisitors : Collections.emptyList();
}
}
diff --git a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedMojoDescriptor.java b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedMojoDescriptor.java
index 7dad0a00..342f0fa1 100644
--- a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedMojoDescriptor.java
+++ b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedMojoDescriptor.java
@@ -18,6 +18,9 @@
*/
package org.apache.maven.tools.plugin;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
@@ -31,6 +34,7 @@ import org.apache.maven.plugin.descriptor.Parameter;
public class ExtendedMojoDescriptor extends MojoDescriptor {
private final boolean containsXhtmlTextValues;
private boolean v4Api;
+ private List<AfterLink> afterLinks;
public ExtendedMojoDescriptor() {
this(false);
@@ -63,6 +67,66 @@ public class ExtendedMojoDescriptor extends MojoDescriptor {
this.v4Api = v4Api;
}
+ /**
+ * Returns the after-links declared via {@code @After} annotations.
+ *
+ * @return unmodifiable list of after-links, never {@code null}
+ * @since 4.0.0
+ */
+ public List<AfterLink> getAfterLinks() {
+ return afterLinks != null ? Collections.unmodifiableList(afterLinks) : Collections.emptyList();
+ }
+
+ /**
+ * Adds an after-link.
+ *
+ * @param afterLink the after-link to add
+ * @since 4.0.0
+ */
+ public void addAfterLink(AfterLink afterLink) {
+ if (this.afterLinks == null) {
+ this.afterLinks = new ArrayList<>();
+ }
+ this.afterLinks.add(afterLink);
+ }
+
+ /**
+ * Holds after-link data from the {@code @After} annotation.
+ * <p>
+ * Mirrors the {@code org.apache.maven.api.plugin.descriptor.AfterLink} model class
+ * from the Maven 4 API without introducing a compile-time dependency on it.
+ *
+ * @since 4.0.0
+ */
+ public static class AfterLink {
+ private final String phase;
+ private final String type;
+ private final String scope;
+
+ public AfterLink(String phase, String type, String scope) {
+ this.phase = phase;
+ this.type = type;
+ this.scope = scope;
+ }
+
+ public String getPhase() {
+ return phase;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ @Override
+ public String toString() {
+ return "AfterLink{phase='" + phase + "', type='" + type + "', scope='" + scope + "'}";
+ }
+ }
+
@Override
public int hashCode() {
return Objects.hash(containsXhtmlTextValues, v4Api);
diff --git a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGenerator.java b/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGenerator.java
index 07ee9956..e1f6ffdc 100644
--- a/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGenerator.java
+++ b/maven-plugin-tools-generators/src/main/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGenerator.java
@@ -332,6 +332,32 @@ public class PluginDescriptorFilesGenerator implements Generator {
GeneratorUtils.element(w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle());
}
+ // ----------------------------------------------------------------------
+ // After links (v4 API only)
+ // ----------------------------------------------------------------------
+
+ if (isV4 && mojoDescriptor instanceof ExtendedMojoDescriptor) {
+ List<ExtendedMojoDescriptor.AfterLink> afterLinks =
+ ((ExtendedMojoDescriptor) mojoDescriptor).getAfterLinks();
+ if (!afterLinks.isEmpty()) {
+ w.startElement("afterLinks");
+ for (ExtendedMojoDescriptor.AfterLink afterLink : afterLinks) {
+ w.startElement("afterLink");
+ if (afterLink.getPhase() != null) {
+ GeneratorUtils.element(w, "phase", afterLink.getPhase());
+ }
+ if (afterLink.getType() != null) {
+ GeneratorUtils.element(w, "type", afterLink.getType());
+ }
+ if (afterLink.getScope() != null) {
+ GeneratorUtils.element(w, "scope", afterLink.getScope());
+ }
+ w.endElement(); // afterLink
+ }
+ w.endElement(); // afterLinks
+ }
+ }
+
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------