This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-plugin-tools.git


The following commit(s) were added to refs/heads/master by this push:
     new 010c9b2e [MPLUGIN-508] Upgrade to 4.0.0-beta-3 (#286)
010c9b2e is described below

commit 010c9b2efe5a4baa264e5b9750e0bfc471590398
Author: Guillaume Nodet <[email protected]>
AuthorDate: Thu Jun 13 08:35:10 2024 +0200

    [MPLUGIN-508] Upgrade to 4.0.0-beta-3 (#286)
---
 .github/workflows/maven-verify.yml                 |   5 +
 .../src/it/v4api/invoker.properties                |   1 +
 .../java/org/apache/maven/its/v4api/FirstMojo.java |  29 +-
 maven-plugin-plugin/src/it/v4api/verify.groovy     |  53 +---
 .../plugin/plugin/DescriptorGeneratorMojo.java     | 157 +++++++++-
 .../src/it/plugin-report-annotations/pom.xml       |   6 -
 .../maven/plugin/plugin/report/PluginReport.java   |   2 +-
 .../EnhancedPluginDescriptorBuilder.java           |   1 -
 .../plugin/descriptor/PluginDescriptorBuilder.java | 325 +++++++++++++++++++++
 maven-plugin-tools-annotations/pom.xml             |   4 -
 .../datamodel/ExecuteAnnotationContent.java        |  15 +
 .../datamodel/MojoAnnotationContent.java           |  21 +-
 .../scanner/DefaultMojoAnnotationsScanner.java     |  17 +-
 .../annotations/ParametersWithGenericsMojo.java    |   3 +
 .../scanner/DefaultMojoAnnotationsScannerTest.java |   6 +
 .../tools/plugin/ExtendedPluginDescriptor.java     |  14 +-
 maven-plugin-tools-generators/pom.xml              |  16 +
 .../generator/PluginDescriptorFilesGenerator.java  |  87 ++++--
 .../src/main/resources/help-class-source-v4.vm     |  33 ++-
 .../generator/AbstractGeneratorTestCase.java       |  72 ++++-
 .../PluginDescriptorFilesGeneratorTest.java        |  47 +--
 pom.xml                                            |  17 +-
 22 files changed, 781 insertions(+), 150 deletions(-)

diff --git a/.github/workflows/maven-verify.yml 
b/.github/workflows/maven-verify.yml
index 932827cf..89d9cfdb 100644
--- a/.github/workflows/maven-verify.yml
+++ b/.github/workflows/maven-verify.yml
@@ -25,3 +25,8 @@ jobs:
   build:
     name: Verify
     uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4
+    with:
+      maven-matrix: '[ "3.6.3", "3.9.7", "4.0.0-beta-3" ]'
+      jdk-matrix: '[ "8", "17", "21" ]'
+      matrix-exclude: '[ { maven: "4.0.0-beta-3", jdk: "8" } ]'
+
diff --git a/maven-plugin-plugin/src/it/v4api/invoker.properties 
b/maven-plugin-plugin/src/it/v4api/invoker.properties
index 43be5b1d..e1b69341 100644
--- a/maven-plugin-plugin/src/it/v4api/invoker.properties
+++ b/maven-plugin-plugin/src/it/v4api/invoker.properties
@@ -15,4 +15,5 @@
 # specific language governing permissions and limitations
 # under the License.
 
+invoker.java.version = 17+
 invoker.goals.1 = clean install
diff --git 
a/maven-plugin-plugin/src/it/v4api/src/main/java/org/apache/maven/its/v4api/FirstMojo.java
 
b/maven-plugin-plugin/src/it/v4api/src/main/java/org/apache/maven/its/v4api/FirstMojo.java
index 879f3cef..abab3cee 100644
--- 
a/maven-plugin-plugin/src/it/v4api/src/main/java/org/apache/maven/its/v4api/FirstMojo.java
+++ 
b/maven-plugin-plugin/src/it/v4api/src/main/java/org/apache/maven/its/v4api/FirstMojo.java
@@ -22,13 +22,12 @@ import java.nio.file.Path;
 
 import org.apache.maven.api.MojoExecution;
 import org.apache.maven.api.Project;
-import org.apache.maven.api.ResolutionScope;
 import org.apache.maven.api.Session;
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.di.Named;
 import org.apache.maven.api.plugin.Log;
 import org.apache.maven.api.plugin.MojoException;
-import org.apache.maven.api.plugin.annotations.Component;
 import org.apache.maven.api.plugin.annotations.Execute;
-import org.apache.maven.api.plugin.annotations.LifecyclePhase;
 import org.apache.maven.api.plugin.annotations.Mojo;
 import org.apache.maven.api.plugin.annotations.Parameter;
 import org.apache.maven.api.services.ArtifactInstaller;
@@ -38,15 +37,12 @@ import org.apache.maven.api.settings.Settings;
  * Test mojo for the v4 api plugin descriptor generation.
  * This mojo is not actually runnable because:
  *  - it's using a custom lifecycle which is not defined
- *  - it has a @Component dependency on ArtifactInstaller (hint=test) which 
does not exist
+ *  - it has a @Inject dependency on ArtifactInstaller (hint=test) which does 
not exist
  *
  * @since 1.2
  */
-@Mojo(
-        name = "first",
-        requiresDependencyResolution = ResolutionScope.TEST,
-        defaultPhase = LifecyclePhase.INTEGRATION_TEST)
-@Execute(phase = LifecyclePhase.GENERATE_SOURCES, lifecycle = "cobertura")
+@Mojo(name = "first", defaultPhase = "integration-test")
+@Execute(phase = "generate-sources", lifecycle = "cobertura")
 public class FirstMojo implements org.apache.maven.api.plugin.Mojo {
 
     /**
@@ -66,23 +62,24 @@ public class FirstMojo implements 
org.apache.maven.api.plugin.Mojo {
     @Parameter(name = "namedParam", alias = "alias")
     private String aliasedParam;
 
-    @Component
+    @Inject
     private Session session;
 
-    @Component
+    @Inject
     private Project project;
 
-    @Component
+    @Inject
     private MojoExecution mojo;
 
-    @Component
+    @Inject
     private Settings settings;
 
-    @Component
+    @Inject
     private Log log;
 
-    @Component(role = ArtifactInstaller.class, hint = "test")
-    private Object custom;
+    @Inject
+    @Named("test")
+    private ArtifactInstaller custom;
 
     public void execute() throws MojoException {}
 }
diff --git a/maven-plugin-plugin/src/it/v4api/verify.groovy 
b/maven-plugin-plugin/src/it/v4api/verify.groovy
index 1405e231..878ab95a 100644
--- a/maven-plugin-plugin/src/it/v4api/verify.groovy
+++ b/maven-plugin-plugin/src/it/v4api/verify.groovy
@@ -24,8 +24,8 @@ assert descriptorFile.isFile()
 
 def pluginDescriptor = new XmlParser().parse( descriptorFile );
 
-assert pluginDescriptor.requiredJavaVersion.text() == '1.8'
-assert pluginDescriptor.requiredMavenVersion.text() == '4.0.0-alpha-4'
+assert pluginDescriptor.requiredJavaVersion.text() == '17'
+assert pluginDescriptor.requiredMavenVersion.text() == '4.0.0-beta-3'
 
 def mojo = pluginDescriptor.mojos.mojo.findAll{ it.goal.text() == "first" }[0]
 
@@ -33,51 +33,12 @@ assert mojo.goal.text() == 'first'
 assert mojo.implementation.text() == 'org.apache.maven.its.v4api.FirstMojo'
 assert mojo.language.text() == 'java'
 assert mojo.description.text().startsWith('Test mojo for the v4 api plugin 
descriptor generation.')
-assert mojo.requiresDependencyResolution.text() == 'test'
-assert mojo.requiresDependencyCollection.text() == ''
-assert mojo.requiresProject.text() == 'true'
-assert mojo.requiresOnline.text() == 'false'
-assert mojo.requiresDirectInvocation.text() == 'false'
+assert mojo.projectRequired.text() == 'true'
+assert mojo.onlineRequired.text() == 'false'
 assert mojo.aggregator.text() == 'false'
-assert mojo.threadSafe.text() == 'false'
 assert mojo.phase.text() == 'integration-test'
 assert mojo.executePhase.text() == 'generate-sources'
 assert mojo.executeLifecycle.text() == 'cobertura'
-assert mojo.v4Api.text() == 'true'
-
-assert mojo.configuration.basedir[0].text() == ''
-assert mojo.configuration.basedir[0].'@implementation' == 'java.nio.file.Path'
-assert mojo.configuration.basedir[0].'@default-value' == '${basedir}'
-
-assert mojo.configuration.touchFile[0].text() == '${first.touchFile}'
-assert mojo.configuration.touchFile[0].'@implementation' == 
'java.nio.file.Path'
-assert mojo.configuration.touchFile[0].'@default-value' == 
'${project.build.directory}/touch.txt'
-
-assert mojo.requirements.requirement.size() == 6
-
-assert mojo.requirements.requirement[0].role.text() == 
'org.apache.maven.api.services.ArtifactInstaller'
-assert mojo.requirements.requirement[0].'role-hint'.text() == 'test'
-assert mojo.requirements.requirement[0].'field-name'.text() == 'custom'
-
-assert mojo.requirements.requirement[1].role.text() == 
'org.apache.maven.api.plugin.Log'
-assert mojo.requirements.requirement[1].'role-hint'.isEmpty()
-assert mojo.requirements.requirement[1].'field-name'.text() == 'log'
-
-assert mojo.requirements.requirement[2].role.text() == 
'org.apache.maven.api.MojoExecution'
-assert mojo.requirements.requirement[2].'role-hint'.isEmpty()
-assert mojo.requirements.requirement[2].'field-name'.text() == 'mojo'
-
-assert mojo.requirements.requirement[3].role.text() == 
'org.apache.maven.api.Project'
-assert mojo.requirements.requirement[3].'role-hint'.isEmpty()
-assert mojo.requirements.requirement[3].'field-name'.text() == 'project'
-
-assert mojo.requirements.requirement[4].role.text() == 
'org.apache.maven.api.Session'
-assert mojo.requirements.requirement[4].'role-hint'.isEmpty()
-assert mojo.requirements.requirement[4].'field-name'.text() == 'session'
-
-assert mojo.requirements.requirement[5].role.text() == 
'org.apache.maven.api.settings.Settings'
-assert mojo.requirements.requirement[5].'role-hint'.isEmpty()
-assert mojo.requirements.requirement[5].'field-name'.text() == 'settings'
 
 assert mojo.parameters.parameter.size() == 3
 
@@ -90,6 +51,8 @@ assert parameter.deprecated.isEmpty()
 assert parameter.required.text() == 'false'
 assert parameter.editable.text() == 'false'
 assert parameter.description.text() == 'Project directory.'
+assert parameter.defaultValue.text() == '${basedir}'
+assert parameter.expression.isEmpty()
 
 parameter = mojo.parameters.parameter.findAll{ it.name.text() == "touchFile" 
}[0]
 assert parameter.name.text() == 'touchFile'
@@ -100,6 +63,8 @@ assert parameter.deprecated.isEmpty()
 assert parameter.required.text() == 'true'
 assert parameter.editable.text() == 'true'
 assert parameter.description.text() == ''
+assert parameter.defaultValue.text() == '${project.build.directory}/touch.txt'
+assert parameter.expression.text() == '${first.touchFile}'
 
 parameter = mojo.parameters.parameter.findAll{ it.name.text() == "namedParam" 
}[0]
 assert parameter.name.text() == 'namedParam'
@@ -110,6 +75,8 @@ assert parameter.deprecated.text() == 'As of 0.2'
 assert parameter.required.text() == 'false'
 assert parameter.editable.text() == 'true'
 assert parameter.description.text() == ''
+assert parameter.defaultValue.isEmpty()
+assert parameter.expression.isEmpty()
 
 // check help mojo source and class
 assert new File( basedir, 
"target/classes/org/apache/maven/its/v4api/HelpMojo.class" ).isFile()
diff --git 
a/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/DescriptorGeneratorMojo.java
 
b/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/DescriptorGeneratorMojo.java
index 862edeae..73a0e561 100644
--- 
a/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/DescriptorGeneratorMojo.java
+++ 
b/maven-plugin-plugin/src/main/java/org/apache/maven/plugin/plugin/DescriptorGeneratorMojo.java
@@ -19,12 +19,16 @@
 package org.apache.maven.plugin.plugin;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.net.URI;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
@@ -32,6 +36,7 @@ import 
org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -39,6 +44,7 @@ import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
+import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
 import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
 import org.apache.maven.tools.plugin.PluginToolsRequest;
 import org.apache.maven.tools.plugin.extractor.ExtractionException;
@@ -48,8 +54,14 @@ import 
org.apache.maven.tools.plugin.generator.PluginDescriptorFilesGenerator;
 import org.apache.maven.tools.plugin.scanner.MojoScanner;
 import org.codehaus.plexus.component.repository.ComponentDependency;
 import org.codehaus.plexus.util.ReaderFactory;
+import org.codehaus.plexus.util.io.CachingOutputStream;
+import org.codehaus.plexus.util.io.CachingWriter;
+import org.objectweb.asm.*;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
+import static org.objectweb.asm.Opcodes.*;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
 /**
  * <p>
  * Generate a plugin descriptor.
@@ -78,6 +90,12 @@ public class DescriptorGeneratorMojo extends 
AbstractGeneratorMojo {
     @Parameter(defaultValue = 
"${project.build.outputDirectory}/META-INF/maven", readonly = true)
     private File outputDirectory;
 
+    /**
+     * The directory where the generated class files will be put.
+     */
+    @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = 
true)
+    private File classesOutputDirectory;
+
     /**
      * The file encoding of the source files.
      *
@@ -269,7 +287,6 @@ public class DescriptorGeneratorMojo extends 
AbstractGeneratorMojo {
     protected BuildContext buildContext;
 
     public void generate() throws MojoExecutionException {
-
         if (!"maven-plugin".equalsIgnoreCase(project.getArtifactId())
                 && project.getArtifactId().toLowerCase().startsWith("maven-")
                 && project.getArtifactId().toLowerCase().endsWith("-plugin")
@@ -352,6 +369,12 @@ public class DescriptorGeneratorMojo extends 
AbstractGeneratorMojo {
             PluginDescriptorFilesGenerator pluginDescriptorGenerator = new 
PluginDescriptorFilesGenerator();
             pluginDescriptorGenerator.execute(outputDirectory, request);
 
+            // Generate the additional factories for v4 mojos
+            generateFactories(request.getPluginDescriptor());
+
+            // Generate index for v4 beans
+            generateIndex();
+
             buildContext.refresh(outputDirectory);
         } catch (GeneratorException e) {
             throw new MojoExecutionException("Error writing plugin 
descriptor", e);
@@ -367,6 +390,128 @@ public class DescriptorGeneratorMojo extends 
AbstractGeneratorMojo {
         }
     }
 
+    private void generateIndex() throws GeneratorException {
+        try {
+            Set<String> diBeans = new TreeSet<>();
+            try (Stream<Path> paths = 
Files.walk(classesOutputDirectory.toPath())) {
+                List<Path> classes = paths.filter(
+                                p -> 
p.getFileName().toString().endsWith(".class"))
+                        .collect(Collectors.toList());
+                for (Path classFile : classes) {
+                    String fileString = classFile.toString();
+                    String className = fileString
+                            .substring(0, fileString.length() - 
".class".length())
+                            .replace('/', '.');
+                    try (InputStream is = Files.newInputStream(classFile)) {
+                        ClassReader rdr = new ClassReader(is);
+                        rdr.accept(
+                                new ClassVisitor(Opcodes.ASM9) {
+                                    String className;
+
+                                    @Override
+                                    public void visit(
+                                            int version,
+                                            int access,
+                                            String name,
+                                            String signature,
+                                            String superName,
+                                            String[] interfaces) {
+                                        super.visit(version, access, name, 
signature, superName, interfaces);
+                                        className = name;
+                                    }
+
+                                    @Override
+                                    public AnnotationVisitor 
visitAnnotation(String descriptor, boolean visible) {
+                                        if 
("Lorg/apache/maven/api/di/Named;".equals(descriptor)) {
+                                            diBeans.add(className.replace('/', 
'.'));
+                                        }
+                                        return null;
+                                    }
+                                },
+                                ClassReader.SKIP_FRAMES | 
ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
+                    }
+
+                    //                    Class<?> clazz = 
project.getClassRealm().loadClass(className);
+                    //                    boolean hasQualifier = 
Stream.of(clazz.getAnnotations())
+                    //                            .flatMap(ann -> 
Stream.of(ann.getClass().getAnnotations()))
+                    //                            .anyMatch(ann -> 
"org.apache.maven.api.di.Qualifier"
+                    //                                    
.equals(ann.annotationType().getName()));
+                    //                    if (hasQualifier) {
+                    //                        diBeans.add(className);
+                    //                    }
+                }
+            }
+            Path path = 
outputDirectory.toPath().resolve("org.apache.maven.api.di.Inject");
+            if (diBeans.isEmpty()) {
+                Files.deleteIfExists(path);
+            } else {
+                String nl = System.lineSeparator();
+                try (CachingWriter w = new CachingWriter(path, 
StandardCharsets.UTF_8)) {
+                    String content = 
diBeans.stream().collect(Collectors.joining(nl, "", nl));
+                    w.write(content);
+                }
+            }
+        } catch (Exception e) {
+            throw new GeneratorException("Unable to generate index for v4 
beans", e);
+        }
+    }
+
+    private void generateFactories(PluginDescriptor pd) throws 
GeneratorException {
+        try {
+            for (MojoDescriptor md : pd.getMojos()) {
+                if (md instanceof ExtendedMojoDescriptor && 
((ExtendedMojoDescriptor) md).isV4Api()) {
+                    generateFactory(md);
+                }
+            }
+        } catch (IOException e) {
+            throw new GeneratorException("Unable to generate factories for v4 
mojos", e);
+        }
+    }
+
+    private void generateFactory(MojoDescriptor md) throws IOException {
+        String mojoClassName = md.getImplementation();
+        String packageName = mojoClassName.substring(0, 
mojoClassName.lastIndexOf('.'));
+        String generatorClassName = 
mojoClassName.substring(mojoClassName.lastIndexOf('.') + 1) + "Factory";
+        String mojoName = md.getId();
+
+        getLog().debug("Generating v4 factory for " + mojoClassName);
+
+        byte[] bin = computeGeneratorClassBytes(packageName, 
generatorClassName, mojoName, mojoClassName);
+
+        try (OutputStream os = new CachingOutputStream(classesOutputDirectory
+                .toPath()
+                .resolve(packageName.replace('.', '/') + "/" + 
generatorClassName + ".class"))) {
+            os.write(bin);
+        }
+    }
+
+    static byte[] computeGeneratorClassBytes(
+            String packageName, String generatorClassName, String mojoName, 
String mojoClassName) {
+        String mojo = mojoClassName.replace('.', '/');
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | 
ClassWriter.COMPUTE_MAXS);
+        cw.visitSource(generatorClassName + ".java", null);
+        AnnotationVisitor av = 
cw.visitAnnotation("Lorg/apache/maven/api/di/Named;", true);
+        av.visit("value", mojoName);
+        av.visitEnd();
+        cw.visitAnnotation("Lorg/apache/maven/api/annotations/Generated;", 
true).visitEnd();
+        cw.visit(
+                V1_8,
+                ACC_PUBLIC + ACC_SUPER + ACC_SYNTHETIC,
+                packageName.replace(".", "/") + "/" + generatorClassName,
+                null,
+                mojo,
+                null);
+        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_SYNTHETIC, 
"<init>", "()V", null, null);
+        mv.visitCode();
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, mojo, "<init>", "()V", 
false);
+        mv.visitInsn(Opcodes.RETURN);
+        mv.visitMaxs(-1, -1);
+        mv.visitEnd();
+        cw.visitEnd();
+        return cw.toByteArray();
+    }
+
     private PluginDescriptor extendPluginDescriptor(PluginToolsRequest 
request) {
         ExtendedPluginDescriptor extendedPluginDescriptor = new 
ExtendedPluginDescriptor(request.getPluginDescriptor());
         
extendedPluginDescriptor.setRequiredJavaVersion(getRequiredJavaVersion(request));
diff --git 
a/maven-plugin-report-plugin/src/it/plugin-report-annotations/pom.xml 
b/maven-plugin-report-plugin/src/it/plugin-report-annotations/pom.xml
index 1f355686..3522d6ab 100644
--- a/maven-plugin-report-plugin/src/it/plugin-report-annotations/pom.xml
+++ b/maven-plugin-report-plugin/src/it/plugin-report-annotations/pom.xml
@@ -55,12 +55,6 @@ under the License.
       <version>@maven3Version@</version>
       <scope>provided</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.plugin-tools</groupId>
-      <artifactId>maven-plugin-annotations</artifactId>
-      <version>@project.version@</version>
-      <scope>provided</scope>
-    </dependency>
     <dependency>
       <groupId>org.apache.maven.reporting</groupId>
       <artifactId>maven-reporting-api</artifactId>
diff --git 
a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
index 5979fe70..6c747f4e 100644
--- 
a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
+++ 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugin/plugin/report/PluginReport.java
@@ -35,13 +35,13 @@ import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.building.ModelBuildingRequest;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Execute;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import 
org.apache.maven.plugins.plugin.descriptor.EnhancedPluginDescriptorBuilder;
+import org.apache.maven.plugins.plugin.descriptor.PluginDescriptorBuilder;
 import org.apache.maven.project.DefaultProjectBuildingRequest;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
diff --git 
a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/EnhancedPluginDescriptorBuilder.java
 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/EnhancedPluginDescriptorBuilder.java
index 2f29303a..7c15b1b6 100644
--- 
a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/EnhancedPluginDescriptorBuilder.java
+++ 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/EnhancedPluginDescriptorBuilder.java
@@ -30,7 +30,6 @@ import java.util.Optional;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.Parameter;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
 import org.apache.maven.plugin.plugin.report.PluginReport;
 import org.apache.maven.rtinfo.RuntimeInformation;
 import org.apache.maven.tools.plugin.EnhancedParameterWrapper;
diff --git 
a/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/PluginDescriptorBuilder.java
 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/PluginDescriptorBuilder.java
new file mode 100644
index 00000000..037fdf6f
--- /dev/null
+++ 
b/maven-plugin-report-plugin/src/main/java/org/apache/maven/plugins/plugin/descriptor/PluginDescriptorBuilder.java
@@ -0,0 +1,325 @@
+/*
+ * 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.plugins.plugin.descriptor;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.Parameter;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.codehaus.plexus.component.repository.ComponentDependency;
+import org.codehaus.plexus.component.repository.ComponentRequirement;
+import org.codehaus.plexus.configuration.PlexusConfiguration;
+import org.codehaus.plexus.configuration.PlexusConfigurationException;
+import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
+import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Build plugin descriptor object from {@code plugin.xml}.
+ *
+ * @author Jason van Zyl
+ */
+public class PluginDescriptorBuilder {
+    public PluginDescriptor build(Reader reader) throws 
PlexusConfigurationException {
+        return build(reader, null);
+    }
+
+    public PluginDescriptor build(Reader reader, String source) throws 
PlexusConfigurationException {
+        PlexusConfiguration c = buildConfiguration(reader);
+
+        PluginDescriptor pluginDescriptor = new PluginDescriptor();
+
+        pluginDescriptor.setSource(source);
+        pluginDescriptor.setGroupId(c.getChild("groupId").getValue());
+        pluginDescriptor.setArtifactId(c.getChild("artifactId").getValue());
+        pluginDescriptor.setVersion(c.getChild("version").getValue());
+        pluginDescriptor.setGoalPrefix(c.getChild("goalPrefix").getValue());
+
+        pluginDescriptor.setName(c.getChild("name").getValue());
+        pluginDescriptor.setDescription(c.getChild("description").getValue());
+
+        String isolatedRealm = c.getChild("isolatedRealm").getValue();
+
+        if (isolatedRealm != null) {
+            
pluginDescriptor.setIsolatedRealm(Boolean.parseBoolean(isolatedRealm));
+        }
+
+        String inheritedByDefault = 
c.getChild("inheritedByDefault").getValue();
+
+        if (inheritedByDefault != null) {
+            
pluginDescriptor.setInheritedByDefault(Boolean.parseBoolean(inheritedByDefault));
+        }
+
+        // 
----------------------------------------------------------------------
+        // Components
+        // 
----------------------------------------------------------------------
+
+        PlexusConfiguration[] mojoConfigurations = 
c.getChild("mojos").getChildren("mojo");
+
+        for (PlexusConfiguration component : mojoConfigurations) {
+            MojoDescriptor mojoDescriptor = 
buildComponentDescriptor(component, pluginDescriptor);
+
+            pluginDescriptor.addMojo(mojoDescriptor);
+        }
+
+        // 
----------------------------------------------------------------------
+        // Dependencies
+        // 
----------------------------------------------------------------------
+
+        PlexusConfiguration[] dependencyConfigurations =
+                c.getChild("dependencies").getChildren("dependency");
+
+        List<ComponentDependency> dependencies = new ArrayList<>();
+
+        for (PlexusConfiguration d : dependencyConfigurations) {
+            ComponentDependency cd = new ComponentDependency();
+
+            cd.setArtifactId(d.getChild("artifactId").getValue());
+
+            cd.setGroupId(d.getChild("groupId").getValue());
+
+            cd.setType(d.getChild("type").getValue());
+
+            cd.setVersion(d.getChild("version").getValue());
+
+            dependencies.add(cd);
+        }
+
+        pluginDescriptor.setDependencies(dependencies);
+
+        return pluginDescriptor;
+    }
+
+    @SuppressWarnings("checkstyle:methodlength")
+    public MojoDescriptor buildComponentDescriptor(PlexusConfiguration c, 
PluginDescriptor pluginDescriptor)
+            throws PlexusConfigurationException {
+        MojoDescriptor mojo = new MojoDescriptor();
+        mojo.setPluginDescriptor(pluginDescriptor);
+
+        mojo.setGoal(c.getChild("goal").getValue());
+
+        mojo.setImplementation(c.getChild("implementation").getValue());
+
+        PlexusConfiguration langConfig = c.getChild("language");
+
+        if (langConfig != null) {
+            mojo.setLanguage(langConfig.getValue());
+        }
+
+        PlexusConfiguration configuratorConfig = c.getChild("configurator");
+
+        if (configuratorConfig != null) {
+            mojo.setComponentConfigurator(configuratorConfig.getValue());
+        }
+
+        PlexusConfiguration composerConfig = c.getChild("composer");
+
+        if (composerConfig != null) {
+            mojo.setComponentComposer(composerConfig.getValue());
+        }
+
+        String since = c.getChild("since").getValue();
+
+        if (since != null) {
+            mojo.setSince(since);
+        }
+
+        PlexusConfiguration deprecated = c.getChild("deprecated", false);
+
+        if (deprecated != null) {
+            mojo.setDeprecated(deprecated.getValue());
+        }
+
+        String phase = c.getChild("phase").getValue();
+
+        if (phase != null) {
+            mojo.setPhase(phase);
+        }
+
+        String executePhase = c.getChild("executePhase").getValue();
+
+        if (executePhase != null) {
+            mojo.setExecutePhase(executePhase);
+        }
+
+        String executeMojo = c.getChild("executeGoal").getValue();
+
+        if (executeMojo != null) {
+            mojo.setExecuteGoal(executeMojo);
+        }
+
+        String executeLifecycle = c.getChild("executeLifecycle").getValue();
+
+        if (executeLifecycle != null) {
+            mojo.setExecuteLifecycle(executeLifecycle);
+        }
+
+        
mojo.setInstantiationStrategy(c.getChild("instantiationStrategy").getValue());
+
+        mojo.setDescription(c.getChild("description").getValue());
+
+        PlexusConfiguration dependencyResolution = 
c.getChild("requiresDependencyResolution", false);
+
+        if (dependencyResolution != null) {
+            
mojo.setDependencyResolutionRequired(dependencyResolution.getValue());
+        }
+
+        PlexusConfiguration dependencyCollection = 
c.getChild("requiresDependencyCollection", false);
+
+        if (dependencyCollection != null) {
+            
mojo.setDependencyCollectionRequired(dependencyCollection.getValue());
+        }
+
+        String directInvocationOnly = 
c.getChild("requiresDirectInvocation").getValue();
+
+        if (directInvocationOnly != null) {
+            
mojo.setDirectInvocationOnly(Boolean.parseBoolean(directInvocationOnly));
+        }
+
+        String requiresProject = c.getChild("requiresProject").getValue();
+
+        if (requiresProject != null) {
+            mojo.setProjectRequired(Boolean.parseBoolean(requiresProject));
+        }
+
+        String requiresReports = c.getChild("requiresReports").getValue();
+
+        if (requiresReports != null) {
+            mojo.setRequiresReports(Boolean.parseBoolean(requiresReports));
+        }
+
+        String aggregator = c.getChild("aggregator").getValue();
+
+        if (aggregator != null) {
+            mojo.setAggregator(Boolean.parseBoolean(aggregator));
+        }
+
+        String requiresOnline = c.getChild("requiresOnline").getValue();
+
+        if (requiresOnline != null) {
+            mojo.setOnlineRequired(Boolean.parseBoolean(requiresOnline));
+        }
+
+        String inheritedByDefault = 
c.getChild("inheritedByDefault").getValue();
+
+        if (inheritedByDefault != null) {
+            
mojo.setInheritedByDefault(Boolean.parseBoolean(inheritedByDefault));
+        }
+
+        String threadSafe = c.getChild("threadSafe").getValue();
+
+        if (threadSafe != null) {
+            mojo.setThreadSafe(Boolean.parseBoolean(threadSafe));
+        }
+
+        // 
----------------------------------------------------------------------
+        // Configuration
+        // 
----------------------------------------------------------------------
+
+        PlexusConfiguration mojoConfig = c.getChild("configuration");
+        mojo.setMojoConfiguration(mojoConfig);
+
+        // 
----------------------------------------------------------------------
+        // Parameters
+        // 
----------------------------------------------------------------------
+
+        PlexusConfiguration[] parameterConfigurations = 
c.getChild("parameters").getChildren("parameter");
+
+        List<Parameter> parameters = new ArrayList<>();
+
+        for (PlexusConfiguration d : parameterConfigurations) {
+            Parameter parameter = new Parameter();
+
+            parameter.setName(d.getChild("name").getValue());
+
+            parameter.setAlias(d.getChild("alias").getValue());
+
+            parameter.setType(d.getChild("type").getValue());
+
+            String required = d.getChild("required").getValue();
+
+            parameter.setRequired(Boolean.parseBoolean(required));
+
+            PlexusConfiguration editableConfig = d.getChild("editable");
+
+            // we need the null check for pre-build legacy plugins...
+            if (editableConfig != null) {
+                String editable = d.getChild("editable").getValue();
+
+                parameter.setEditable(editable == null || 
Boolean.parseBoolean(editable));
+            }
+
+            parameter.setDescription(d.getChild("description").getValue());
+
+            parameter.setDeprecated(d.getChild("deprecated").getValue());
+
+            
parameter.setImplementation(d.getChild("implementation").getValue());
+
+            parameter.setSince(d.getChild("since").getValue());
+
+            PlexusConfiguration paramConfig = 
mojoConfig.getChild(parameter.getName(), false);
+            if (paramConfig != null) {
+                parameter.setExpression(paramConfig.getValue(null));
+                
parameter.setDefaultValue(paramConfig.getAttribute("default-value"));
+            }
+
+            parameters.add(parameter);
+        }
+
+        mojo.setParameters(parameters);
+
+        // TODO this should not need to be handed off...
+
+        // 
----------------------------------------------------------------------
+        // Requirements
+        // 
----------------------------------------------------------------------
+
+        PlexusConfiguration[] requirements = 
c.getChild("requirements").getChildren("requirement");
+
+        for (PlexusConfiguration requirement : requirements) {
+            ComponentRequirement cr = new ComponentRequirement();
+
+            cr.setRole(requirement.getChild("role").getValue());
+
+            cr.setRoleHint(requirement.getChild("role-hint").getValue());
+
+            cr.setFieldName(requirement.getChild("field-name").getValue());
+
+            mojo.addRequirement(cr);
+        }
+
+        return mojo;
+    }
+
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
+    public PlexusConfiguration buildConfiguration(Reader configuration) throws 
PlexusConfigurationException {
+        try {
+            return new 
XmlPlexusConfiguration(Xpp3DomBuilder.build(configuration));
+        } catch (IOException | XmlPullParserException e) {
+            throw new PlexusConfigurationException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/maven-plugin-tools-annotations/pom.xml 
b/maven-plugin-tools-annotations/pom.xml
index 732fdbe6..bff4e926 100644
--- a/maven-plugin-tools-annotations/pom.xml
+++ b/maven-plugin-tools-annotations/pom.xml
@@ -36,10 +36,6 @@
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven</groupId>
-      <artifactId>maven-core</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-model</artifactId>
diff --git 
a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/ExecuteAnnotationContent.java
 
b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/ExecuteAnnotationContent.java
index c1e3c930..a8e7af4e 100644
--- 
a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/ExecuteAnnotationContent.java
+++ 
b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/ExecuteAnnotationContent.java
@@ -19,6 +19,7 @@
 package org.apache.maven.tools.plugin.extractor.annotations.datamodel;
 
 import java.lang.annotation.Annotation;
+import java.util.Objects;
 
 import org.apache.maven.plugins.annotations.Execute;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -57,6 +58,20 @@ public class ExecuteAnnotationContent implements Execute {
     }
 
     public void phase(String phase) {
+        if (phase != null && !phase.isEmpty()) {
+            for (LifecyclePhase p : LifecyclePhase.values()) {
+                if (Objects.equals(phase, p.id()) || Objects.equals(phase, 
p.name())) {
+                    this.phase = p;
+                    this.customPhase = null;
+                    return;
+                }
+            }
+            this.phase = null;
+            this.customPhase = phase;
+        } else {
+            this.phase = null;
+            this.customPhase = null;
+        }
         this.phase = LifecyclePhase.valueOf(phase);
     }
 
diff --git 
a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/MojoAnnotationContent.java
 
b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/MojoAnnotationContent.java
index a3b6f6e8..12196990 100644
--- 
a/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/MojoAnnotationContent.java
+++ 
b/maven-plugin-tools-annotations/src/main/java/org/apache/maven/tools/plugin/extractor/annotations/datamodel/MojoAnnotationContent.java
@@ -19,6 +19,7 @@
 package org.apache.maven.tools.plugin.extractor.annotations.datamodel;
 
 import java.lang.annotation.Annotation;
+import java.util.Objects;
 
 import org.apache.maven.plugins.annotations.InstantiationStrategy;
 import org.apache.maven.plugins.annotations.LifecyclePhase;
@@ -69,7 +70,17 @@ public class MojoAnnotationContent extends AnnotatedContent 
implements Mojo {
     }
 
     public void defaultPhase(String phase) {
-        this.defaultPhase = LifecyclePhase.valueOf(phase);
+        if (phase != null && !phase.isEmpty()) {
+            for (LifecyclePhase p : LifecyclePhase.values()) {
+                if (Objects.equals(phase, p.id()) || Objects.equals(phase, 
p.name())) {
+                    this.defaultPhase = p;
+                    return;
+                }
+            }
+            throw new IllegalArgumentException("Could not find a matching 
phase for " + phase);
+        } else {
+            this.defaultPhase = null;
+        }
     }
 
     @Override
@@ -81,6 +92,10 @@ public class MojoAnnotationContent extends AnnotatedContent 
implements Mojo {
         this.requiresDependencyResolution = 
ResolutionScope.valueOf(requiresDependencyResolution);
     }
 
+    public void dependencyResolutionRequired(String 
dependencyResolutionRequired) {
+        this.requiresDependencyResolution = 
ResolutionScope.valueOf(dependencyResolutionRequired);
+    }
+
     @Override
     public ResolutionScope requiresDependencyCollection() {
         return requiresDependencyCollection;
@@ -117,6 +132,10 @@ public class MojoAnnotationContent extends 
AnnotatedContent implements Mojo {
         this.requiresProject = requiresProject;
     }
 
+    public void projectRequired(boolean requiresProject) {
+        this.requiresProject = requiresProject;
+    }
+
     @Override
     public boolean requiresReports() {
         return requiresReports;
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 a91aea24..75989011 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
@@ -31,6 +31,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.regex.Pattern;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
@@ -70,7 +71,6 @@ public class DefaultMojoAnnotationsScanner extends 
AbstractLogEnabled implements
     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 COMPONENT_V4 = MVN4_API + "Component";
 
     public static final String MOJO_V3 = Mojo.class.getName();
     public static final String EXECUTE_V3 = Execute.class.getName();
@@ -88,15 +88,23 @@ public class DefaultMojoAnnotationsScanner extends 
AbstractLogEnabled implements
         Map<String, MojoAnnotatedClass> mojoAnnotatedClasses = new HashMap<>();
 
         try {
+            String mavenApiVersion = null;
             for (Artifact dependency : request.getDependencies()) {
                 scan(mojoAnnotatedClasses, dependency.getFile(), 
request.getIncludePatterns(), dependency, true);
                 if (request.getMavenApiVersion() == null
                         && dependency.getGroupId().equals("org.apache.maven")
                         && 
(dependency.getArtifactId().equals("maven-plugin-api")
                                 || 
dependency.getArtifactId().equals("maven-api-core"))) {
-                    request.setMavenApiVersion(dependency.getVersion());
+                    String version = dependency.getVersion();
+                    if (mavenApiVersion != null && !Objects.equals(version, 
mavenApiVersion)) {
+                        throw new UnsupportedOperationException("Mixing Maven 
3 and Maven 4 plugins is not supported."
+                                + " Fix your dependencies so that you depend 
either on maven-plugin-api for a Maven 3 plugin,"
+                                + " or maven-api-core for a Maven 4 plugin.");
+                    }
+                    mavenApiVersion = version;
                 }
             }
+            request.setMavenApiVersion(mavenApiVersion);
 
             for (File classDirectory : request.getClassesDirectories()) {
                 scan(
@@ -332,16 +340,13 @@ public class DefaultMojoAnnotationsScanner extends 
AbstractLogEnabled implements
 
             // @Component annotations
             List<MojoFieldVisitor> mojoFieldVisitors =
-                    mojoClassVisitor.findFieldWithAnnotation(new 
HashSet<>(Arrays.asList(COMPONENT_V3, COMPONENT_V4)));
+                    mojoClassVisitor.findFieldWithAnnotation(new 
HashSet<>(Arrays.asList(COMPONENT_V3)));
             for (MojoFieldVisitor mojoFieldVisitor : mojoFieldVisitors) {
                 ComponentAnnotationContent componentAnnotationContent =
                         new 
ComponentAnnotationContent(mojoFieldVisitor.getFieldName());
 
                 Map<String, MojoAnnotationVisitor> annotationVisitorMap = 
mojoFieldVisitor.getAnnotationVisitorMap();
                 MojoAnnotationVisitor annotationVisitor = 
annotationVisitorMap.get(COMPONENT_V3);
-                if (annotationVisitor == null) {
-                    annotationVisitor = annotationVisitorMap.get(COMPONENT_V4);
-                }
 
                 if (annotationVisitor != null) {
                     for (Map.Entry<String, Object> entry :
diff --git 
a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/ParametersWithGenericsMojo.java
 
b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/ParametersWithGenericsMojo.java
index d5796ba5..5de14478 100644
--- 
a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/ParametersWithGenericsMojo.java
+++ 
b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/ParametersWithGenericsMojo.java
@@ -46,6 +46,9 @@ public class ParametersWithGenericsMojo extends AbstractMojo {
     @Parameter
     private Collection<Integer[]> integerArrayCollection;
 
+    @Parameter
+    private Map<String, List<String>> stringListStringMap;
+
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException 
{}
 
diff --git 
a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScannerTest.java
 
b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScannerTest.java
index 83dd04c4..bc8c0723 100644
--- 
a/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScannerTest.java
+++ 
b/maven-plugin-tools-annotations/src/test/java/org/apache/maven/tools/plugin/extractor/annotations/scanner/DefaultMojoAnnotationsScannerTest.java
@@ -132,6 +132,12 @@ class DefaultMojoAnnotationsScannerTest {
         assertNotNull(parameter);
         assertEquals("java.util.List", parameter.getClassName());
         
assertThat(parameter.getTypeParameters()).containsExactly("java.lang.Number");
+
+        parameter = annotatedClass.getParameters().get("stringListStringMap");
+        assertNotNull(parameter);
+        assertEquals("java.util.Map", parameter.getClassName());
+        assertThat(parameter.getTypeParameters())
+                .containsExactly("java.lang.String", 
"java.util.List<java.lang.String>");
     }
 
     @Test
diff --git 
a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedPluginDescriptor.java
 
b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedPluginDescriptor.java
index bb3dc5c0..0ae9e935 100644
--- 
a/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedPluginDescriptor.java
+++ 
b/maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/ExtendedPluginDescriptor.java
@@ -18,7 +18,6 @@
  */
 package org.apache.maven.tools.plugin;
 
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -29,9 +28,7 @@ import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
-import org.apache.maven.plugin.lifecycle.Lifecycle;
 import org.codehaus.plexus.classworlds.realm.ClassRealm;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 /**
  * Extensions to {@link PluginDescriptor} not supported by Maven 3.2.5.
@@ -91,6 +88,12 @@ public class ExtendedPluginDescriptor extends 
PluginDescriptor {
         delegate.addMojo(mojoDescriptor);
     }
 
+    public void addMojos(List<MojoDescriptor> mojos) throws 
DuplicateMojoDescriptorException {
+        for (MojoDescriptor mojoDescriptor : mojos) {
+            addMojo(mojoDescriptor);
+        }
+    }
+
     public String getGroupId() {
         return delegate.getGroupId();
     }
@@ -222,11 +225,6 @@ public class ExtendedPluginDescriptor extends 
PluginDescriptor {
         delegate.setPluginArtifact(pluginArtifact);
     }
 
-    @Override
-    public Lifecycle getLifecycleMapping(String lifecycleId) throws 
IOException, XmlPullParserException {
-        return delegate.getLifecycleMapping(lifecycleId);
-    }
-
     public PluginDescriptor clone() {
         return delegate.clone();
     }
diff --git a/maven-plugin-tools-generators/pom.xml 
b/maven-plugin-tools-generators/pom.xml
index b2af7953..dd1aabe9 100644
--- a/maven-plugin-tools-generators/pom.xml
+++ b/maven-plugin-tools-generators/pom.xml
@@ -54,6 +54,8 @@
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
+      <version>${maven3Version}</version>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
@@ -89,6 +91,15 @@
       <version>${slf4jVersion}</version>
     </dependency>
 
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm-commons</artifactId>
+    </dependency>
+
     <!-- for HTML to plain text conversion -->
     <dependency>
       <groupId>org.jsoup</groupId>
@@ -108,6 +119,11 @@
       <artifactId>junit-jupiter-engine</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven.resolver</groupId>
+      <artifactId>maven-resolver-impl</artifactId>
+      <version>${resolverVersion}</version>
+    </dependency>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-testing</artifactId>
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 c6923de6..ac83bc42 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
@@ -110,6 +110,9 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
             destinationFile.getParentFile().mkdirs();
         }
 
+        String apiVersion = 
request.getPluginDescriptor().getRequiredMavenVersion();
+        boolean isV4 = apiVersion != null && apiVersion.startsWith("4.");
+
         try (Writer writer = new OutputStreamWriter(new 
CachingOutputStream(destinationFile), UTF_8)) {
             XMLWriter w = new PrettyPrintXMLWriter(writer, UTF_8.name(), null);
 
@@ -128,6 +131,13 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
             w.writeMarkup("\n<!-- Generated by maven-plugin-tools " + 
getVersion() + additionalInfo + "-->\n\n");
 
             w.startElement("plugin");
+            if (isV4) {
+                w.addAttribute("xmlns", 
"http://maven.apache.org/PLUGIN/2.0.0";);
+                w.addAttribute("xmlns:xsi", 
"http://www.w3.org/2001/XMLSchema-instance";);
+                w.addAttribute(
+                        "xsi:location",
+                        "http://maven.apache.org/PLUGIN/2.0.0 
https://maven.apache.org/xsd/plugin-2.0.0.xsd";);
+            }
 
             GeneratorUtils.element(w, "name", pluginDescriptor.getName());
 
@@ -178,13 +188,13 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
                 PluginUtils.sortMojos(descriptors);
 
                 for (MojoDescriptor descriptor : descriptors) {
-                    processMojoDescriptor(descriptor, w, type, 
javadocLinkGenerator);
+                    processMojoDescriptor(descriptor, w, type, 
javadocLinkGenerator, isV4);
                 }
             }
 
             w.endElement();
 
-            if (type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
+            if (!isV4 && type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
                 GeneratorUtils.writeDependencies(w, pluginDescriptor);
             }
 
@@ -221,7 +231,8 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
             MojoDescriptor mojoDescriptor,
             XMLWriter w,
             DescriptorType type,
-            JavadocLinkGenerator javadocLinkGenerator) {
+            JavadocLinkGenerator javadocLinkGenerator,
+            boolean isV4) {
         boolean containsXhtmlTextValues = mojoDescriptor instanceof 
ExtendedMojoDescriptor
                 && ((ExtendedMojoDescriptor) 
mojoDescriptor).containsXhtmlTextValues();
 
@@ -251,27 +262,36 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
         //
         // 
----------------------------------------------------------------------
 
-        if 
(StringUtils.isNotEmpty(mojoDescriptor.isDependencyResolutionRequired())) {
-            GeneratorUtils.element(w, "requiresDependencyResolution", 
mojoDescriptor.isDependencyResolutionRequired());
+        if 
(StringUtils.isNotEmpty(mojoDescriptor.getDependencyResolutionRequired())) {
+            GeneratorUtils.element(
+                    w,
+                    isV4 ? "dependencyResolution" : 
"requiresDependencyResolution",
+                    mojoDescriptor.getDependencyResolutionRequired());
         }
 
         // 
----------------------------------------------------------------------
         //
         // 
----------------------------------------------------------------------
 
-        GeneratorUtils.element(w, "requiresDirectInvocation", 
String.valueOf(mojoDescriptor.isDirectInvocationOnly()));
+        GeneratorUtils.element(
+                w,
+                isV4 ? "directInvocationOnly" : "requiresDirectInvocation",
+                String.valueOf(mojoDescriptor.isDirectInvocationOnly()));
 
         // 
----------------------------------------------------------------------
         //
         // 
----------------------------------------------------------------------
 
-        GeneratorUtils.element(w, "requiresProject", 
String.valueOf(mojoDescriptor.isProjectRequired()));
+        GeneratorUtils.element(
+                w, isV4 ? "projectRequired" : "requiresProject", 
String.valueOf(mojoDescriptor.isProjectRequired()));
 
         // 
----------------------------------------------------------------------
         //
         // 
----------------------------------------------------------------------
 
-        GeneratorUtils.element(w, "requiresReports", 
String.valueOf(mojoDescriptor.isRequiresReports()));
+        if (!isV4) {
+            GeneratorUtils.element(w, "requiresReports", 
String.valueOf(mojoDescriptor.isRequiresReports()));
+        }
 
         // 
----------------------------------------------------------------------
         //
@@ -283,7 +303,8 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
         //
         // 
----------------------------------------------------------------------
 
-        GeneratorUtils.element(w, "requiresOnline", 
String.valueOf(mojoDescriptor.isOnlineRequired()));
+        GeneratorUtils.element(
+                w, isV4 ? "onlineRequired" : "requiresOnline", 
String.valueOf(mojoDescriptor.isOnlineRequired()));
 
         // 
----------------------------------------------------------------------
         //
@@ -345,7 +366,7 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
         //
         // 
----------------------------------------------------------------------
 
-        if (StringUtils.isNotEmpty(mojoDescriptor.getComponentComposer())) {
+        if (!isV4 && 
StringUtils.isNotEmpty(mojoDescriptor.getComponentComposer())) {
             w.startElement("composer");
             w.writeText(mojoDescriptor.getComponentComposer());
             w.endElement();
@@ -355,17 +376,22 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
         //
         // 
----------------------------------------------------------------------
 
-        w.startElement("instantiationStrategy");
-        w.writeText(mojoDescriptor.getInstantiationStrategy());
-        w.endElement();
+        if (!isV4) {
+            w.startElement("instantiationStrategy");
+            w.writeText(mojoDescriptor.getInstantiationStrategy());
+            w.endElement();
+        }
 
         // 
----------------------------------------------------------------------
         // Strategy for handling repeated reference to mojo in
         // the calculated (decorated, resolved) execution stack
         // 
----------------------------------------------------------------------
-        w.startElement("executionStrategy");
-        w.writeText(mojoDescriptor.getExecutionStrategy());
-        w.endElement();
+
+        if (!isV4) {
+            w.startElement("executionStrategy");
+            w.writeText(mojoDescriptor.getExecutionStrategy());
+            w.endElement();
+        }
 
         // 
----------------------------------------------------------------------
         //
@@ -407,14 +433,12 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
             ExtendedMojoDescriptor extendedMojoDescriptor = 
(ExtendedMojoDescriptor) mojoDescriptor;
             if (extendedMojoDescriptor.getDependencyCollectionRequired() != 
null) {
                 GeneratorUtils.element(
-                        w, "requiresDependencyCollection", 
extendedMojoDescriptor.getDependencyCollectionRequired());
+                        w,
+                        isV4 ? "dependencyCollection" : 
"requiresDependencyCollection",
+                        
extendedMojoDescriptor.getDependencyCollectionRequired());
             }
-
-            GeneratorUtils.element(w, "threadSafe", 
String.valueOf(extendedMojoDescriptor.isThreadSafe()));
-
-            boolean v4Api = extendedMojoDescriptor.isV4Api();
-            if (v4Api) {
-                GeneratorUtils.element(w, "v4Api", String.valueOf(v4Api));
+            if (!isV4) {
+                GeneratorUtils.element(w, "threadSafe", 
String.valueOf(extendedMojoDescriptor.isThreadSafe()));
             }
         }
 
@@ -495,7 +519,7 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
                         }
                     }
 
-                    if (parameter.getImplementation() != null) {
+                    if (!isV4 && parameter.getImplementation() != null) {
                         GeneratorUtils.element(w, "implementation", 
parameter.getImplementation());
                     }
 
@@ -506,9 +530,18 @@ public class PluginDescriptorFilesGenerator implements 
Generator {
                     GeneratorUtils.element(
                             w, "description", getTextValue(type, 
containsXhtmlTextValues, parameter.getDescription()));
 
-                    if (StringUtils.isNotEmpty(parameter.getDefaultValue())
-                            || 
StringUtils.isNotEmpty(parameter.getExpression())) {
-                        configuration.add(parameter);
+                    if (isV4) {
+                        if (StringUtils.isNotEmpty(parameter.getExpression())) 
{
+                            GeneratorUtils.element(w, "expression", 
parameter.getExpression());
+                        }
+                        if 
(StringUtils.isNotEmpty(parameter.getDefaultValue())) {
+                            GeneratorUtils.element(w, "defaultValue", 
parameter.getDefaultValue());
+                        }
+                    } else {
+                        if (StringUtils.isNotEmpty(parameter.getDefaultValue())
+                                || 
StringUtils.isNotEmpty(parameter.getExpression())) {
+                            configuration.add(parameter);
+                        }
                     }
 
                     w.endElement();
diff --git 
a/maven-plugin-tools-generators/src/main/resources/help-class-source-v4.vm 
b/maven-plugin-tools-generators/src/main/resources/help-class-source-v4.vm
index 0ac5b891..008c5219 100644
--- a/maven-plugin-tools-generators/src/main/resources/help-class-source-v4.vm
+++ b/maven-plugin-tools-generators/src/main/resources/help-class-source-v4.vm
@@ -18,8 +18,8 @@
 package ${helpPackageName};
 #end
 
+import org.apache.maven.api.di.Inject;
 import org.apache.maven.api.plugin.MojoException;
-import org.apache.maven.api.plugin.annotations.Component;
 import org.apache.maven.api.plugin.annotations.Mojo;
 import org.apache.maven.api.plugin.annotations.Parameter;
 import org.apache.maven.api.plugin.Log;
@@ -43,11 +43,11 @@ import java.util.List;
  * Call <code>mvn ${goalPrefix}:help -Ddetail=true 
-Dgoal=&lt;goal-name&gt;</code> to display parameter details.
  * @author maven-plugin-tools
  */
-@Mojo( name = "help", requiresProject = false )
+@Mojo( name = "help", projectRequired = false )
 public class HelpMojo
     implements org.apache.maven.api.plugin.Mojo
 {
-    @Component
+    @Inject
     private Log logger;
 
     /**
@@ -189,6 +189,21 @@ public class HelpMojo
         return getSingleChild( node, elementName ).getTextContent();
     }
 
+    private static String getValueOr( Node node, String elementName, String 
def )
+        throws MojoException
+    {
+        List<Node> namedChild = findNamedChild( node, elementName );
+        if ( namedChild.isEmpty() )
+        {
+            return def;
+        }
+        if ( namedChild.size() > 1 )
+        {
+            throw new MojoException( "Multiple " + elementName + " in 
plugin-help.xml" );
+        }
+        return namedChild.get( 0 ).getTextContent();
+    }
+
     private static Node getSingleChild( Node node, String elementName )
         throws MojoException
     {
@@ -286,10 +301,10 @@ public class HelpMojo
           fieldConfigurationElement =  (Element) findSingleChild( 
configurationElement, parameterName );
         }
 
-        String parameterDefaultValue = "";
-        if ( fieldConfigurationElement != null && 
fieldConfigurationElement.hasAttribute( "default-value" ) )
+        String parameterDefaultValue = getValueOr( parameter, "defaultValue", 
"" );
+        if ( isNotEmpty( parameterDefaultValue ) )
         {
-            parameterDefaultValue = " (Default: " + 
fieldConfigurationElement.getAttribute( "default-value" ) + ")";
+            parameterDefaultValue = " (Default: " + parameterDefaultValue + 
")";
         }
         append( sb, parameterName + parameterDefaultValue, 2 );
         Node deprecated = findSingleChild( parameter, "deprecated" );
@@ -305,9 +320,11 @@ public class HelpMojo
         {
             append( sb, "Required: Yes", 3 );
         }
-        if ( ( fieldConfigurationElement != null ) && isNotEmpty( 
fieldConfigurationElement.getTextContent() ) )
+
+        String parameterExpression = getValueOr( parameter, "expression", "" );
+        if ( isNotEmpty( parameterExpression ) )
         {
-            String property = getPropertyFromExpression( 
fieldConfigurationElement.getTextContent() );
+            String property = getPropertyFromExpression( parameterExpression );
             append( sb, "User property: " + property, 3 );
         }
 
diff --git 
a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/AbstractGeneratorTestCase.java
 
b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/AbstractGeneratorTestCase.java
index f592b631..4b63712f 100644
--- 
a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/AbstractGeneratorTestCase.java
+++ 
b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/AbstractGeneratorTestCase.java
@@ -36,6 +36,7 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
 import org.codehaus.plexus.component.repository.ComponentDependency;
 import org.codehaus.plexus.util.FileUtils;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -105,7 +106,74 @@ public abstract class AbstractGeneratorTestCase {
         extendPluginDescriptor(pluginDescriptor);
         generator.execute(destinationDirectory, new 
DefaultPluginToolsRequest(mavenProject, pluginDescriptor));
 
-        validate(destinationDirectory);
+        validate(destinationDirectory, false);
+
+        FileUtils.deleteDirectory(destinationDirectory);
+    }
+
+    @Test
+    @Disabled
+    public void testGeneratorV4() throws Exception {
+        setupGenerator();
+
+        MojoDescriptor mojoDescriptor = new MojoDescriptor();
+        mojoDescriptor.setGoal("testGoal");
+        
mojoDescriptor.setImplementation("org.apache.maven.tools.plugin.generator.TestMojo");
+        mojoDescriptor.setDependencyResolutionRequired("compile");
+        mojoDescriptor.setSince("mojoSince");
+
+        List<Parameter> params = new ArrayList<>();
+
+        Parameter param = new Parameter();
+        param.setExpression("${project.build.directory}");
+        param.setDefaultValue("</markup-must-be-escaped>");
+        param.setName("dir");
+        param.setRequired(true);
+        param.setType("java.lang.String");
+        param.setDescription("Test parameter description");
+        param.setAlias("some.alias");
+        param.setSince("paramDirSince");
+        params.add(param);
+
+        param = new Parameter();
+        param.setName("withoutSince");
+        param.setType("java.lang.String");
+        params.add(param);
+
+        mojoDescriptor.setParameters(params);
+
+        PluginDescriptor pluginDescriptor = new PluginDescriptor();
+        mojoDescriptor.setPluginDescriptor(pluginDescriptor);
+
+        pluginDescriptor.addMojo(mojoDescriptor);
+
+        pluginDescriptor.setArtifactId("maven-unitTesting-plugin");
+        pluginDescriptor.setGoalPrefix("test");
+
+        ComponentDependency dependency = new ComponentDependency();
+        dependency.setGroupId("testGroup");
+        dependency.setArtifactId("testArtifact");
+        dependency.setVersion("0.0.0");
+
+        
pluginDescriptor.setDependencies(Collections.singletonList(dependency));
+
+        File destinationDirectory =
+                Files.createTempDirectory("testGenerator-outDir").toFile();
+        destinationDirectory.mkdir();
+
+        MavenProject mavenProject = new MavenProject();
+        mavenProject.setGroupId("foo");
+        mavenProject.setArtifactId("bar");
+        Build build = new Build();
+        build.setDirectory(basedir + "/target");
+        build.setOutputDirectory(basedir + "/target");
+        mavenProject.setBuild(build);
+        extendPluginDescriptor(pluginDescriptor);
+        DefaultPluginToolsRequest request = new 
DefaultPluginToolsRequest(mavenProject, pluginDescriptor);
+        pluginDescriptor.setRequiredMavenVersion("4.0.0");
+        generator.execute(destinationDirectory, request);
+
+        validate(destinationDirectory, true);
 
         FileUtils.deleteDirectory(destinationDirectory);
     }
@@ -145,7 +213,7 @@ public abstract class AbstractGeneratorTestCase {
     //
     // ----------------------------------------------------------------------
 
-    protected void validate(File destinationDirectory) throws Exception {
+    protected void validate(File destinationDirectory, boolean isV4) throws 
Exception {
         // empty
     }
 }
diff --git 
a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGeneratorTest.java
 
b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGeneratorTest.java
index eb35539e..98fd2e41 100644
--- 
a/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGeneratorTest.java
+++ 
b/maven-plugin-tools-generators/src/test/java/org/apache/maven/tools/plugin/generator/PluginDescriptorFilesGeneratorTest.java
@@ -60,35 +60,33 @@ public class PluginDescriptorFilesGeneratorTest extends 
AbstractGeneratorTestCas
     }
 
     @Override
-    protected void validate(File destinationDirectory) throws Exception {
+    protected void validate(File destinationDirectory, boolean isV4) throws 
Exception {
         PluginDescriptorBuilder pdb = new PluginDescriptorBuilder();
 
         File pluginDescriptorFile = new File(destinationDirectory, 
"plugin.xml");
 
         String pd = readFile(pluginDescriptorFile);
 
+        System.err.println(pd);
+
         PluginDescriptor pluginDescriptor = pdb.build(new StringReader(pd));
 
         assertEquals(1, pluginDescriptor.getMojos().size());
 
         MojoDescriptor mojoDescriptor = pluginDescriptor.getMojos().get(0);
 
-        checkMojo(mojoDescriptor);
+        checkMojo(mojoDescriptor, isV4);
 
         // 
----------------------------------------------------------------------
         // Dependencies
         // 
----------------------------------------------------------------------
 
-        List<ComponentDependency> dependencies = 
pluginDescriptor.getDependencies();
-
-        checkDependency("testGroup", "testArtifact", "0.0.0", 
dependencies.get(0));
+        if (!isV4) {
+            List<ComponentDependency> dependencies = 
pluginDescriptor.getDependencies();
 
-        assertEquals(1, dependencies.size());
-
-        ComponentDependency dependency = dependencies.get(0);
-        assertEquals("testGroup", dependency.getGroupId());
-        assertEquals("testArtifact", dependency.getArtifactId());
-        assertEquals("0.0.0", dependency.getVersion());
+            assertEquals(1, dependencies.size());
+            checkDependency("testGroup", "testArtifact", "0.0.0", 
dependencies.get(0));
+        }
     }
 
     private String readFile(File pluginDescriptorFile) throws IOException {
@@ -105,15 +103,19 @@ public class PluginDescriptorFilesGeneratorTest extends 
AbstractGeneratorTestCas
         return sWriter.toString();
     }
 
-    private void checkMojo(MojoDescriptor mojoDescriptor) {
+    private void checkMojo(MojoDescriptor mojoDescriptor, boolean isV4) {
         assertEquals("test:testGoal", mojoDescriptor.getFullGoalName());
 
         assertEquals("org.apache.maven.tools.plugin.generator.TestMojo", 
mojoDescriptor.getImplementation());
 
         // The following should be defaults
-        assertEquals("per-lookup", mojoDescriptor.getInstantiationStrategy());
+        if (!isV4) {
+            assertEquals("per-lookup", 
mojoDescriptor.getInstantiationStrategy());
+        }
 
-        assertNotNull(mojoDescriptor.isDependencyResolutionRequired());
+        if (!isV4) {
+            assertNotNull(mojoDescriptor.getDependencyResolutionRequired());
+        }
 
         // check the default parameter
         checkParameter(mojoDescriptor.getParameters().get(0));
@@ -124,12 +126,17 @@ public class PluginDescriptorFilesGeneratorTest extends 
AbstractGeneratorTestCas
         assertEquals("parameterWithGenerics", parameterWithGenerics.getName());
         assertEquals("java.util.Collection", parameterWithGenerics.getType());
 
-        PlexusConfiguration configurations = 
mojoDescriptor.getMojoConfiguration();
-        assertNotNull(configurations);
-        PlexusConfiguration configuration = 
configurations.getChild("parameterWithGenerics");
-        assertEquals("java.util.Collection", 
configuration.getAttribute("implementation"));
-        assertEquals("a,b,c", configuration.getAttribute("default-value"));
-        assertEquals("${customParam}", configuration.getValue());
+        if (isV4) {
+            assertEquals("${customParam}", 
parameterWithGenerics.getExpression());
+            assertEquals("a,b,c", parameterWithGenerics.getDefaultValue());
+        } else {
+            PlexusConfiguration configurations = 
mojoDescriptor.getMojoConfiguration();
+            assertNotNull(configurations);
+            PlexusConfiguration configuration = 
configurations.getChild("parameterWithGenerics");
+            assertEquals("java.util.Collection", 
configuration.getAttribute("implementation"));
+            assertEquals("a,b,c", configuration.getAttribute("default-value"));
+            assertEquals("${customParam}", configuration.getValue());
+        }
     }
 
     private void checkParameter(Parameter parameter) {
diff --git a/pom.xml b/pom.xml
index a4cfceb6..4def9de6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,7 +90,7 @@
   <properties>
     <javaVersion>8</javaVersion>
     <pluginTestingHarnessVersion>3.3.0</pluginTestingHarnessVersion>
-    <maven4Version>4.0.0-alpha-4</maven4Version>
+    <maven4Version>4.0.0-beta-3</maven4Version>
     <maven3Version>3.9.6</maven3Version>
     <resolverVersion>1.9.18</resolverVersion>
     <slf4jVersion>1.7.36</slf4jVersion>
@@ -308,6 +308,21 @@
   <build>
     <pluginManagement>
       <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-enforcer-plugin</artifactId>
+          <configuration>
+            <rules>
+              <enforceBytecodeVersion>
+                <maxJdkVersion>${maven.compiler.target}</maxJdkVersion>
+                <excludes>
+                  <exclude>org.apache.maven:*</exclude>
+                </excludes>
+              </enforceBytecodeVersion>
+            </rules>
+            <fail>true</fail>
+          </configuration>
+        </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-javadoc-plugin</artifactId>

Reply via email to