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-testing.git

commit c6ef221ab068ee487e1c535aa2b8925ecc4f0db6
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Nov 28 18:28:40 2023 +0100

    [MPLUGINTESTING-94] Switch to Maven 4.0.0-beta-3
---
 .github/workflows/maven-verify.yml                 |    2 +-
 maven-plugin-testing-harness/pom.xml               |   46 +-
 .../maven/api/di/testing/MavenDIExtension.java     |  150 +++
 .../testing/MavenDITest.java}                      |   18 +-
 .../testing/{MojoParameters.java => Basedir.java}  |    6 +-
 .../maven/api/plugin/testing/InjectMojo.java       |    6 +-
 .../maven/api/plugin/testing/MojoExtension.java    |  598 ++++++++---
 .../maven/api/plugin/testing/MojoParameter.java    |    2 +
 .../maven/api/plugin/testing/MojoParameters.java   |    2 +
 .../testing/ResolverExpressionEvaluatorStub.java   |   12 +-
 .../api/plugin/testing/stubs/ArtifactStub.java     |   23 +-
 .../plugin/testing/stubs/MojoExecutionStub.java    |   67 +-
 .../maven/api/plugin/testing/stubs/PluginStub.java |  102 ++
 .../api/plugin/testing/stubs/ProjectStub.java      |  152 ++-
 .../testing/stubs/RepositorySystemSupplier.java    | 1069 ++++++++++++++++++++
 .../api/plugin/testing/stubs/SessionStub.java      |  203 +++-
 .../apache/maven/api/di/testing/SimpleDITest.java  |   50 +
 .../plugin/testing/ExpressionEvaluatorTest.java    |   38 +-
 .../META-INF/maven/org.apache.maven.api.di.Inject  |   18 +-
 .../src/test/resources/META-INF/maven/plugin.xml   |   41 +-
 pom.xml                                            |    6 +-
 21 files changed, 2272 insertions(+), 339 deletions(-)

diff --git a/.github/workflows/maven-verify.yml 
b/.github/workflows/maven-verify.yml
index fa7dc4b..9177951 100644
--- a/.github/workflows/maven-verify.yml
+++ b/.github/workflows/maven-verify.yml
@@ -26,4 +26,4 @@ jobs:
     name: Verify
     uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4
     with:
-      jdk-matrix: '[ "8", "11", "17" ]'
+      jdk-matrix: '[ "17", "21" ]'
diff --git a/maven-plugin-testing-harness/pom.xml 
b/maven-plugin-testing-harness/pom.xml
index beaa39d..a1d4e4d 100644
--- a/maven-plugin-testing-harness/pom.xml
+++ b/maven-plugin-testing-harness/pom.xml
@@ -35,7 +35,7 @@ under the License.
       <dependency>
         <groupId>org.junit</groupId>
         <artifactId>junit-bom</artifactId>
-        <version>5.10.0</version>
+        <version>5.10.1</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
@@ -62,18 +62,33 @@ under the License.
       <version>${mavenVersion}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-model-builder</artifactId>
+      <version>${mavenVersion}</version>
+      <scope>provided</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-plugin-api</artifactId>
       <version>${mavenVersion}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-xml-impl</artifactId>
+      <version>${mavenVersion}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-resolver-provider</artifactId>
       <version>${mavenVersion}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.eclipse.sisu</groupId>
+      <artifactId>org.eclipse.sisu.plexus</artifactId>
+    </dependency>
 
     <!-- plexus -->
     <dependency>
@@ -85,7 +100,7 @@ under the License.
       <!-- correct version must be provided by project depends on Maven 
version -->
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-xml</artifactId>
-      <version>4.0.0</version>
+      <version>4.0.3</version>
       <optional>true</optional>
     </dependency>
     <dependency>
@@ -97,14 +112,17 @@ under the License.
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-testing</artifactId>
-      <version>1.1.0</version>
-      <exclusions>
-        <exclusion>
-          <!-- we need as optional dependencies -->
-          <groupId>org.junit.jupiter</groupId>
-          <artifactId>junit-jupiter-api</artifactId>
-        </exclusion>
-      </exclusions>
+      <version>1.3.0</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <version>6.0.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.ow2.asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>9.6</version>
     </dependency>
     <dependency>
       <!-- newer version is required by plexus-testing -->
@@ -112,12 +130,6 @@ under the License.
       <artifactId>guava</artifactId>
       <version>32.0.1-jre</version>
     </dependency>
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>4.13.2</version>
-      <optional>true</optional>
-    </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
@@ -131,7 +143,7 @@ under the License.
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-simple</artifactId>
-      <version>1.7.36</version>
+      <version>2.0.13</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java
new file mode 100644
index 0000000..c6f3b86
--- /dev/null
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di.testing;
+
+import java.io.*;
+
+import org.apache.maven.di.Injector;
+import org.apache.maven.di.Key;
+import org.apache.maven.di.impl.DIException;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * This is a slightly modified version of the original plexus class
+ * available at 
https://raw.githubusercontent.com/codehaus-plexus/plexus-containers/master/plexus-container-default/
+ *              src/main/java/org/codehaus/plexus/PlexusTestCase.java
+ * in order to migrate the tests to JUnit 4.
+ *
+ * @author Jason van Zyl
+ * @author <a href="mailto:[email protected]";>Trygve Laugst&oslash;l</a>
+ * @author <a href="mailto:[email protected]";>Michal Maczka</a>
+ * @author Guillaume Nodet
+ */
+public class MavenDIExtension implements BeforeEachCallback, AfterEachCallback 
{
+    protected static ExtensionContext context;
+    protected Injector injector;
+    protected static String basedir;
+
+    @Override
+    public void beforeEach(ExtensionContext context) throws Exception {
+        basedir = getBasedir();
+
+        setContext(context);
+
+        getInjector().bindInstance((Class<Object>) 
context.getRequiredTestClass(), context.getRequiredTestInstance());
+        getInjector().injectInstance(context.getRequiredTestInstance());
+    }
+
+    protected void setContext(ExtensionContext context) {
+        this.context = context;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void setupContainer() {
+        try {
+            injector = Injector.create();
+            injector.bindInstance(ExtensionContext.class, this.context);
+            
injector.discover(this.context.getRequiredTestClass().getClassLoader());
+            injector.bindInstance(Injector.class, injector);
+            injector.bindInstance((Class) this.context.getRequiredTestClass(), 
this.context.getRequiredTestInstance());
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Failed to create DI 
injector.", e);
+        }
+    }
+
+    @Override
+    public void afterEach(ExtensionContext context) throws Exception {
+        if (injector != null) {
+            // TODO: implement
+            // injector.dispose();
+            injector = null;
+        }
+    }
+
+    public Injector getInjector() {
+        if (injector == null) {
+            setupContainer();
+        }
+
+        return injector;
+    }
+
+    // ----------------------------------------------------------------------
+    // Container access
+    // ----------------------------------------------------------------------
+
+    protected <T> T lookup(Class<T> componentClass) throws DIException {
+        return getInjector().getInstance(componentClass);
+    }
+
+    protected <T> T lookup(Class<T> componentClass, String roleHint) throws 
DIException {
+        return getInjector().getInstance(Key.ofType(componentClass, roleHint));
+    }
+
+    protected <T> T lookup(Class<T> componentClass, Object qualifier) throws 
DIException {
+        return getInjector().getInstance(Key.ofType(componentClass, 
qualifier));
+    }
+
+    protected void release(Object component) throws DIException {
+        // TODO: implement
+        // getInjector().release(component);
+    }
+
+    // ----------------------------------------------------------------------
+    // Helper methods for sub classes
+    // ----------------------------------------------------------------------
+
+    public static File getTestFile(String path) {
+        return new File(getBasedir(), path);
+    }
+
+    public static File getTestFile(String basedir, String path) {
+        File basedirFile = new File(basedir);
+
+        if (!basedirFile.isAbsolute()) {
+            basedirFile = getTestFile(basedir);
+        }
+
+        return new File(basedirFile, path);
+    }
+
+    public static String getTestPath(String path) {
+        return getTestFile(path).getAbsolutePath();
+    }
+
+    public static String getTestPath(String basedir, String path) {
+        return getTestFile(basedir, path).getAbsolutePath();
+    }
+
+    public static String getBasedir() {
+        if (basedir != null) {
+            return basedir;
+        }
+
+        basedir = System.getProperty("basedir");
+
+        if (basedir == null) {
+            basedir = new File("").getAbsolutePath();
+        }
+
+        return basedir;
+    }
+}
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java
similarity index 77%
copy from 
maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
copy to 
maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java
index 2a28f48..ddc6d2f 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java
@@ -16,19 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.maven.api.plugin.testing;
+package org.apache.maven.api.di.testing;
 
-import java.lang.annotation.Repeatable;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.ExtendWith;
 
 /**
- * Mojo parameter
+ * Plexus test
  */
 @Retention(RetentionPolicy.RUNTIME)
-@Repeatable(MojoParameters.class)
-public @interface MojoParameter {
-    String name();
-
-    String value();
-}
+@ExtendWith(MavenDIExtension.class)
+@Target(ElementType.TYPE)
+public @interface MavenDITest {}
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java
similarity index 90%
copy from 
maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
copy to 
maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java
index 434abe1..69eaba2 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.api.plugin.testing;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -25,6 +26,7 @@ import java.lang.annotation.RetentionPolicy;
  * Mojo parameters container
  */
 @Retention(RetentionPolicy.RUNTIME)
-public @interface MojoParameters {
-    MojoParameter[] value();
+@Inherited
+public @interface Basedir {
+    String value() default "";
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java
index e094d06..272eb9b 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.api.plugin.testing;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -25,11 +26,10 @@ import java.lang.annotation.RetentionPolicy;
  *
  */
 @Retention(RetentionPolicy.RUNTIME)
+@Inherited
 public @interface InjectMojo {
 
     String goal();
 
-    String pom();
-
-    boolean empty() default false;
+    String pom() default "";
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
index b217fab..0147261 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
@@ -18,68 +18,75 @@
  */
 package org.apache.maven.api.plugin.testing;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.StringReader;
+import java.io.*;
 import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.Field;
 import java.net.URL;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import com.google.inject.internal.ProviderMethodsModule;
 import org.apache.maven.api.MojoExecution;
 import org.apache.maven.api.Project;
 import org.apache.maven.api.Session;
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Priority;
+import org.apache.maven.api.di.Provides;
+import org.apache.maven.api.di.Singleton;
+import org.apache.maven.api.di.testing.MavenDIExtension;
+import org.apache.maven.api.model.Build;
+import org.apache.maven.api.model.ConfigurationContainer;
+import org.apache.maven.api.model.Model;
 import org.apache.maven.api.plugin.Log;
 import org.apache.maven.api.plugin.Mojo;
+import org.apache.maven.api.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.api.plugin.descriptor.Parameter;
+import org.apache.maven.api.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.api.plugin.testing.stubs.*;
+import org.apache.maven.api.services.ArtifactDeployer;
+import org.apache.maven.api.services.ArtifactFactory;
+import org.apache.maven.api.services.ArtifactInstaller;
+import org.apache.maven.api.services.ArtifactManager;
+import org.apache.maven.api.services.LocalRepositoryManager;
+import org.apache.maven.api.services.ProjectBuilder;
+import org.apache.maven.api.services.ProjectManager;
+import org.apache.maven.api.services.RepositoryFactory;
+import org.apache.maven.api.services.VersionParser;
+import org.apache.maven.api.services.xml.ModelXmlFactory;
 import org.apache.maven.api.xml.XmlNode;
 import org.apache.maven.configuration.internal.EnhancedComponentConfigurator;
+import org.apache.maven.di.Injector;
+import org.apache.maven.di.Key;
+import org.apache.maven.di.impl.DIException;
 import org.apache.maven.internal.impl.DefaultLog;
+import org.apache.maven.internal.impl.InternalSession;
+import org.apache.maven.internal.impl.model.DefaultModelPathTranslator;
+import org.apache.maven.internal.impl.model.DefaultPathTranslator;
 import org.apache.maven.internal.xml.XmlNodeImpl;
+import org.apache.maven.internal.xml.XmlPlexusConfiguration;
 import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
+import org.apache.maven.model.v4.MavenMerger;
+import org.apache.maven.model.v4.MavenStaxReader;
 import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4;
-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.codehaus.plexus.DefaultPlexusContainer;
-import org.codehaus.plexus.PlexusContainer;
-import org.codehaus.plexus.component.configurator.ComponentConfigurator;
+import org.apache.maven.plugin.descriptor.io.PluginDescriptorStaxReader;
 import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
 import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import 
org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator;
-import org.codehaus.plexus.component.repository.ComponentDescriptor;
-import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
-import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
-import org.codehaus.plexus.testing.PlexusExtension;
-import org.codehaus.plexus.util.InterpolationFilterReader;
-import org.codehaus.plexus.util.ReaderFactory;
 import org.codehaus.plexus.util.ReflectionUtils;
 import org.codehaus.plexus.util.xml.XmlStreamReader;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
-import org.junit.jupiter.api.extension.ExtensionContext;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.api.extension.ParameterResolutionException;
-import org.junit.jupiter.api.extension.ParameterResolver;
+import org.eclipse.aether.RepositorySystem;
+import org.junit.jupiter.api.extension.*;
+import org.junit.platform.commons.support.AnnotationSupport;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Objects.requireNonNull;
+
 /**
  * JUnit extension to help testing Mojos. The extension should be 
automatically registered
  * by adding the {@link MojoTest} annotation on the test class.
@@ -87,8 +94,25 @@ import org.slf4j.LoggerFactory;
  * @see MojoTest
  * @see InjectMojo
  * @see MojoParameter
+ * @see Basedir
  */
-public class MojoExtension extends PlexusExtension implements 
ParameterResolver {
+public class MojoExtension extends MavenDIExtension implements 
ParameterResolver, BeforeEachCallback {
+
+    protected static String pluginBasedir;
+    protected static String basedir;
+
+    public static String getTestId() {
+        return context.getRequiredTestClass().getSimpleName() + "-"
+                + context.getRequiredTestMethod().getName();
+    }
+
+    public static String getBasedir() {
+        return requireNonNull(basedir != null ? basedir : 
MavenDIExtension.basedir);
+    }
+
+    public static String getPluginBasedir() {
+        return requireNonNull(pluginBasedir);
+    }
 
     @Override
     public boolean supportsParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext)
@@ -101,115 +125,382 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
     public Object resolveParameter(ParameterContext parameterContext, 
ExtensionContext extensionContext)
             throws ParameterResolutionException {
         try {
-            InjectMojo injectMojo = parameterContext
-                    .findAnnotation(InjectMojo.class)
-                    .orElseGet(() -> 
parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class));
-
-            Set<MojoParameter> mojoParameters =
-                    new 
HashSet<>(parameterContext.findRepeatableAnnotations(MojoParameter.class));
-
-            
Optional.ofNullable(parameterContext.getDeclaringExecutable().getAnnotation(MojoParameter.class))
-                    .ifPresent(mojoParameters::add);
-
-            
Optional.ofNullable(parameterContext.getDeclaringExecutable().getAnnotation(MojoParameters.class))
-                    .map(MojoParameters::value)
-                    .map(Arrays::asList)
-                    .ifPresent(mojoParameters::addAll);
-
             Class<?> holder = parameterContext.getTarget().get().getClass();
             PluginDescriptor descriptor = extensionContext
                     .getStore(ExtensionContext.Namespace.GLOBAL)
                     .get(PluginDescriptor.class, PluginDescriptor.class);
-            return lookupMojo(holder, injectMojo, mojoParameters, descriptor);
+            Model model =
+                    
extensionContext.getStore(ExtensionContext.Namespace.GLOBAL).get(Model.class, 
Model.class);
+            InjectMojo parameterInjectMojo =
+                    
parameterContext.getAnnotatedElement().getAnnotation(InjectMojo.class);
+            String goal;
+            if (parameterInjectMojo != null) {
+                String pom = parameterInjectMojo.pom();
+                if (pom != null && !pom.isEmpty()) {
+                    try (Reader r = openPomUrl(holder, pom, new Path[1])) {
+                        Model localModel = new MavenStaxReader().read(r);
+                        model = new MavenMerger().merge(localModel, model, 
false, null);
+                        model = new DefaultModelPathTranslator(new 
DefaultPathTranslator())
+                                .alignToBaseDirectory(model, 
Paths.get(getBasedir()), null);
+                    }
+                }
+                goal = parameterInjectMojo.goal();
+            } else {
+                InjectMojo methodInjectMojo = AnnotationSupport.findAnnotation(
+                                parameterContext.getDeclaringExecutable(), 
InjectMojo.class)
+                        .orElse(null);
+                if (methodInjectMojo != null) {
+                    goal = methodInjectMojo.goal();
+                } else {
+                    goal = getGoalFromMojoImplementationClass(
+                            parameterContext.getParameter().getType());
+                }
+            }
+
+            Set<MojoParameter> mojoParameters = new LinkedHashSet<>();
+            for (AnnotatedElement ae :
+                    Arrays.asList(parameterContext.getDeclaringExecutable(), 
parameterContext.getAnnotatedElement())) {
+                
mojoParameters.addAll(AnnotationSupport.findRepeatableAnnotations(ae, 
MojoParameter.class));
+            }
+            String[] coord = mojoCoordinates(goal);
+
+            XmlNode pluginConfiguration = 
model.getBuild().getPlugins().stream()
+                    .filter(p ->
+                            Objects.equals(p.getGroupId(), coord[0]) && 
Objects.equals(p.getArtifactId(), coord[1]))
+                    .map(ConfigurationContainer::getConfiguration)
+                    .findFirst()
+                    .orElseGet(() -> new XmlNodeImpl("config"));
+            List<XmlNode> children = mojoParameters.stream()
+                    .map(mp -> new XmlNodeImpl(mp.name(), mp.value()))
+                    .collect(Collectors.toList());
+            XmlNode config = new XmlNodeImpl("configuration", null, null, 
children, null);
+            pluginConfiguration = XmlNode.merge(config, pluginConfiguration);
+
+            // load default config
+            // pluginkey = groupId : artifactId : version : goal
+            Mojo mojo = lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + 
coord[2] + ":" + coord[3]);
+            for (MojoDescriptor mojoDescriptor : descriptor.getMojos()) {
+                if (Objects.equals(mojoDescriptor.getGoal(), coord[3])) {
+                    if (pluginConfiguration != null) {
+                        pluginConfiguration = 
finalizeConfig(pluginConfiguration, mojoDescriptor);
+                    }
+                }
+            }
+
+            Session session = getInjector().getInstance(Session.class);
+            Project project = getInjector().getInstance(Project.class);
+            MojoExecution mojoExecution = 
getInjector().getInstance(MojoExecution.class);
+            ExpressionEvaluator evaluator = new WrapEvaluator(
+                    getInjector(), new 
PluginParameterExpressionEvaluatorV4(session, project, mojoExecution));
+
+            EnhancedComponentConfigurator configurator = new 
EnhancedComponentConfigurator();
+            configurator.configureComponent(
+                    mojo, new XmlPlexusConfiguration(pluginConfiguration), 
evaluator, null, null);
+            return mojo;
         } catch (Exception e) {
-            throw new ParameterResolutionException("Unable to resolve 
parameter", e);
+            throw new ParameterResolutionException("Unable to resolve mojo", 
e);
         }
     }
 
+    /**
+     * The @Mojo annotation is only retained in the class file, not at runtime,
+     * so we need to actually read the class file with ASM to find the 
annotation and
+     * the goal.
+     */
+    private static String getGoalFromMojoImplementationClass(Class<?> cl) 
throws IOException {
+        return cl.getAnnotation(Named.class).value();
+    }
+
     @Override
     public void beforeEach(ExtensionContext context) throws Exception {
-        // TODO provide protected setters in PlexusExtension
-        Field field = PlexusExtension.class.getDeclaredField("basedir");
-        field.setAccessible(true);
-        field.set(null, getBasedir());
-        field = PlexusExtension.class.getDeclaredField("context");
-        field.setAccessible(true);
-        field.set(this, context);
-
-        getContainer().addComponent(getContainer(), 
PlexusContainer.class.getName());
-
-        ((DefaultPlexusContainer) 
getContainer()).addPlexusInjector(Collections.emptyList(), binder -> {
-            
binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance()));
-            binder.requestInjection(context.getRequiredTestInstance());
-            binder.bind(Log.class).toInstance(new 
DefaultLog(LoggerFactory.getLogger("anonymous")));
-        });
+        if (pluginBasedir == null) {
+            pluginBasedir = MavenDIExtension.getBasedir();
+        }
+        basedir = AnnotationSupport.findAnnotation(context.getElement().get(), 
Basedir.class)
+                .map(Basedir::value)
+                .orElse(pluginBasedir);
+        if (basedir != null) {
+            if (basedir.isEmpty()) {
+                basedir = pluginBasedir + "/target/tests/"
+                        + context.getRequiredTestClass().getSimpleName() + "/"
+                        + context.getRequiredTestMethod().getName();
+            } else {
+                basedir = basedir.replace("${basedir}", pluginBasedir);
+            }
+        }
 
-        Map<Object, Object> map = getContainer().getContext().getContextData();
+        setContext(context);
+
+        /*
+           
binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance()));
+           binder.requestInjection(context.getRequiredTestInstance());
+           binder.bind(Log.class).toInstance(new 
DefaultLog(LoggerFactory.getLogger("anonymous")));
+           binder.bind(ExtensionContext.class).toInstance(context);
+           // Load maven 4 api Services interfaces and try to bind them to the 
(possible) mock instances
+           // returned by the (possibly) mock InternalSession
+           try {
+               for (ClassPath.ClassInfo clazz :
+                       ClassPath.from(getClassLoader()).getAllClasses()) {
+                   if 
("org.apache.maven.api.services".equals(clazz.getPackageName())) {
+                       Class<?> load = clazz.load();
+                       if (Service.class.isAssignableFrom(load)) {
+                           Class<Service> svc = (Class) load;
+                           binder.bind(svc).toProvider(() -> {
+                               try {
+                                   return getContainer()
+                                           .lookup(InternalSession.class)
+                                           .getService(svc);
+                               } catch (ComponentLookupException e) {
+                                   throw new RuntimeException("Unable to 
lookup service " + svc.getName());
+                               }
+                           });
+                       }
+                   }
+               }
+           } catch (Exception e) {
+               throw new RuntimeException("Unable to bind session services", 
e);
+           }
+
+        */
+
+        Path basedirPath = Paths.get(getBasedir());
+
+        InjectMojo mojo = 
AnnotationSupport.findAnnotation(context.getElement().get(), InjectMojo.class)
+                .orElse(null);
+        Model defaultModel = Model.newBuilder()
+                .groupId("myGroupId")
+                .artifactId("myArtifactId")
+                .version("1.0-SNAPSHOT")
+                .packaging("jar")
+                .build(Build.newBuilder()
+                        .directory(basedirPath.resolve("target").toString())
+                        
.outputDirectory(basedirPath.resolve("target/classes").toString())
+                        
.sourceDirectory(basedirPath.resolve("src/main/java").toString())
+                        .testSourceDirectory(
+                                
basedirPath.resolve("src/test/java").toString())
+                        .testOutputDirectory(
+                                
basedirPath.resolve("target/test-classes").toString())
+                        .build())
+                .build();
+        Path[] modelPath = new Path[] {null};
+        Model tmodel = null;
+        if (mojo != null) {
+            String pom = mojo.pom();
+            if (pom != null && !pom.isEmpty()) {
+                try (Reader r = openPomUrl(context.getRequiredTestClass(), 
pom, modelPath)) {
+                    tmodel = new MavenStaxReader().read(r);
+                }
+            } else {
+                Path pomPath = basedirPath.resolve("pom.xml");
+                if (Files.exists(pomPath)) {
+                    try (Reader r = Files.newBufferedReader(pomPath)) {
+                        tmodel = new MavenStaxReader().read(r);
+                        modelPath[0] = pomPath;
+                    }
+                }
+            }
+        }
+        Model model;
+        if (tmodel == null) {
+            model = defaultModel;
+        } else {
+            model = new MavenMerger().merge(tmodel, defaultModel, false, null);
+        }
+        tmodel = new DefaultModelPathTranslator(new DefaultPathTranslator())
+                .alignToBaseDirectory(tmodel, Paths.get(getBasedir()), null);
+        context.getStore(ExtensionContext.Namespace.GLOBAL).put(Model.class, 
tmodel);
 
+        // mojo execution
+        // Map<Object, Object> map = 
getInjector().getContext().getContextData();
+        PluginDescriptor pluginDescriptor;
         ClassLoader classLoader = 
context.getRequiredTestClass().getClassLoader();
-        try (InputStream is = Objects.requireNonNull(
+        try (InputStream is = requireNonNull(
                         
classLoader.getResourceAsStream(getPluginDescriptorLocation()),
                         "Unable to find plugin descriptor: " + 
getPluginDescriptorLocation());
-                Reader reader = new BufferedReader(new XmlStreamReader(is));
-                InterpolationFilterReader interpolationReader = new 
InterpolationFilterReader(reader, map, "${", "}")) {
+                Reader reader = new BufferedReader(new XmlStreamReader(is))) {
+            // new InterpolationFilterReader(reader, map, "${", "}");
+            pluginDescriptor = new PluginDescriptorStaxReader().read(reader);
+        }
+        
context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, 
pluginDescriptor);
+        // for (ComponentDescriptor<?> desc : 
pluginDescriptor.getComponents()) {
+        //    getContainer().addComponentDescriptor(desc);
+        // }
+
+        @SuppressWarnings({"unused", "MagicNumber"})
+        class Foo {
+
+            @Provides
+            @Singleton
+            @Priority(-10)
+            private InternalSession createSession() {
+                return SessionStub.getMockSession(getBasedir());
+            }
+
+            @Provides
+            @Singleton
+            @Priority(-10)
+            private Project createProject(InternalSession s) {
+                ProjectStub stub = new ProjectStub();
+                if (!"pom".equals(model.getPackaging())) {
+                    ArtifactStub artifact = new ArtifactStub(
+                            model.getGroupId(), model.getArtifactId(), "", 
model.getVersion(), model.getPackaging());
+                    stub.setMainArtifact(artifact);
+                }
+                stub.setModel(model);
+                stub.setBasedir(Paths.get(MojoExtension.getBasedir()));
+                stub.setPomPath(modelPath[0]);
+                
s.getService(ArtifactManager.class).setPath(stub.getPomArtifact(), 
modelPath[0]);
+                return stub;
+            }
+
+            @Provides
+            @Singleton
+            @Priority(-10)
+            private MojoExecution createMojoExecution() {
+                MojoExecutionStub mes = new MojoExecutionStub("executionId", 
null);
+                if (mojo != null) {
+                    String goal = mojo.goal();
+                    int idx = goal.lastIndexOf(':');
+                    if (idx >= 0) {
+                        goal = goal.substring(idx + 1);
+                    }
+                    mes.setGoal(goal);
+                    for (MojoDescriptor md : pluginDescriptor.getMojos()) {
+                        if (goal.equals(md.getGoal())) {
+                            mes.setDescriptor(md);
+                        }
+                    }
+                    requireNonNull(mes.getDescriptor());
+                }
+                PluginStub plugin = new PluginStub();
+                plugin.setDescriptor(pluginDescriptor);
+                mes.setPlugin(plugin);
+                return mes;
+            }
+
+            @Provides
+            @Singleton
+            @Priority(-10)
+            private Log createLog() {
+                return new DefaultLog(LoggerFactory.getLogger("anonymous"));
+            }
 
-            PluginDescriptor pluginDescriptor = new 
PluginDescriptorBuilder().build(interpolationReader);
+            @Provides
+            static RepositorySystemSupplier newRepositorySystemSupplier() {
+                return new RepositorySystemSupplier();
+            }
+
+            @Provides
+            static RepositorySystem 
newRepositorySystem(RepositorySystemSupplier repositorySystemSupplier) {
+                return repositorySystemSupplier.getRepositorySystem();
+            }
+
+            @Provides
+            @Priority(10)
+            static RepositoryFactory newRepositoryFactory(Session session) {
+                return session.getService(RepositoryFactory.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static VersionParser newVersionParser(Session session) {
+                return session.getService(VersionParser.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static LocalRepositoryManager newLocalRepositoryManager(Session 
session) {
+                return session.getService(LocalRepositoryManager.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static ArtifactInstaller newArtifactInstaller(Session session) {
+                return session.getService(ArtifactInstaller.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static ArtifactDeployer newArtifactDeployer(Session session) {
+                return session.getService(ArtifactDeployer.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static ArtifactManager newArtifactManager(Session session) {
+                return session.getService(ArtifactManager.class);
+            }
 
-            
context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, 
pluginDescriptor);
+            @Provides
+            @Priority(10)
+            static ProjectManager newProjectManager(Session session) {
+                return session.getService(ProjectManager.class);
+            }
 
-            for (ComponentDescriptor<?> desc : 
pluginDescriptor.getComponents()) {
-                getContainer().addComponentDescriptor(desc);
+            @Provides
+            @Priority(10)
+            static ArtifactFactory newArtifactFactory(Session session) {
+                return session.getService(ArtifactFactory.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static ProjectBuilder newProjectBuilder(Session session) {
+                return session.getService(ProjectBuilder.class);
+            }
+
+            @Provides
+            @Priority(10)
+            static ModelXmlFactory newModelXmlFactory(Session session) {
+                return session.getService(ModelXmlFactory.class);
             }
         }
-    }
 
-    protected String getPluginDescriptorLocation() {
-        return "META-INF/maven/plugin.xml";
+        getInjector().bindInstance(Foo.class, new Foo());
+
+        getInjector().injectInstance(context.getRequiredTestInstance());
+
+        //        SessionScope sessionScope = 
getInjector().getInstance(SessionScope.class);
+        //        sessionScope.enter();
+        //        sessionScope.seed(Session.class, s);
+        //        sessionScope.seed(InternalSession.class, s);
+
+        //        MojoExecutionScope mojoExecutionScope = 
getInjector().getInstance(MojoExecutionScope.class);
+        //        mojoExecutionScope.enter();
+        //        mojoExecutionScope.seed(Project.class, p);
+        //        mojoExecutionScope.seed(MojoExecution.class, me);
     }
 
-    private Mojo lookupMojo(
-            Class<?> holder,
-            InjectMojo injectMojo,
-            Collection<MojoParameter> mojoParameters,
-            PluginDescriptor descriptor)
-            throws Exception {
-        String goal = injectMojo.goal();
-        String pom = injectMojo.pom();
-        String[] coord = mojoCoordinates(goal);
-        Xpp3Dom pomDom;
+    private Reader openPomUrl(Class<?> holder, String pom, Path[] modelPath) 
throws IOException {
         if (pom.startsWith("file:")) {
             Path path = 
Paths.get(getBasedir()).resolve(pom.substring("file:".length()));
-            pomDom = 
Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile()));
+            modelPath[0] = path;
+            return Files.newBufferedReader(path);
         } else if (pom.startsWith("classpath:")) {
             URL url = holder.getResource(pom.substring("classpath:".length()));
             if (url == null) {
                 throw new IllegalStateException("Unable to find pom on 
classpath: " + pom);
             }
-            pomDom = 
Xpp3DomBuilder.build(ReaderFactory.newXmlReader(url.openStream()));
+            return new XmlStreamReader(url.openStream());
         } else if (pom.contains("<project>")) {
-            pomDom = Xpp3DomBuilder.build(new StringReader(pom));
+            return new StringReader(pom);
         } else {
             Path path = Paths.get(getBasedir()).resolve(pom);
-            pomDom = 
Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile()));
-        }
-        XmlNode pluginConfiguration = extractPluginConfiguration(coord[1], 
pomDom);
-        if (!mojoParameters.isEmpty()) {
-            List<XmlNode> children = mojoParameters.stream()
-                    .map(mp -> new XmlNodeImpl(mp.name(), mp.value()))
-                    .collect(Collectors.toList());
-            XmlNode config = new XmlNodeImpl("configuration", null, null, 
children, null);
-            pluginConfiguration = XmlNode.merge(config, pluginConfiguration);
+            modelPath[0] = path;
+            return Files.newBufferedReader(path);
         }
-        Mojo mojo = lookupMojo(coord, pluginConfiguration, descriptor);
-        return mojo;
+    }
+
+    protected String getPluginDescriptorLocation() {
+        return "META-INF/maven/plugin.xml";
     }
 
     protected String[] mojoCoordinates(String goal) throws Exception {
         if (goal.matches(".*:.*:.*:.*")) {
             return goal.split(":");
         } else {
-            Path pluginPom = Paths.get(getBasedir(), "pom.xml");
-            Xpp3Dom pluginPomDom = 
Xpp3DomBuilder.build(ReaderFactory.newXmlReader(pluginPom.toFile()));
+            Path pluginPom = Paths.get(getPluginBasedir(), "pom.xml");
+            Xpp3Dom pluginPomDom = 
Xpp3DomBuilder.build(Files.newBufferedReader(pluginPom));
             String artifactId = pluginPomDom.getChild("artifactId").getValue();
             String groupId = resolveFromRootThenParent(pluginPomDom, 
"groupId");
             String version = resolveFromRootThenParent(pluginPomDom, 
"version");
@@ -217,55 +508,11 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
         }
     }
 
-    /**
-     * lookup the mojo while we have all the relevent information
-     */
-    protected Mojo lookupMojo(String[] coord, XmlNode pluginConfiguration, 
PluginDescriptor descriptor)
-            throws Exception {
-        // pluginkey = groupId : artifactId : version : goal
-        Mojo mojo = lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + 
coord[2] + ":" + coord[3]);
-        for (MojoDescriptor mojoDescriptor : descriptor.getMojos()) {
-            if (Objects.equals(
-                    mojoDescriptor.getImplementation(), 
mojo.getClass().getName())) {
-                if (pluginConfiguration != null) {
-                    pluginConfiguration = finalizeConfig(pluginConfiguration, 
mojoDescriptor);
-                }
-            }
-        }
-        if (pluginConfiguration != null) {
-            Session session = getContainer().lookup(Session.class);
-            Project project;
-            try {
-                project = getContainer().lookup(Project.class);
-            } catch (ComponentLookupException e) {
-                project = null;
-            }
-            org.apache.maven.plugin.MojoExecution mojoExecution;
-            try {
-                MojoExecution me = getContainer().lookup(MojoExecution.class);
-                mojoExecution = new org.apache.maven.plugin.MojoExecution(
-                        new org.apache.maven.model.Plugin(me.getPlugin()), 
me.getGoal(), me.getExecutionId());
-            } catch (ComponentLookupException e) {
-                mojoExecution = null;
-            }
-            ExpressionEvaluator evaluator = new WrapEvaluator(
-                    getContainer(), new 
PluginParameterExpressionEvaluatorV4(session, project, mojoExecution));
-            ComponentConfigurator configurator = new 
EnhancedComponentConfigurator();
-            configurator.configureComponent(
-                    mojo,
-                    new XmlPlexusConfiguration(new 
Xpp3Dom(pluginConfiguration)),
-                    evaluator,
-                    getContainer().getContainerRealm());
-        }
-
-        return mojo;
-    }
-
     private XmlNode finalizeConfig(XmlNode config, MojoDescriptor 
mojoDescriptor) {
         List<XmlNode> children = new ArrayList<>();
         if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) {
-            XmlNode defaultConfiguration =
-                    MojoDescriptorCreator.convert(mojoDescriptor).getDom();
+            XmlNode defaultConfiguration;
+            defaultConfiguration = 
MojoDescriptorCreator.convert(mojoDescriptor);
             for (Parameter parameter : mojoDescriptor.getParameters()) {
                 XmlNode parameterConfiguration = 
config.getChild(parameter.getName());
                 if (parameterConfiguration == null) {
@@ -275,10 +522,10 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
                 parameterConfiguration = XmlNode.merge(parameterConfiguration, 
parameterDefaults, Boolean.TRUE);
                 if (parameterConfiguration != null) {
                     Map<String, String> attributes = new 
HashMap<>(parameterConfiguration.getAttributes());
-                    if 
(isEmpty(parameterConfiguration.getAttribute("implementation"))
-                            && !isEmpty(parameter.getImplementation())) {
-                        attributes.put("implementation", 
parameter.getImplementation());
-                    }
+                    // if 
(isEmpty(parameterConfiguration.getAttribute("implementation"))
+                    //         && !isEmpty(parameter.getImplementation())) {
+                    //     attributes.put("implementation", 
parameter.getImplementation());
+                    // }
                     parameterConfiguration = new XmlNodeImpl(
                             parameter.getName(),
                             parameterConfiguration.getValue(),
@@ -380,18 +627,18 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
     public static void setVariableValueToObject(Object object, String 
variable, Object value)
             throws IllegalAccessException {
         Field field = 
ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, 
object.getClass());
-        Objects.requireNonNull(field, "Field " + variable + " not found");
+        requireNonNull(field, "Field " + variable + " not found");
         field.setAccessible(true);
         field.set(object, value);
     }
 
     static class WrapEvaluator implements TypeAwareExpressionEvaluator {
 
-        private final PlexusContainer container;
+        private final Injector injector;
         private final TypeAwareExpressionEvaluator evaluator;
 
-        WrapEvaluator(PlexusContainer container, TypeAwareExpressionEvaluator 
evaluator) {
-            this.container = container;
+        WrapEvaluator(Injector injector, TypeAwareExpressionEvaluator 
evaluator) {
+            this.injector = injector;
             this.evaluator = evaluator;
         }
 
@@ -407,8 +654,8 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
                 String expr = stripTokens(expression);
                 if (expr != null) {
                     try {
-                        value = container.lookup(type, expr);
-                    } catch (ComponentLookupException e) {
+                        value = injector.getInstance(Key.of(type, expr));
+                    } catch (DIException e) {
                         // nothing
                     }
                 }
@@ -428,4 +675,35 @@ public class MojoExtension extends PlexusExtension 
implements ParameterResolver
             return evaluator.alignToBaseDirectory(path);
         }
     }
+
+    /*
+    private Scope getScopeInstanceOrNull(final Injector injector, final 
Binding<?> binding) {
+        return binding.acceptScopingVisitor(new 
DefaultBindingScopingVisitor<Scope>() {
+
+            @Override
+            public Scope visitScopeAnnotation(Class<? extends Annotation> 
scopeAnnotation) {
+                throw new RuntimeException(String.format(
+                        "I don't know how to handle the scopeAnnotation: %s", 
scopeAnnotation.getCanonicalName()));
+            }
+
+            @Override
+            public Scope visitNoScoping() {
+                if (binding instanceof LinkedKeyBinding) {
+                    Binding<?> childBinding = 
injector.getBinding(((LinkedKeyBinding) binding).getLinkedKey());
+                    return getScopeInstanceOrNull(injector, childBinding);
+                }
+                return null;
+            }
+
+            @Override
+            public Scope visitEagerSingleton() {
+                return Scopes.SINGLETON;
+            }
+
+            public Scope visitScope(Scope scope) {
+                return scope;
+            }
+        });
+    }*/
+
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
index 2a28f48..8c37804 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.api.plugin.testing;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -27,6 +28,7 @@ import java.lang.annotation.RetentionPolicy;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Repeatable(MojoParameters.class)
+@Inherited
 public @interface MojoParameter {
     String name();
 
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
index 434abe1..373c926 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.api.plugin.testing;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -25,6 +26,7 @@ import java.lang.annotation.RetentionPolicy;
  * Mojo parameters container
  */
 @Retention(RetentionPolicy.RUNTIME)
+@Inherited
 public @interface MojoParameters {
     MojoParameter[] value();
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java
index 4461071..1ca785d 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java
@@ -21,11 +21,9 @@ package org.apache.maven.api.plugin.testing;
 import java.io.File;
 import java.util.Map;
 
-import org.codehaus.plexus.PlexusTestCase;
 import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
 import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import 
org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator;
-import org.codehaus.plexus.testing.PlexusExtension;
 import org.eclipse.aether.repository.LocalRepository;
 
 /**
@@ -81,14 +79,14 @@ public class ResolverExpressionEvaluatorStub implements 
TypeAwareExpressionEvalu
             return expression.contains("$$") ? expression.replaceAll("\\$\\$", 
"\\$") : expression;
         } else {
             if ("basedir".equals(expression) || 
"project.basedir".equals(expression)) {
-                value = PlexusExtension.getBasedir();
+                value = MojoExtension.getBasedir();
             } else if (expression.startsWith("basedir") || 
expression.startsWith("project.basedir")) {
                 int pathSeparator = expression.indexOf("/");
                 if (pathSeparator > 0) {
-                    value = PlexusTestCase.getBasedir() + 
expression.substring(pathSeparator);
+                    value = MojoExtension.getBasedir() + 
expression.substring(pathSeparator);
                 }
             } else if ("localRepository".equals(expression)) {
-                File localRepo = new File(PlexusTestCase.getBasedir(), 
"target/local-repo");
+                File localRepo = new File(MojoExtension.getBasedir(), 
"target/local-repo");
                 return new LocalRepository("file://" + 
localRepo.getAbsolutePath());
             }
             if (value == null && properties != null && 
properties.containsKey(expression)) {
@@ -109,12 +107,12 @@ public class ResolverExpressionEvaluatorStub implements 
TypeAwareExpressionEvalu
     /** {@inheritDoc} */
     @Override
     public File alignToBaseDirectory(File file) {
-        if (file.getAbsolutePath().startsWith(PlexusExtension.getBasedir())) {
+        if (file.getAbsolutePath().startsWith(MojoExtension.getBasedir())) {
             return file;
         } else if (file.isAbsolute()) {
             return file;
         } else {
-            return new File(PlexusExtension.getBasedir(), file.getPath());
+            return new File(MojoExtension.getBasedir(), file.getPath());
         }
     }
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java
index 59be14f..b9b6416 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java
@@ -23,9 +23,11 @@ import java.util.Objects;
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.ArtifactCoordinate;
 import org.apache.maven.api.Version;
-import org.apache.maven.api.VersionRange;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.internal.impl.DefaultVersionParser;
+import org.apache.maven.repository.internal.DefaultModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
 
 /**
  *
@@ -35,6 +37,7 @@ public class ArtifactStub implements Artifact {
     private String artifactId;
     private String classifier;
     private String version;
+    private String baseVersion;
     private String extension;
 
     public ArtifactStub() {
@@ -86,13 +89,21 @@ public class ArtifactStub implements Artifact {
     @Nonnull
     @Override
     public Version getVersion() {
-        return new DefaultVersionParser().parseVersion(version);
+        return getParser().parseVersion(version);
     }
 
     public void setVersion(String version) {
         this.version = version;
     }
 
+    public Version getBaseVersion() {
+        return getParser().parseVersion(baseVersion != null ? baseVersion : 
version);
+    }
+
+    public void setBaseVersion(String baseVersion) {
+        this.baseVersion = baseVersion;
+    }
+
     @Nonnull
     @Override
     public String getExtension() {
@@ -127,8 +138,8 @@ public class ArtifactStub implements Artifact {
             }
 
             @Override
-            public VersionRange getVersion() {
-                return new DefaultVersionParser().parseVersionRange(version);
+            public VersionConstraint getVersion() {
+                return getParser().parseVersionConstraint(version);
             }
 
             @Override
@@ -169,4 +180,8 @@ public class ArtifactStub implements Artifact {
     public int hashCode() {
         return Objects.hash(groupId, artifactId, classifier, version, 
extension);
     }
+
+    private static DefaultVersionParser getParser() {
+        return new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme()));
+    }
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java
index a2d2b5b..2619b75 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java
@@ -21,24 +21,28 @@ package org.apache.maven.api.plugin.testing.stubs;
 import java.util.Optional;
 
 import org.apache.maven.api.MojoExecution;
-import org.apache.maven.api.model.Plugin;
+import org.apache.maven.api.Plugin;
+import org.apache.maven.api.model.PluginExecution;
+import org.apache.maven.api.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.api.xml.XmlNode;
 
 /**
  * Stub for {@link MojoExecution}.
  */
 public class MojoExecutionStub implements MojoExecution {
-    private final String artifactId;
-    private final String executionId;
-    private final String goal;
-    private final XmlNode dom;
+    private String executionId;
+    private String goal;
+    private XmlNode dom;
+    private Plugin plugin = new PluginStub();
+    private PluginExecution model;
+    private MojoDescriptor descriptor;
+    private String lifecyclePhase;
 
-    public MojoExecutionStub(String artifactId, String executionId, String 
goal) {
-        this(artifactId, executionId, goal, null);
+    public MojoExecutionStub(String executionId, String goal) {
+        this(executionId, goal, null);
     }
 
-    public MojoExecutionStub(String artifactId, String executionId, String 
goal, XmlNode dom) {
-        this.artifactId = artifactId;
+    public MojoExecutionStub(String executionId, String goal, XmlNode dom) {
         this.executionId = executionId;
         this.goal = goal;
         this.dom = dom;
@@ -46,7 +50,22 @@ public class MojoExecutionStub implements MojoExecution {
 
     @Override
     public Plugin getPlugin() {
-        return Plugin.newBuilder().artifactId(artifactId).build();
+        return plugin;
+    }
+
+    @Override
+    public PluginExecution getModel() {
+        return model;
+    }
+
+    @Override
+    public MojoDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    @Override
+    public String getLifecyclePhase() {
+        return lifecyclePhase;
     }
 
     @Override
@@ -63,4 +82,32 @@ public class MojoExecutionStub implements MojoExecution {
     public Optional<XmlNode> getConfiguration() {
         return Optional.ofNullable(dom);
     }
+
+    public void setExecutionId(String executionId) {
+        this.executionId = executionId;
+    }
+
+    public void setGoal(String goal) {
+        this.goal = goal;
+    }
+
+    public void setDom(XmlNode dom) {
+        this.dom = dom;
+    }
+
+    public void setPlugin(Plugin plugin) {
+        this.plugin = plugin;
+    }
+
+    public void setModel(PluginExecution model) {
+        this.model = model;
+    }
+
+    public void setDescriptor(MojoDescriptor descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    public void setLifecyclePhase(String lifecyclePhase) {
+        this.lifecyclePhase = lifecyclePhase;
+    }
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java
new file mode 100644
index 0000000..b7b4cfc
--- /dev/null
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.plugin.testing.stubs;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.api.Artifact;
+import org.apache.maven.api.Dependency;
+import org.apache.maven.api.Plugin;
+import org.apache.maven.api.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.api.plugin.descriptor.lifecycle.Lifecycle;
+
+public class PluginStub implements Plugin {
+
+    org.apache.maven.api.model.Plugin model;
+    PluginDescriptor descriptor;
+    List<Lifecycle> lifecycles = Collections.emptyList();
+    ClassLoader classLoader;
+    Artifact artifact;
+    List<Dependency> dependencies = Collections.emptyList();
+    Map<String, Dependency> dependenciesMap = Collections.emptyMap();
+
+    @Override
+    public org.apache.maven.api.model.Plugin getModel() {
+        return model;
+    }
+
+    public void setModel(org.apache.maven.api.model.Plugin model) {
+        this.model = model;
+    }
+
+    @Override
+    public PluginDescriptor getDescriptor() {
+        return descriptor;
+    }
+
+    public void setDescriptor(PluginDescriptor descriptor) {
+        this.descriptor = descriptor;
+    }
+
+    @Override
+    public List<Lifecycle> getLifecycles() {
+        return lifecycles;
+    }
+
+    public void setLifecycles(List<Lifecycle> lifecycles) {
+        this.lifecycles = lifecycles;
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    @Override
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    public void setArtifact(Artifact artifact) {
+        this.artifact = artifact;
+    }
+
+    @Override
+    public List<Dependency> getDependencies() {
+        return dependencies;
+    }
+
+    public void setDependencies(List<Dependency> dependencies) {
+        this.dependencies = dependencies;
+    }
+
+    public Map<String, Dependency> getDependenciesMap() {
+        return dependenciesMap;
+    }
+
+    public void setDependenciesMap(Map<String, Dependency> dependenciesMap) {
+        this.dependenciesMap = dependenciesMap;
+    }
+}
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java
index b3624cf..450cd1a 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java
@@ -18,18 +18,16 @@
  */
 package org.apache.maven.api.plugin.testing.stubs;
 
-import java.io.File;
 import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-import org.apache.maven.api.Artifact;
-import org.apache.maven.api.DependencyCoordinate;
-import org.apache.maven.api.Project;
-import org.apache.maven.api.RemoteRepository;
+import java.util.*;
+
+import org.apache.maven.api.*;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.model.Model;
+import org.apache.maven.api.model.PluginContainer;
+import org.apache.maven.internal.impl.DefaultVersionParser;
+import org.apache.maven.repository.internal.DefaultModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
 
 /**
  * @author Olivier Lamy
@@ -40,10 +38,11 @@ public class ProjectStub implements Project {
 
     private Model model = Model.newInstance();
     private Path basedir;
-    private File pomPath;
+    private Path pomPath;
     private boolean topProject;
-    private Artifact artifact;
     private Path rootDirectory;
+    private Map<String, String> properties = new HashMap<>();
+    private Artifact mainArtifact;
 
     public void setModel(Model model) {
         this.model = model;
@@ -73,14 +72,59 @@ public class ProjectStub implements Project {
 
     @Nonnull
     @Override
-    public String getPackaging() {
-        return model.getPackaging();
+    public Packaging getPackaging() {
+        return new Packaging() {
+            @Override
+            public String id() {
+                return model.getPackaging();
+            }
+
+            @Override
+            public Type type() {
+                return new Type() {
+                    @Override
+                    public String id() {
+                        return model.getPackaging();
+                    }
+
+                    @Override
+                    public Language getLanguage() {
+                        return null;
+                    }
+
+                    @Override
+                    public String getExtension() {
+                        return model.getPackaging();
+                    }
+
+                    @Override
+                    public String getClassifier() {
+                        return "";
+                    }
+
+                    @Override
+                    public boolean isIncludesDependencies() {
+                        return false;
+                    }
+
+                    @Override
+                    public Set<PathType> getPathTypes() {
+                        return Set.of();
+                    }
+                };
+            }
+
+            @Override
+            public Map<String, PluginContainer> plugins() {
+                return Map.of();
+            }
+        };
     }
 
-    @Nonnull
     @Override
-    public Artifact getArtifact() {
-        return artifact;
+    public List<Artifact> getArtifacts() {
+        Artifact pomArtifact = new ArtifactStub(getGroupId(), getArtifactId(), 
"", getVersion(), "pom");
+        return mainArtifact != null ? Arrays.asList(pomArtifact, mainArtifact) 
: Arrays.asList(pomArtifact);
     }
 
     @Nonnull
@@ -91,8 +135,8 @@ public class ProjectStub implements Project {
 
     @Nonnull
     @Override
-    public Optional<Path> getPomPath() {
-        return Optional.ofNullable(pomPath).map(File::toPath);
+    public Path getPomPath() {
+        return pomPath;
     }
 
     @Nonnull
@@ -108,34 +152,19 @@ public class ProjectStub implements Project {
     }
 
     @Override
-    public Optional<Path> getBasedir() {
-        return Optional.ofNullable(basedir);
+    public Path getBasedir() {
+        return basedir;
     }
 
     public void setBasedir(Path basedir) {
         this.basedir = basedir;
     }
 
-    @Override
-    public boolean isExecutionRoot() {
-        return isTopProject();
-    }
-
     @Override
     public Optional<Project> getParent() {
         return Optional.empty();
     }
 
-    @Override
-    public List<RemoteRepository> getRemoteProjectRepositories() {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public List<RemoteRepository> getRemotePluginRepositories() {
-        return Collections.emptyList();
-    }
-
     @Override
     public boolean isTopProject() {
         return topProject;
@@ -171,11 +200,11 @@ public class ProjectStub implements Project {
         model = model.withPackaging(packaging);
     }
 
-    public void setArtifact(Artifact artifact) {
-        this.artifact = artifact;
+    public void setMainArtifact(Artifact mainArtifact) {
+        this.mainArtifact = mainArtifact;
     }
 
-    public void setPomPath(File pomPath) {
+    public void setPomPath(Path pomPath) {
         this.pomPath = pomPath;
     }
 
@@ -190,4 +219,51 @@ public class ProjectStub implements Project {
     public void setRootDirectory(Path rootDirectory) {
         this.rootDirectory = rootDirectory;
     }
+
+    public void addProperty(String key, String value) {
+        properties.put(key, value);
+    }
+
+    class ProjectArtifact implements Artifact {
+        @Override
+        public String getGroupId() {
+            return ProjectStub.this.getGroupId();
+        }
+
+        @Override
+        public String getArtifactId() {
+            return ProjectStub.this.getArtifactId();
+        }
+
+        @Override
+        public Version getVersion() {
+            return new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme()))
+                    .parseVersion(ProjectStub.this.getVersion());
+        }
+
+        @Override
+        public Version getBaseVersion() {
+            return null;
+        }
+
+        @Override
+        public String getClassifier() {
+            return "";
+        }
+
+        @Override
+        public String getExtension() {
+            return "pom";
+        }
+
+        @Override
+        public boolean isSnapshot() {
+            return false;
+        }
+
+        @Override
+        public ArtifactCoordinate toCoordinate() {
+            return null;
+        }
+    }
 }
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java
new file mode 100644
index 0000000..0e709b3
--- /dev/null
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java
@@ -0,0 +1,1069 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.plugin.testing.stubs;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+
+import org.apache.maven.api.services.ModelBuilder;
+import org.apache.maven.internal.impl.DefaultModelUrlNormalizer;
+import org.apache.maven.internal.impl.DefaultModelVersionParser;
+import org.apache.maven.internal.impl.DefaultModelXmlFactory;
+import org.apache.maven.internal.impl.DefaultPluginConfigurationExpander;
+import org.apache.maven.internal.impl.DefaultSuperPomProvider;
+import org.apache.maven.internal.impl.DefaultUrlNormalizer;
+import org.apache.maven.internal.impl.model.BuildModelTransformer;
+import 
org.apache.maven.internal.impl.model.DefaultDependencyManagementImporter;
+import 
org.apache.maven.internal.impl.model.DefaultDependencyManagementInjector;
+import org.apache.maven.internal.impl.model.DefaultInheritanceAssembler;
+import org.apache.maven.internal.impl.model.DefaultModelBuilder;
+import org.apache.maven.internal.impl.model.DefaultModelInterpolator;
+import org.apache.maven.internal.impl.model.DefaultModelNormalizer;
+import org.apache.maven.internal.impl.model.DefaultModelPathTranslator;
+import org.apache.maven.internal.impl.model.DefaultModelProcessor;
+import org.apache.maven.internal.impl.model.DefaultModelValidator;
+import org.apache.maven.internal.impl.model.DefaultModelVersionProcessor;
+import org.apache.maven.internal.impl.model.DefaultPathTranslator;
+import org.apache.maven.internal.impl.model.DefaultPluginManagementInjector;
+import org.apache.maven.internal.impl.model.DefaultProfileInjector;
+import org.apache.maven.internal.impl.model.DefaultProfileSelector;
+import org.apache.maven.internal.impl.model.DefaultRootLocator;
+import 
org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
+import org.apache.maven.internal.impl.resolver.DefaultArtifactDescriptorReader;
+import org.apache.maven.internal.impl.resolver.DefaultVersionRangeResolver;
+import org.apache.maven.internal.impl.resolver.DefaultVersionResolver;
+import org.apache.maven.internal.impl.resolver.MavenArtifactRelocationSource;
+import org.apache.maven.internal.impl.resolver.PluginsMetadataGeneratorFactory;
+import 
org.apache.maven.internal.impl.resolver.SnapshotMetadataGeneratorFactory;
+import 
org.apache.maven.internal.impl.resolver.VersionsMetadataGeneratorFactory;
+import 
org.apache.maven.internal.impl.resolver.relocation.DistributionManagementArtifactRelocationSource;
+import 
org.apache.maven.internal.impl.resolver.relocation.UserPropertiesArtifactRelocationSource;
+import org.eclipse.aether.RepositoryListener;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.eclipse.aether.impl.ArtifactDescriptorReader;
+import org.eclipse.aether.impl.ArtifactResolver;
+import org.eclipse.aether.impl.DependencyCollector;
+import org.eclipse.aether.impl.Deployer;
+import org.eclipse.aether.impl.Installer;
+import org.eclipse.aether.impl.LocalRepositoryProvider;
+import org.eclipse.aether.impl.MetadataGeneratorFactory;
+import org.eclipse.aether.impl.MetadataResolver;
+import org.eclipse.aether.impl.OfflineController;
+import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
+import org.eclipse.aether.impl.RemoteRepositoryManager;
+import org.eclipse.aether.impl.RepositoryConnectorProvider;
+import org.eclipse.aether.impl.RepositoryEventDispatcher;
+import org.eclipse.aether.impl.RepositorySystemLifecycle;
+import org.eclipse.aether.impl.UpdateCheckManager;
+import org.eclipse.aether.impl.UpdatePolicyAnalyzer;
+import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.impl.VersionResolver;
+import org.eclipse.aether.internal.impl.*;
+import 
org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
+import org.eclipse.aether.internal.impl.checksum.Md5ChecksumAlgorithmFactory;
+import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
+import 
org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory;
+import 
org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory;
+import 
org.eclipse.aether.internal.impl.checksum.SparseDirectoryTrustedChecksumsSource;
+import 
org.eclipse.aether.internal.impl.checksum.SummaryFileTrustedChecksumsSource;
+import 
org.eclipse.aether.internal.impl.checksum.TrustedToProvidedChecksumsSourceAdapter;
+import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate;
+import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector;
+import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector;
+import 
org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterManager;
+import 
org.eclipse.aether.internal.impl.filter.GroupIdRemoteRepositoryFilterSource;
+import 
org.eclipse.aether.internal.impl.filter.PrefixesRemoteRepositoryFilterSource;
+import 
org.eclipse.aether.internal.impl.resolution.TrustedChecksumsArtifactResolverPostProcessor;
+import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
+import org.eclipse.aether.internal.impl.synccontext.named.NameMapper;
+import org.eclipse.aether.internal.impl.synccontext.named.NameMappers;
+import 
org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactory;
+import 
org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactoryImpl;
+import 
org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor;
+import org.eclipse.aether.internal.impl.transport.http.Nx2ChecksumExtractor;
+import org.eclipse.aether.internal.impl.transport.http.XChecksumExtractor;
+import org.eclipse.aether.named.NamedLockFactory;
+import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
+import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory;
+import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory;
+import org.eclipse.aether.named.providers.NoopNamedLockFactory;
+import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory;
+import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
+import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory;
+import org.eclipse.aether.spi.checksums.ProvidedChecksumsSource;
+import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
+import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import 
org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
+import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
+import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
+import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
+import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterProvider;
+import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
+import 
org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy;
+import org.eclipse.aether.spi.io.ChecksumProcessor;
+import org.eclipse.aether.spi.io.PathProcessor;
+import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
+import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
+import org.eclipse.aether.spi.synccontext.SyncContextFactory;
+import org.eclipse.aether.transport.apache.ApacheTransporterFactory;
+import org.eclipse.aether.transport.file.FileTransporterFactory;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.VersionScheme;
+
+/**
+ * A simple memorizing {@link Supplier} of {@link RepositorySystem} instance, 
that on first call
+ * supplies lazily constructed instance, and on each subsequent call same 
instance. Hence, this instance should be
+ * thrown away immediately once repository system was created and there is no 
need for more instances. If new
+ * repository system instance needed, new instance of this class must be 
created. For proper shut down of returned
+ * repository system instance(s) use {@link RepositorySystem#shutdown()} 
method on supplied instance(s).
+ * <p>
+ * Since Resolver 2.0 this class offers access to various components via 
public getters, and allows even partial object
+ * graph construction.
+ * <p>
+ * Extend this class {@code createXXX()} methods and override to customize, if 
needed. The contract of this class makes
+ * sure that these (potentially overridden) methods are invoked only once, and 
instance created by those methods are
+ * memorized and kept as long as supplier instance is kept open.
+ * <p>
+ * This class is not thread safe and must be used from one thread only, while 
the constructed {@link RepositorySystem}
+ * is thread safe.
+ * <p>
+ * Important: Given the instance of supplier memorizes the supplier {@link 
RepositorySystem} instance it supplies,
+ * their lifecycle is shared as well: once supplied repository system is 
shut-down, this instance becomes closed as
+ * well. Any subsequent {@code getXXX} method invocation attempt will fail 
with {@link IllegalStateException}.
+ */
+public class RepositorySystemSupplier implements Supplier<RepositorySystem> {
+    private final AtomicBoolean closed = new AtomicBoolean(false);
+
+    public RepositorySystemSupplier() {}
+
+    private void checkClosed() {
+        if (closed.get()) {
+            throw new IllegalStateException("Supplier is closed");
+        }
+    }
+
+    private PathProcessor pathProcessor;
+
+    public final PathProcessor getPathProcessor() {
+        checkClosed();
+        if (pathProcessor == null) {
+            pathProcessor = createPathProcessor();
+        }
+        return pathProcessor;
+    }
+
+    protected PathProcessor createPathProcessor() {
+        return new DefaultPathProcessor();
+    }
+
+    private ChecksumProcessor checksumProcessor;
+
+    public final ChecksumProcessor getChecksumProcessor() {
+        checkClosed();
+        if (checksumProcessor == null) {
+            checksumProcessor = createChecksumProcessor();
+        }
+        return checksumProcessor;
+    }
+
+    protected ChecksumProcessor createChecksumProcessor() {
+        return new DefaultChecksumProcessor(getPathProcessor());
+    }
+
+    private TrackingFileManager trackingFileManager;
+
+    public final TrackingFileManager getTrackingFileManager() {
+        checkClosed();
+        if (trackingFileManager == null) {
+            trackingFileManager = createTrackingFileManager();
+        }
+        return trackingFileManager;
+    }
+
+    protected TrackingFileManager createTrackingFileManager() {
+        return new DefaultTrackingFileManager();
+    }
+
+    private LocalPathComposer localPathComposer;
+
+    public final LocalPathComposer getLocalPathComposer() {
+        checkClosed();
+        if (localPathComposer == null) {
+            localPathComposer = createLocalPathComposer();
+        }
+        return localPathComposer;
+    }
+
+    protected LocalPathComposer createLocalPathComposer() {
+        return new DefaultLocalPathComposer();
+    }
+
+    private LocalPathPrefixComposerFactory localPathPrefixComposerFactory;
+
+    public final LocalPathPrefixComposerFactory 
getLocalPathPrefixComposerFactory() {
+        checkClosed();
+        if (localPathPrefixComposerFactory == null) {
+            localPathPrefixComposerFactory = 
createLocalPathPrefixComposerFactory();
+        }
+        return localPathPrefixComposerFactory;
+    }
+
+    protected LocalPathPrefixComposerFactory 
createLocalPathPrefixComposerFactory() {
+        return new DefaultLocalPathPrefixComposerFactory();
+    }
+
+    private RepositorySystemLifecycle repositorySystemLifecycle;
+
+    public final RepositorySystemLifecycle getRepositorySystemLifecycle() {
+        checkClosed();
+        if (repositorySystemLifecycle == null) {
+            repositorySystemLifecycle = createRepositorySystemLifecycle();
+            repositorySystemLifecycle.addOnSystemEndedHandler(() -> 
closed.set(true));
+        }
+        return repositorySystemLifecycle;
+    }
+
+    protected RepositorySystemLifecycle createRepositorySystemLifecycle() {
+        return new DefaultRepositorySystemLifecycle();
+    }
+
+    private OfflineController offlineController;
+
+    public final OfflineController getOfflineController() {
+        checkClosed();
+        if (offlineController == null) {
+            offlineController = createOfflineController();
+        }
+        return offlineController;
+    }
+
+    protected OfflineController createOfflineController() {
+        return new DefaultOfflineController();
+    }
+
+    private UpdatePolicyAnalyzer updatePolicyAnalyzer;
+
+    public final UpdatePolicyAnalyzer getUpdatePolicyAnalyzer() {
+        checkClosed();
+        if (updatePolicyAnalyzer == null) {
+            updatePolicyAnalyzer = createUpdatePolicyAnalyzer();
+        }
+        return updatePolicyAnalyzer;
+    }
+
+    protected UpdatePolicyAnalyzer createUpdatePolicyAnalyzer() {
+        return new DefaultUpdatePolicyAnalyzer();
+    }
+
+    private ChecksumPolicyProvider checksumPolicyProvider;
+
+    public final ChecksumPolicyProvider getChecksumPolicyProvider() {
+        checkClosed();
+        if (checksumPolicyProvider == null) {
+            checksumPolicyProvider = createChecksumPolicyProvider();
+        }
+        return checksumPolicyProvider;
+    }
+
+    protected ChecksumPolicyProvider createChecksumPolicyProvider() {
+        return new DefaultChecksumPolicyProvider();
+    }
+
+    private UpdateCheckManager updateCheckManager;
+
+    public final UpdateCheckManager getUpdateCheckManager() {
+        checkClosed();
+        if (updateCheckManager == null) {
+            updateCheckManager = createUpdateCheckManager();
+        }
+        return updateCheckManager;
+    }
+
+    protected UpdateCheckManager createUpdateCheckManager() {
+        return new DefaultUpdateCheckManager(getTrackingFileManager(), 
getUpdatePolicyAnalyzer(), getPathProcessor());
+    }
+
+    private Map<String, NamedLockFactory> namedLockFactories;
+
+    public final Map<String, NamedLockFactory> getNamedLockFactories() {
+        checkClosed();
+        if (namedLockFactories == null) {
+            namedLockFactories = createNamedLockFactories();
+        }
+        return namedLockFactories;
+    }
+
+    protected Map<String, NamedLockFactory> createNamedLockFactories() {
+        HashMap<String, NamedLockFactory> result = new HashMap<>();
+        result.put(NoopNamedLockFactory.NAME, new NoopNamedLockFactory());
+        result.put(LocalReadWriteLockNamedLockFactory.NAME, new 
LocalReadWriteLockNamedLockFactory());
+        result.put(LocalSemaphoreNamedLockFactory.NAME, new 
LocalSemaphoreNamedLockFactory());
+        result.put(FileLockNamedLockFactory.NAME, new 
FileLockNamedLockFactory());
+        return result;
+    }
+
+    private Map<String, NameMapper> nameMappers;
+
+    public final Map<String, NameMapper> getNameMappers() {
+        checkClosed();
+        if (nameMappers == null) {
+            nameMappers = createNameMappers();
+        }
+        return nameMappers;
+    }
+
+    protected Map<String, NameMapper> createNameMappers() {
+        HashMap<String, NameMapper> result = new HashMap<>();
+        result.put(NameMappers.STATIC_NAME, NameMappers.staticNameMapper());
+        result.put(NameMappers.GAV_NAME, NameMappers.gavNameMapper());
+        result.put(NameMappers.DISCRIMINATING_NAME, 
NameMappers.discriminatingNameMapper());
+        result.put(NameMappers.FILE_GAV_NAME, NameMappers.fileGavNameMapper());
+        result.put(NameMappers.FILE_HGAV_NAME, 
NameMappers.fileHashingGavNameMapper());
+        return result;
+    }
+
+    private NamedLockFactoryAdapterFactory namedLockFactoryAdapterFactory;
+
+    public final NamedLockFactoryAdapterFactory 
getNamedLockFactoryAdapterFactory() {
+        checkClosed();
+        if (namedLockFactoryAdapterFactory == null) {
+            namedLockFactoryAdapterFactory = 
createNamedLockFactoryAdapterFactory();
+        }
+        return namedLockFactoryAdapterFactory;
+    }
+
+    protected NamedLockFactoryAdapterFactory 
createNamedLockFactoryAdapterFactory() {
+        return new NamedLockFactoryAdapterFactoryImpl(
+                getNamedLockFactories(), getNameMappers(), 
getRepositorySystemLifecycle());
+    }
+
+    private SyncContextFactory syncContextFactory;
+
+    public final SyncContextFactory getSyncContextFactory() {
+        checkClosed();
+        if (syncContextFactory == null) {
+            syncContextFactory = createSyncContextFactory();
+        }
+        return syncContextFactory;
+    }
+
+    protected SyncContextFactory createSyncContextFactory() {
+        return new 
DefaultSyncContextFactory(getNamedLockFactoryAdapterFactory());
+    }
+
+    private Map<String, ChecksumAlgorithmFactory> checksumAlgorithmFactories;
+
+    public final Map<String, ChecksumAlgorithmFactory> 
getChecksumAlgorithmFactories() {
+        checkClosed();
+        if (checksumAlgorithmFactories == null) {
+            checksumAlgorithmFactories = createChecksumAlgorithmFactories();
+        }
+        return checksumAlgorithmFactories;
+    }
+
+    protected Map<String, ChecksumAlgorithmFactory> 
createChecksumAlgorithmFactories() {
+        HashMap<String, ChecksumAlgorithmFactory> result = new HashMap<>();
+        result.put(Sha512ChecksumAlgorithmFactory.NAME, new 
Sha512ChecksumAlgorithmFactory());
+        result.put(Sha256ChecksumAlgorithmFactory.NAME, new 
Sha256ChecksumAlgorithmFactory());
+        result.put(Sha1ChecksumAlgorithmFactory.NAME, new 
Sha1ChecksumAlgorithmFactory());
+        result.put(Md5ChecksumAlgorithmFactory.NAME, new 
Md5ChecksumAlgorithmFactory());
+        return result;
+    }
+
+    private ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
+
+    public final ChecksumAlgorithmFactorySelector 
getChecksumAlgorithmFactorySelector() {
+        checkClosed();
+        if (checksumAlgorithmFactorySelector == null) {
+            checksumAlgorithmFactorySelector = 
createChecksumAlgorithmFactorySelector();
+        }
+        return checksumAlgorithmFactorySelector;
+    }
+
+    protected ChecksumAlgorithmFactorySelector 
createChecksumAlgorithmFactorySelector() {
+        return new 
DefaultChecksumAlgorithmFactorySelector(getChecksumAlgorithmFactories());
+    }
+
+    private ArtifactPredicateFactory artifactPredicateFactory;
+
+    public final ArtifactPredicateFactory getArtifactPredicateFactory() {
+        checkClosed();
+        if (artifactPredicateFactory == null) {
+            artifactPredicateFactory = createArtifactPredicateFactory();
+        }
+        return artifactPredicateFactory;
+    }
+
+    protected ArtifactPredicateFactory createArtifactPredicateFactory() {
+        return new 
DefaultArtifactPredicateFactory(getChecksumAlgorithmFactorySelector());
+    }
+
+    private Map<String, RepositoryLayoutFactory> repositoryLayoutFactories;
+
+    public final Map<String, RepositoryLayoutFactory> 
getRepositoryLayoutFactories() {
+        checkClosed();
+        if (repositoryLayoutFactories == null) {
+            repositoryLayoutFactories = createRepositoryLayoutFactories();
+        }
+        return repositoryLayoutFactories;
+    }
+
+    protected Map<String, RepositoryLayoutFactory> 
createRepositoryLayoutFactories() {
+        HashMap<String, RepositoryLayoutFactory> result = new HashMap<>();
+        result.put(
+                Maven2RepositoryLayoutFactory.NAME,
+                new Maven2RepositoryLayoutFactory(
+                        getChecksumAlgorithmFactorySelector(), 
getArtifactPredicateFactory()));
+        return result;
+    }
+
+    private RepositoryLayoutProvider repositoryLayoutProvider;
+
+    public final RepositoryLayoutProvider getRepositoryLayoutProvider() {
+        checkClosed();
+        if (repositoryLayoutProvider == null) {
+            repositoryLayoutProvider = createRepositoryLayoutProvider();
+        }
+        return repositoryLayoutProvider;
+    }
+
+    protected RepositoryLayoutProvider createRepositoryLayoutProvider() {
+        return new 
DefaultRepositoryLayoutProvider(getRepositoryLayoutFactories());
+    }
+
+    private LocalRepositoryProvider localRepositoryProvider;
+
+    public final LocalRepositoryProvider getLocalRepositoryProvider() {
+        checkClosed();
+        if (localRepositoryProvider == null) {
+            localRepositoryProvider = createLocalRepositoryProvider();
+        }
+        return localRepositoryProvider;
+    }
+
+    protected LocalRepositoryProvider createLocalRepositoryProvider() {
+        LocalPathComposer localPathComposer = getLocalPathComposer();
+        HashMap<String, LocalRepositoryManagerFactory> 
localRepositoryProviders = new HashMap<>(2);
+        localRepositoryProviders.put(
+                SimpleLocalRepositoryManagerFactory.NAME, new 
SimpleLocalRepositoryManagerFactory(localPathComposer));
+        localRepositoryProviders.put(
+                EnhancedLocalRepositoryManagerFactory.NAME,
+                new EnhancedLocalRepositoryManagerFactory(
+                        localPathComposer, getTrackingFileManager(), 
getLocalPathPrefixComposerFactory()));
+        return new DefaultLocalRepositoryProvider(localRepositoryProviders);
+    }
+
+    private RemoteRepositoryManager remoteRepositoryManager;
+
+    public final RemoteRepositoryManager getRemoteRepositoryManager() {
+        checkClosed();
+        if (remoteRepositoryManager == null) {
+            remoteRepositoryManager = createRemoteRepositoryManager();
+        }
+        return remoteRepositoryManager;
+    }
+
+    protected RemoteRepositoryManager createRemoteRepositoryManager() {
+        return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(), 
getChecksumPolicyProvider());
+    }
+
+    private Map<String, RemoteRepositoryFilterSource> 
remoteRepositoryFilterSources;
+
+    public final Map<String, RemoteRepositoryFilterSource> 
getRemoteRepositoryFilterSources() {
+        checkClosed();
+        if (remoteRepositoryFilterSources == null) {
+            remoteRepositoryFilterSources = 
createRemoteRepositoryFilterSources();
+        }
+        return remoteRepositoryFilterSources;
+    }
+
+    protected Map<String, RemoteRepositoryFilterSource> 
createRemoteRepositoryFilterSources() {
+        HashMap<String, RemoteRepositoryFilterSource> result = new HashMap<>();
+        result.put(
+                GroupIdRemoteRepositoryFilterSource.NAME,
+                new 
GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle()));
+        result.put(
+                PrefixesRemoteRepositoryFilterSource.NAME,
+                new 
PrefixesRemoteRepositoryFilterSource(getRepositoryLayoutProvider()));
+        return result;
+    }
+
+    private RemoteRepositoryFilterManager remoteRepositoryFilterManager;
+
+    public final RemoteRepositoryFilterManager 
getRemoteRepositoryFilterManager() {
+        checkClosed();
+        if (remoteRepositoryFilterManager == null) {
+            remoteRepositoryFilterManager = 
createRemoteRepositoryFilterManager();
+        }
+        return remoteRepositoryFilterManager;
+    }
+
+    protected RemoteRepositoryFilterManager 
createRemoteRepositoryFilterManager() {
+        return new 
DefaultRemoteRepositoryFilterManager(getRemoteRepositoryFilterSources());
+    }
+
+    private Map<String, RepositoryListener> repositoryListeners;
+
+    public final Map<String, RepositoryListener> getRepositoryListeners() {
+        checkClosed();
+        if (repositoryListeners == null) {
+            repositoryListeners = createRepositoryListeners();
+        }
+        return repositoryListeners;
+    }
+
+    protected Map<String, RepositoryListener> createRepositoryListeners() {
+        return new HashMap<>();
+    }
+
+    private RepositoryEventDispatcher repositoryEventDispatcher;
+
+    public final RepositoryEventDispatcher getRepositoryEventDispatcher() {
+        checkClosed();
+        if (repositoryEventDispatcher == null) {
+            repositoryEventDispatcher = createRepositoryEventDispatcher();
+        }
+        return repositoryEventDispatcher;
+    }
+
+    protected RepositoryEventDispatcher createRepositoryEventDispatcher() {
+        return new DefaultRepositoryEventDispatcher(getRepositoryListeners());
+    }
+
+    private Map<String, TrustedChecksumsSource> trustedChecksumsSources;
+
+    public final Map<String, TrustedChecksumsSource> 
getTrustedChecksumsSources() {
+        checkClosed();
+        if (trustedChecksumsSources == null) {
+            trustedChecksumsSources = createTrustedChecksumsSources();
+        }
+        return trustedChecksumsSources;
+    }
+
+    protected Map<String, TrustedChecksumsSource> 
createTrustedChecksumsSources() {
+        HashMap<String, TrustedChecksumsSource> result = new HashMap<>();
+        result.put(
+                SparseDirectoryTrustedChecksumsSource.NAME,
+                new 
SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), 
getLocalPathComposer()));
+        result.put(
+                SummaryFileTrustedChecksumsSource.NAME,
+                new SummaryFileTrustedChecksumsSource(getLocalPathComposer(), 
getRepositorySystemLifecycle()));
+        return result;
+    }
+
+    private Map<String, ProvidedChecksumsSource> providedChecksumsSources;
+
+    public final Map<String, ProvidedChecksumsSource> 
getProvidedChecksumsSources() {
+        checkClosed();
+        if (providedChecksumsSources == null) {
+            providedChecksumsSources = createProvidedChecksumsSources();
+        }
+        return providedChecksumsSources;
+    }
+
+    protected Map<String, ProvidedChecksumsSource> 
createProvidedChecksumsSources() {
+        HashMap<String, ProvidedChecksumsSource> result = new HashMap<>();
+        result.put(
+                TrustedToProvidedChecksumsSourceAdapter.NAME,
+                new 
TrustedToProvidedChecksumsSourceAdapter(getTrustedChecksumsSources()));
+        return result;
+    }
+
+    private Map<String, ChecksumExtractorStrategy> checksumExtractorStrategies;
+
+    public final Map<String, ChecksumExtractorStrategy> 
getChecksumExtractorStrategies() {
+        checkClosed();
+        if (checksumExtractorStrategies == null) {
+            checksumExtractorStrategies = createChecksumExtractorStrategies();
+        }
+        return checksumExtractorStrategies;
+    }
+
+    protected Map<String, ChecksumExtractorStrategy> 
createChecksumExtractorStrategies() {
+        HashMap<String, ChecksumExtractorStrategy> result = new HashMap<>();
+        result.put(XChecksumExtractor.NAME, new XChecksumExtractor());
+        result.put(Nx2ChecksumExtractor.NAME, new Nx2ChecksumExtractor());
+        return result;
+    }
+
+    private ChecksumExtractor checksumExtractor;
+
+    public final ChecksumExtractor getChecksumExtractor() {
+        checkClosed();
+        if (checksumExtractor == null) {
+            checksumExtractor = createChecksumExtractor();
+        }
+        return checksumExtractor;
+    }
+
+    protected ChecksumExtractor createChecksumExtractor() {
+        return new DefaultChecksumExtractor(getChecksumExtractorStrategies());
+    }
+
+    private Map<String, TransporterFactory> transporterFactories;
+
+    public final Map<String, TransporterFactory> getTransporterFactories() {
+        checkClosed();
+        if (transporterFactories == null) {
+            transporterFactories = createTransporterFactories();
+        }
+        return transporterFactories;
+    }
+
+    protected Map<String, TransporterFactory> createTransporterFactories() {
+        HashMap<String, TransporterFactory> result = new HashMap<>();
+        result.put(FileTransporterFactory.NAME, new FileTransporterFactory());
+        result.put(
+                ApacheTransporterFactory.NAME,
+                new ApacheTransporterFactory(getChecksumExtractor(), 
getPathProcessor()));
+        return result;
+    }
+
+    private TransporterProvider transporterProvider;
+
+    public final TransporterProvider getTransporterProvider() {
+        checkClosed();
+        if (transporterProvider == null) {
+            transporterProvider = createTransporterProvider();
+        }
+        return transporterProvider;
+    }
+
+    protected TransporterProvider createTransporterProvider() {
+        return new DefaultTransporterProvider(getTransporterFactories());
+    }
+
+    private BasicRepositoryConnectorFactory basicRepositoryConnectorFactory;
+
+    public final BasicRepositoryConnectorFactory 
getBasicRepositoryConnectorFactory() {
+        checkClosed();
+        if (basicRepositoryConnectorFactory == null) {
+            basicRepositoryConnectorFactory = 
createBasicRepositoryConnectorFactory();
+        }
+        return basicRepositoryConnectorFactory;
+    }
+
+    protected BasicRepositoryConnectorFactory 
createBasicRepositoryConnectorFactory() {
+        return new BasicRepositoryConnectorFactory(
+                getTransporterProvider(),
+                getRepositoryLayoutProvider(),
+                getChecksumPolicyProvider(),
+                getChecksumProcessor(),
+                getProvidedChecksumsSources());
+    }
+
+    private Map<String, RepositoryConnectorFactory> 
repositoryConnectorFactories;
+
+    public final Map<String, RepositoryConnectorFactory> 
getRepositoryConnectorFactories() {
+        checkClosed();
+        if (repositoryConnectorFactories == null) {
+            repositoryConnectorFactories = 
createRepositoryConnectorFactories();
+        }
+        return repositoryConnectorFactories;
+    }
+
+    protected Map<String, RepositoryConnectorFactory> 
createRepositoryConnectorFactories() {
+        HashMap<String, RepositoryConnectorFactory> result = new HashMap<>();
+        result.put(BasicRepositoryConnectorFactory.NAME, 
getBasicRepositoryConnectorFactory());
+        return result;
+    }
+
+    private RepositoryConnectorProvider repositoryConnectorProvider;
+
+    public final RepositoryConnectorProvider getRepositoryConnectorProvider() {
+        checkClosed();
+        if (repositoryConnectorProvider == null) {
+            repositoryConnectorProvider = createRepositoryConnectorProvider();
+        }
+        return repositoryConnectorProvider;
+    }
+
+    protected RepositoryConnectorProvider createRepositoryConnectorProvider() {
+        return new DefaultRepositoryConnectorProvider(
+                getRepositoryConnectorFactories(), 
getRemoteRepositoryFilterManager());
+    }
+
+    private Installer installer;
+
+    public final Installer getInstaller() {
+        checkClosed();
+        if (installer == null) {
+            installer = createInstaller();
+        }
+        return installer;
+    }
+
+    protected Installer createInstaller() {
+        return new DefaultInstaller(
+                getPathProcessor(),
+                getRepositoryEventDispatcher(),
+                getArtifactGeneratorFactories(),
+                getMetadataGeneratorFactories(),
+                getSyncContextFactory());
+    }
+
+    private Deployer deployer;
+
+    public final Deployer getDeployer() {
+        checkClosed();
+        if (deployer == null) {
+            deployer = createDeployer();
+        }
+        return deployer;
+    }
+
+    protected Deployer createDeployer() {
+        return new DefaultDeployer(
+                getPathProcessor(),
+                getRepositoryEventDispatcher(),
+                getRepositoryConnectorProvider(),
+                getRemoteRepositoryManager(),
+                getUpdateCheckManager(),
+                getArtifactGeneratorFactories(),
+                getMetadataGeneratorFactories(),
+                getSyncContextFactory(),
+                getOfflineController());
+    }
+
+    private Map<String, DependencyCollectorDelegate> 
dependencyCollectorDelegates;
+
+    public final Map<String, DependencyCollectorDelegate> 
getDependencyCollectorDelegates() {
+        checkClosed();
+        if (dependencyCollectorDelegates == null) {
+            dependencyCollectorDelegates = 
createDependencyCollectorDelegates();
+        }
+        return dependencyCollectorDelegates;
+    }
+
+    protected Map<String, DependencyCollectorDelegate> 
createDependencyCollectorDelegates() {
+        RemoteRepositoryManager remoteRepositoryManager = 
getRemoteRepositoryManager();
+        ArtifactDescriptorReader artifactDescriptorReader = 
getArtifactDescriptorReader();
+        VersionRangeResolver versionRangeResolver = getVersionRangeResolver();
+        HashMap<String, DependencyCollectorDelegate> result = new HashMap<>();
+        result.put(
+                DfDependencyCollector.NAME,
+                new DfDependencyCollector(
+                        remoteRepositoryManager,
+                        artifactDescriptorReader,
+                        versionRangeResolver,
+                        getArtifactDecoratorFactories()));
+        result.put(
+                BfDependencyCollector.NAME,
+                new BfDependencyCollector(
+                        remoteRepositoryManager,
+                        artifactDescriptorReader,
+                        versionRangeResolver,
+                        getArtifactDecoratorFactories()));
+        return result;
+    }
+
+    private DependencyCollector dependencyCollector;
+
+    public final DependencyCollector getDependencyCollector() {
+        checkClosed();
+        if (dependencyCollector == null) {
+            dependencyCollector = createDependencyCollector();
+        }
+        return dependencyCollector;
+    }
+
+    protected DependencyCollector createDependencyCollector() {
+        return new 
DefaultDependencyCollector(getDependencyCollectorDelegates());
+    }
+
+    private Map<String, ArtifactResolverPostProcessor> 
artifactResolverPostProcessors;
+
+    public final Map<String, ArtifactResolverPostProcessor> 
getArtifactResolverPostProcessors() {
+        checkClosed();
+        if (artifactResolverPostProcessors == null) {
+            artifactResolverPostProcessors = 
createArtifactResolverPostProcessors();
+        }
+        return artifactResolverPostProcessors;
+    }
+
+    protected Map<String, ArtifactResolverPostProcessor> 
createArtifactResolverPostProcessors() {
+        HashMap<String, ArtifactResolverPostProcessor> result = new 
HashMap<>();
+        result.put(
+                TrustedChecksumsArtifactResolverPostProcessor.NAME,
+                new TrustedChecksumsArtifactResolverPostProcessor(
+                        getChecksumAlgorithmFactorySelector(), 
getTrustedChecksumsSources()));
+        return result;
+    }
+
+    private ArtifactResolver artifactResolver;
+
+    public final ArtifactResolver getArtifactResolver() {
+        checkClosed();
+        if (artifactResolver == null) {
+            artifactResolver = createArtifactResolver();
+        }
+        return artifactResolver;
+    }
+
+    protected ArtifactResolver createArtifactResolver() {
+        return new DefaultArtifactResolver(
+                getPathProcessor(),
+                getRepositoryEventDispatcher(),
+                getVersionResolver(),
+                getUpdateCheckManager(),
+                getRepositoryConnectorProvider(),
+                getRemoteRepositoryManager(),
+                getSyncContextFactory(),
+                getOfflineController(),
+                getArtifactResolverPostProcessors(),
+                getRemoteRepositoryFilterManager());
+    }
+
+    private MetadataResolver metadataResolver;
+
+    public final MetadataResolver getMetadataResolver() {
+        checkClosed();
+        if (metadataResolver == null) {
+            metadataResolver = createMetadataResolver();
+        }
+        return metadataResolver;
+    }
+
+    protected MetadataResolver createMetadataResolver() {
+        return new DefaultMetadataResolver(
+                getRepositoryEventDispatcher(),
+                getUpdateCheckManager(),
+                getRepositoryConnectorProvider(),
+                getRemoteRepositoryManager(),
+                getSyncContextFactory(),
+                getOfflineController(),
+                getRemoteRepositoryFilterManager(),
+                getPathProcessor());
+    }
+
+    private VersionScheme versionScheme;
+
+    public final VersionScheme getVersionScheme() {
+        checkClosed();
+        if (versionScheme == null) {
+            versionScheme = createVersionScheme();
+        }
+        return versionScheme;
+    }
+
+    protected VersionScheme createVersionScheme() {
+        return new GenericVersionScheme();
+    }
+
+    private Map<String, ArtifactGeneratorFactory> artifactGeneratorFactories;
+
+    public final Map<String, ArtifactGeneratorFactory> 
getArtifactGeneratorFactories() {
+        checkClosed();
+        if (artifactGeneratorFactories == null) {
+            artifactGeneratorFactories = createArtifactGeneratorFactories();
+        }
+        return artifactGeneratorFactories;
+    }
+
+    protected Map<String, ArtifactGeneratorFactory> 
createArtifactGeneratorFactories() {
+        // by default none, this is extension point
+        return new HashMap<>();
+    }
+
+    private Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories;
+
+    public final Map<String, ArtifactDecoratorFactory> 
getArtifactDecoratorFactories() {
+        checkClosed();
+        if (artifactDecoratorFactories == null) {
+            artifactDecoratorFactories = createArtifactDecoratorFactories();
+        }
+        return artifactDecoratorFactories;
+    }
+
+    protected Map<String, ArtifactDecoratorFactory> 
createArtifactDecoratorFactories() {
+        // by default none, this is extension point
+        return new HashMap<>();
+    }
+
+    // Maven provided
+
+    private Map<String, MetadataGeneratorFactory> metadataGeneratorFactories;
+
+    public final Map<String, MetadataGeneratorFactory> 
getMetadataGeneratorFactories() {
+        checkClosed();
+        if (metadataGeneratorFactories == null) {
+            metadataGeneratorFactories = createMetadataGeneratorFactories();
+        }
+        return metadataGeneratorFactories;
+    }
+
+    protected Map<String, MetadataGeneratorFactory> 
createMetadataGeneratorFactories() {
+        // from maven-resolver-provider
+        HashMap<String, MetadataGeneratorFactory> result = new HashMap<>();
+        result.put(PluginsMetadataGeneratorFactory.NAME, new 
PluginsMetadataGeneratorFactory());
+        result.put(VersionsMetadataGeneratorFactory.NAME, new 
VersionsMetadataGeneratorFactory());
+        result.put(SnapshotMetadataGeneratorFactory.NAME, new 
SnapshotMetadataGeneratorFactory());
+        return result;
+    }
+
+    private LinkedHashMap<String, MavenArtifactRelocationSource> 
artifactRelocationSources;
+
+    public final LinkedHashMap<String, MavenArtifactRelocationSource> 
getMavenArtifactRelocationSources() {
+        checkClosed();
+        if (artifactRelocationSources == null) {
+            artifactRelocationSources = createMavenArtifactRelocationSources();
+        }
+        return artifactRelocationSources;
+    }
+
+    protected LinkedHashMap<String, MavenArtifactRelocationSource> 
createMavenArtifactRelocationSources() {
+        // from maven-resolver-provider
+        LinkedHashMap<String, MavenArtifactRelocationSource> result = new 
LinkedHashMap<>();
+        result.put(UserPropertiesArtifactRelocationSource.NAME, new 
UserPropertiesArtifactRelocationSource());
+        result.put(
+                DistributionManagementArtifactRelocationSource.NAME,
+                new DistributionManagementArtifactRelocationSource());
+        return result;
+    }
+
+    private ArtifactDescriptorReader artifactDescriptorReader;
+
+    public final ArtifactDescriptorReader getArtifactDescriptorReader() {
+        checkClosed();
+        if (artifactDescriptorReader == null) {
+            artifactDescriptorReader = createArtifactDescriptorReader();
+        }
+        return artifactDescriptorReader;
+    }
+
+    protected ArtifactDescriptorReader createArtifactDescriptorReader() {
+        // from maven-resolver-provider
+        return new DefaultArtifactDescriptorReader(
+                getRemoteRepositoryManager(),
+                getVersionResolver(),
+                getVersionRangeResolver(),
+                getArtifactResolver(),
+                getModelBuilder(),
+                getRepositoryEventDispatcher(),
+                getMavenArtifactRelocationSources());
+    }
+
+    private VersionResolver versionResolver;
+
+    public final VersionResolver getVersionResolver() {
+        checkClosed();
+        if (versionResolver == null) {
+            versionResolver = createVersionResolver();
+        }
+        return versionResolver;
+    }
+
+    protected VersionResolver createVersionResolver() {
+        // from maven-resolver-provider
+        return new DefaultVersionResolver(
+                getMetadataResolver(), getSyncContextFactory(), 
getRepositoryEventDispatcher());
+    }
+
+    private VersionRangeResolver versionRangeResolver;
+
+    public final VersionRangeResolver getVersionRangeResolver() {
+        checkClosed();
+        if (versionRangeResolver == null) {
+            versionRangeResolver = createVersionRangeResolver();
+        }
+        return versionRangeResolver;
+    }
+
+    protected VersionRangeResolver createVersionRangeResolver() {
+        // from maven-resolver-provider
+        return new DefaultVersionRangeResolver(
+                getMetadataResolver(), getSyncContextFactory(), 
getRepositoryEventDispatcher(), getVersionScheme());
+    }
+
+    private ModelBuilder modelBuilder;
+
+    public final ModelBuilder getModelBuilder() {
+        checkClosed();
+        if (modelBuilder == null) {
+            modelBuilder = createModelBuilder();
+        }
+        return modelBuilder;
+    }
+
+    protected ModelBuilder createModelBuilder() {
+        // from maven-model-builder
+        DefaultModelProcessor modelProcessor = new DefaultModelProcessor(new 
DefaultModelXmlFactory(), List.of());
+        return new DefaultModelBuilder(
+                modelProcessor,
+                new DefaultModelValidator(new DefaultModelVersionProcessor()),
+                new DefaultModelNormalizer(),
+                new DefaultModelInterpolator(
+                        new DefaultPathTranslator(), new 
DefaultUrlNormalizer(), new DefaultRootLocator()),
+                new DefaultModelPathTranslator(new DefaultPathTranslator()),
+                new DefaultModelUrlNormalizer(new DefaultUrlNormalizer()),
+                new DefaultSuperPomProvider(modelProcessor),
+                new DefaultInheritanceAssembler(),
+                new DefaultProfileSelector(),
+                new DefaultProfileInjector(),
+                new DefaultPluginManagementInjector(),
+                new DefaultDependencyManagementInjector(),
+                new DefaultDependencyManagementImporter(),
+                (m, r, b) -> m,
+                new DefaultPluginConfigurationExpander(),
+                new ProfileActivationFilePathInterpolator(new 
DefaultPathTranslator(), new DefaultRootLocator()),
+                new BuildModelTransformer(),
+                new DefaultModelVersionParser(getVersionScheme()));
+    }
+
+    private RepositorySystem repositorySystem;
+
+    public final RepositorySystem getRepositorySystem() {
+        checkClosed();
+        if (repositorySystem == null) {
+            repositorySystem = createRepositorySystem();
+        }
+        return repositorySystem;
+    }
+
+    protected RepositorySystem createRepositorySystem() {
+        return new DefaultRepositorySystem(
+                getVersionResolver(),
+                getVersionRangeResolver(),
+                getArtifactResolver(),
+                getMetadataResolver(),
+                getArtifactDescriptorReader(),
+                getDependencyCollector(),
+                getInstaller(),
+                getDeployer(),
+                getLocalRepositoryProvider(),
+                getSyncContextFactory(),
+                getRemoteRepositoryManager(),
+                getRepositorySystemLifecycle(),
+                getArtifactDecoratorFactories());
+    }
+
+    @Override
+    public RepositorySystem get() {
+        return getRepositorySystem();
+    }
+}
diff --git 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java
 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java
index 2b05567..799587a 100644
--- 
a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java
+++ 
b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java
@@ -22,17 +22,22 @@ import java.net.URI;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.LocalRepository;
 import org.apache.maven.api.Project;
 import org.apache.maven.api.RemoteRepository;
 import org.apache.maven.api.Session;
+import org.apache.maven.api.SessionData;
 import org.apache.maven.api.model.Model;
 import org.apache.maven.api.model.Repository;
 import org.apache.maven.api.services.ArtifactDeployer;
@@ -48,9 +53,14 @@ import org.apache.maven.api.services.ProjectBuilderRequest;
 import org.apache.maven.api.services.ProjectBuilderResult;
 import org.apache.maven.api.services.ProjectManager;
 import org.apache.maven.api.services.RepositoryFactory;
+import org.apache.maven.api.services.VersionParser;
 import org.apache.maven.api.services.xml.ModelXmlFactory;
 import org.apache.maven.internal.impl.DefaultModelXmlFactory;
+import org.apache.maven.internal.impl.DefaultVersionParser;
+import org.apache.maven.internal.impl.InternalSession;
 import org.apache.maven.model.v4.MavenStaxReader;
+import org.apache.maven.repository.internal.DefaultModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
 import org.mockito.ArgumentMatchers;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -67,17 +77,27 @@ import static org.mockito.Mockito.withSettings;
  */
 public class SessionStub {
 
-    public static Session getMockSession(String localRepo) {
+    public static InternalSession getMockSession(String localRepo) {
         LocalRepository localRepository = mock(LocalRepository.class);
         when(localRepository.getId()).thenReturn("local");
         when(localRepository.getPath()).thenReturn(Paths.get(localRepo));
         return getMockSession(localRepository);
     }
 
-    public static Session getMockSession(LocalRepository localRepository) {
-        Session session = mock(Session.class);
+    public static InternalSession getMockSession(LocalRepository 
localRepository) {
+        InternalSession session = mock(InternalSession.class);
 
+        //
+        // RepositoryFactory
+        //
         RepositoryFactory repositoryFactory = mock(RepositoryFactory.class);
+        when(session.createRemoteRepository(anyString(), 
anyString())).thenAnswer(iom -> {
+            String id = iom.getArgument(0, String.class);
+            String url = iom.getArgument(1, String.class);
+            return 
session.getService(RepositoryFactory.class).createRemote(id, url);
+        });
+        when(session.createRemoteRepository(any()))
+                .thenAnswer(iom -> 
repositoryFactory.createRemote(iom.getArgument(0, Repository.class)));
         
when(repositoryFactory.createRemote(any(Repository.class))).thenAnswer(iom -> {
             Repository repository = iom.getArgument(0, Repository.class);
             return repositoryFactory.createRemote(repository.getId(), 
repository.getUrl());
@@ -92,16 +112,56 @@ public class SessionStub {
             
when(remoteRepository.getProtocol()).thenReturn(URI.create(url).getScheme());
             return remoteRepository;
         });
+        
when(session.getService(RepositoryFactory.class)).thenReturn(repositoryFactory);
+
+        //
+        // VersionParser
+        //
+        VersionParser versionParser =
+                new DefaultVersionParser(new DefaultModelVersionParser(new 
GenericVersionScheme()));
+        when(session.parseVersion(any()))
+                .thenAnswer(iom -> 
versionParser.parseVersion(iom.getArgument(0, String.class)));
+        
when(session.getService(VersionParser.class)).thenReturn(versionParser);
 
+        //
+        // LocalRepositoryManager
+        //
         LocalRepositoryManager localRepositoryManager = 
mock(LocalRepositoryManager.class);
+        when(session.getPathForLocalArtifact(any(Artifact.class)))
+                .then(iom -> localRepositoryManager.getPathForLocalArtifact(
+                        session, session.getLocalRepository(), 
iom.getArgument(0, Artifact.class)));
+        when(session.getPathForRemoteArtifact(any(), any()))
+                .thenAnswer(iom -> 
localRepositoryManager.getPathForRemoteArtifact(
+                        session,
+                        session.getLocalRepository(),
+                        iom.getArgument(0, RemoteRepository.class),
+                        iom.getArgument(1, Artifact.class)));
         when(localRepositoryManager.getPathForLocalArtifact(any(), any(), 
any()))
                 .thenAnswer(iom -> {
                     LocalRepository localRepo = iom.getArgument(1, 
LocalRepository.class);
                     Artifact artifact = iom.getArgument(2, Artifact.class);
                     return 
localRepo.getPath().resolve(getPathForArtifact(artifact, true));
                 });
+        
when(session.getService(LocalRepositoryManager.class)).thenReturn(localRepositoryManager);
 
+        //
+        // ArtifactInstaller
+        //
         ArtifactInstaller artifactInstaller = mock(ArtifactInstaller.class);
+        doAnswer(iom -> {
+                    artifactInstaller.install(
+                            ArtifactInstallerRequest.build(session, 
iom.getArgument(0, Collection.class)));
+                    return null;
+                })
+                .when(session)
+                .installArtifacts(any(Collection.class));
+        doAnswer(iom -> {
+                    artifactInstaller.install(ArtifactInstallerRequest.build(
+                            session, Arrays.asList(iom.getArgument(0, 
Artifact[].class))));
+                    return null;
+                })
+                .when(session)
+                .installArtifacts(any(Artifact[].class));
         doAnswer(iom -> {
                     artifactInstaller.install(ArtifactInstallerRequest.build(
                             iom.getArgument(0, Session.class), 
iom.getArgument(1, Collection.class)));
@@ -109,8 +169,21 @@ public class SessionStub {
                 })
                 .when(artifactInstaller)
                 .install(any(Session.class), 
ArgumentMatchers.<Collection<Artifact>>any());
+        
when(session.getService(ArtifactInstaller.class)).thenReturn(artifactInstaller);
 
+        //
+        // ArtifactDeployer
+        //
         ArtifactDeployer artifactDeployer = mock(ArtifactDeployer.class);
+        doAnswer(iom -> {
+                    artifactDeployer.deploy(ArtifactDeployerRequest.build(
+                            iom.getArgument(0, Session.class),
+                            iom.getArgument(1, RemoteRepository.class),
+                            Arrays.asList(iom.getArgument(2, 
Artifact[].class))));
+                    return null;
+                })
+                .when(session)
+                .deployArtifact(any(), any());
         doAnswer(iom -> {
                     artifactDeployer.deploy(ArtifactDeployerRequest.build(
                             iom.getArgument(0, Session.class),
@@ -120,7 +193,11 @@ public class SessionStub {
                 })
                 .when(artifactDeployer)
                 .deploy(any(), any(), any());
+        
when(session.getService(ArtifactDeployer.class)).thenReturn(artifactDeployer);
 
+        //
+        // ArtifactManager
+        //
         ArtifactManager artifactManager = mock(ArtifactManager.class);
         Map<Artifact, Path> paths = new HashMap<>();
         doAnswer(iom -> {
@@ -132,7 +209,14 @@ public class SessionStub {
         doAnswer(iom -> Optional.ofNullable(paths.get(iom.getArgument(0, 
Artifact.class))))
                 .when(artifactManager)
                 .getPath(any());
+        doAnswer(iom -> artifactManager.getPath(iom.getArgument(0, 
Artifact.class)))
+                .when(session)
+                .getArtifactPath(any());
+        
when(session.getService(ArtifactManager.class)).thenReturn(artifactManager);
 
+        //
+        // ProjectManager
+        //
         ProjectManager projectManager = mock(ProjectManager.class);
         Map<Project, Collection<Artifact>> attachedArtifacts = new HashMap<>();
         doAnswer(iom -> {
@@ -164,7 +248,18 @@ public class SessionStub {
         when(projectManager.getAttachedArtifacts(any()))
                 .then(iom ->
                         attachedArtifacts.computeIfAbsent(iom.getArgument(0, 
Project.class), p -> new ArrayList<>()));
+        when(projectManager.getAllArtifacts(any())).then(iom -> {
+            Project project = iom.getArgument(0, Project.class);
+            List<Artifact> result = new ArrayList<>();
+            result.addAll(project.getArtifacts());
+            result.addAll(attachedArtifacts.computeIfAbsent(project, p -> new 
ArrayList<>()));
+            return result;
+        });
+        
when(session.getService(ProjectManager.class)).thenReturn(projectManager);
 
+        //
+        // ArtifactFactory
+        //
         ArtifactFactory artifactFactory = mock(ArtifactFactory.class);
         when(artifactFactory.create(any())).then(iom -> {
             ArtifactFactoryRequest request = iom.getArgument(0, 
ArtifactFactoryRequest.class);
@@ -180,40 +275,6 @@ public class SessionStub {
             return new ArtifactStub(
                     request.getGroupId(), request.getArtifactId(), classifier, 
request.getVersion(), extension);
         });
-
-        ProjectBuilder projectBuilder = mock(ProjectBuilder.class);
-        when(projectBuilder.build(any(ProjectBuilderRequest.class))).then(iom 
-> {
-            ProjectBuilderRequest request = iom.getArgument(0, 
ProjectBuilderRequest.class);
-            ProjectBuilderResult result = mock(ProjectBuilderResult.class);
-            Model model = new 
MavenStaxReader().read(request.getSource().get().openStream());
-            ProjectStub projectStub = new ProjectStub();
-            projectStub.setModel(model);
-            ArtifactStub artifactStub = new ArtifactStub(
-                    model.getGroupId(), model.getArtifactId(), "", 
model.getVersion(), model.getPackaging());
-            projectStub.setArtifact(artifactStub);
-            when(result.getProject()).thenReturn(Optional.of(projectStub));
-            return result;
-        });
-
-        Properties sysProps = new Properties();
-        Properties usrProps = new Properties();
-        doReturn(sysProps).when(session).getSystemProperties();
-        doReturn(usrProps).when(session).getUserProperties();
-
-        when(session.getLocalRepository()).thenReturn(localRepository);
-        
when(session.getService(RepositoryFactory.class)).thenReturn(repositoryFactory);
-        
when(session.getService(ProjectBuilder.class)).thenReturn(projectBuilder);
-        
when(session.getService(LocalRepositoryManager.class)).thenReturn(localRepositoryManager);
-        
when(session.getService(ProjectManager.class)).thenReturn(projectManager);
-        
when(session.getService(ArtifactManager.class)).thenReturn(artifactManager);
-        
when(session.getService(ArtifactInstaller.class)).thenReturn(artifactInstaller);
-        
when(session.getService(ArtifactDeployer.class)).thenReturn(artifactDeployer);
-        
when(session.getService(ArtifactFactory.class)).thenReturn(artifactFactory);
-        when(session.getService(ModelXmlFactory.class)).thenReturn(new 
DefaultModelXmlFactory());
-
-        when(session.getPathForLocalArtifact(any(Artifact.class)))
-                .then(iom -> localRepositoryManager.getPathForLocalArtifact(
-                        session, session.getLocalRepository(), 
iom.getArgument(0, Artifact.class)));
         when(session.createArtifact(any(), any(), any(), any(), any(), 
any())).thenAnswer(iom -> {
             String groupId = iom.getArgument(0, String.class);
             String artifactId = iom.getArgument(1, String.class);
@@ -246,17 +307,45 @@ public class SessionStub {
                             .extension(extension)
                             .build());
         });
-        when(session.createRemoteRepository(anyString(), 
anyString())).thenAnswer(iom -> {
-            String id = iom.getArgument(0, String.class);
-            String url = iom.getArgument(1, String.class);
-            return 
session.getService(RepositoryFactory.class).createRemote(id, url);
+        
when(session.getService(ArtifactFactory.class)).thenReturn(artifactFactory);
+
+        //
+        // ProjectBuilder
+        //
+        ProjectBuilder projectBuilder = mock(ProjectBuilder.class);
+        when(projectBuilder.build(any(ProjectBuilderRequest.class))).then(iom 
-> {
+            ProjectBuilderRequest request = iom.getArgument(0, 
ProjectBuilderRequest.class);
+            ProjectBuilderResult result = mock(ProjectBuilderResult.class);
+            Model model = new 
MavenStaxReader().read(request.getSource().get().openStream());
+            ProjectStub projectStub = new ProjectStub();
+            projectStub.setModel(model);
+            ArtifactStub artifactStub = new ArtifactStub(
+                    model.getGroupId(), model.getArtifactId(), "", 
model.getVersion(), model.getPackaging());
+            if (!"pom".equals(model.getPackaging())) {
+                projectStub.setMainArtifact(artifactStub);
+            }
+            when(result.getProject()).thenReturn(Optional.of(projectStub));
+            return result;
         });
-        doAnswer(iom -> artifactManager.getPath(iom.getArgument(0, 
Artifact.class)))
-                .when(session)
-                .getArtifactPath(any());
+        
when(session.getService(ProjectBuilder.class)).thenReturn(projectBuilder);
+
+        //
+        // ModelXmlFactory
+        //
+        when(session.getService(ModelXmlFactory.class)).thenReturn(new 
DefaultModelXmlFactory());
 
+        //
+        // Other
+        //
+        Properties sysProps = new Properties();
+        Properties usrProps = new Properties();
+        doReturn(sysProps).when(session).getSystemProperties();
+        doReturn(usrProps).when(session).getUserProperties();
+        when(session.getLocalRepository()).thenReturn(localRepository);
+        when(session.getData()).thenReturn(new TestSessionData());
         when(session.withLocalRepository(any()))
                 .thenAnswer(iom -> getMockSession(iom.getArgument(0, 
LocalRepository.class)));
+
         return session;
     }
 
@@ -275,4 +364,30 @@ public class SessionStub {
         }
         return path.toString();
     }
+
+    static class TestSessionData implements SessionData {
+        private final Map<Key<?>, Object> map = new ConcurrentHashMap<>();
+
+        @Override
+        public <T> void set(Key<T> key, T value) {
+            map.put(key, value);
+        }
+
+        @Override
+        public <T> boolean replace(Key<T> key, T oldValue, T newValue) {
+            return map.replace(key, oldValue, newValue);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T get(Key<T> key) {
+            return (T) map.get(key);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T computeIfAbsent(Key<T> key, Supplier<T> supplier) {
+            return (T) map.computeIfAbsent(key, k -> supplier.get());
+        }
+    }
 }
diff --git 
a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java
 
b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java
new file mode 100644
index 0000000..429a3f5
--- /dev/null
+++ 
b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api.di.testing;
+
+import java.io.File;
+
+import org.apache.maven.api.Session;
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.di.Provides;
+import org.apache.maven.api.plugin.testing.stubs.SessionStub;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.maven.api.di.testing.MavenDIExtension.getBasedir;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@MavenDITest
+public class SimpleDITest {
+
+    private static final String LOCAL_REPO = getBasedir() + File.separator + 
"target" + File.separator + "local-repo";
+
+    @Inject
+    Session session;
+
+    @Test
+    void testSession() {
+        assertNotNull(session);
+        assertNotNull(session.getLocalRepository());
+    }
+
+    @Provides
+    Session createSession() {
+        return SessionStub.getMockSession(LOCAL_REPO);
+    }
+}
diff --git 
a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
 
b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
index b287bbf..51b1ed9 100644
--- 
a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
+++ 
b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java
@@ -18,14 +18,17 @@
  */
 package org.apache.maven.api.plugin.testing;
 
-import javax.inject.Named;
-
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Properties;
 
-import com.google.inject.Provides;
+import org.apache.maven.api.Project;
 import org.apache.maven.api.Session;
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Provides;
 import org.apache.maven.api.plugin.MojoException;
+import org.apache.maven.api.plugin.annotations.Mojo;
+import org.apache.maven.api.plugin.testing.stubs.ProjectStub;
 import org.apache.maven.api.plugin.testing.stubs.SessionStub;
 import org.junit.jupiter.api.Test;
 
@@ -42,16 +45,18 @@ import static org.mockito.Mockito.doReturn;
 public class ExpressionEvaluatorTest {
 
     private static final String LOCAL_REPO = "target/local-repo/";
-    private static final String ARTIFACT_ID = "maven-test-mojo";
-    private static final String COORDINATES = "groupId:" + ARTIFACT_ID + 
":version:goal";
+    private static final String GROUP_ID = "test";
+    private static final String ARTIFACT_ID = "test-plugin";
+    private static final String COORDINATES = GROUP_ID + ":" + ARTIFACT_ID + 
":0.0.1-SNAPSHOT:goal";
     private static final String CONFIG = "<project>\n"
             + "    <build>\n"
             + "        <plugins>\n"
             + "            <plugin>\n"
+            + "                <groupId>" + GROUP_ID + "</groupId>\n"
             + "                <artifactId>" + ARTIFACT_ID + "</artifactId>\n"
             + "                <configuration>\n"
-            + "                    <basedir>${basedir}</basedir>\n"
-            + "                    
<workdir>${basedir}/workDirectory</workdir>\n"
+            + "                    <basedir>${project.basedir}</basedir>\n"
+            + "                    
<workdir>${project.basedir}/workDirectory</workdir>\n"
             + "                </configuration>\n"
             + "            </plugin>\n"
             + "        </plugins>\n"
@@ -68,6 +73,7 @@ public class ExpressionEvaluatorTest {
 
     @Test
     @InjectMojo(goal = COORDINATES, pom = CONFIG)
+    @Basedir("${basedir}/target/test-classes")
     @MojoParameter(name = "param", value = "paramValue")
     public void testParam(ExpressionEvaluatorMojo mojo) {
         assertNotNull(mojo.basedir);
@@ -88,11 +94,12 @@ public class ExpressionEvaluatorTest {
         assertDoesNotThrow(mojo::execute);
     }
 
-    @Named(COORDINATES)
+    @Mojo(name = "goal")
+    @Named("test:test-plugin:0.0.1-SNAPSHOT:goal") // this one is usually 
generated by maven-plugin-plugin
     public static class ExpressionEvaluatorMojo implements 
org.apache.maven.api.plugin.Mojo {
-        private String basedir;
+        private Path basedir;
 
-        private String workdir;
+        private Path workdir;
 
         private String param;
 
@@ -101,11 +108,11 @@ public class ExpressionEvaluatorTest {
         /** {@inheritDoc} */
         @Override
         public void execute() throws MojoException {
-            if (basedir == null || basedir.isEmpty()) {
+            if (basedir == null) {
                 throw new MojoException("basedir was not injected.");
             }
 
-            if (workdir == null || workdir.isEmpty()) {
+            if (workdir == null) {
                 throw new MojoException("workdir was not injected.");
             } else if (!workdir.startsWith(basedir)) {
                 throw new MojoException("workdir does not start with 
basedir.");
@@ -122,4 +129,11 @@ public class ExpressionEvaluatorTest {
         doAnswer(iom -> 
Paths.get(MojoExtension.getBasedir())).when(session).getRootDirectory();
         return session;
     }
+
+    @Provides
+    Project project() {
+        ProjectStub project = new ProjectStub();
+        project.setBasedir(Paths.get(MojoExtension.getBasedir()));
+        return project;
+    }
 }
diff --git a/.github/workflows/maven-verify.yml 
b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject
similarity index 74%
copy from .github/workflows/maven-verify.yml
copy to 
maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject
index fa7dc4b..2a771d7 100644
--- a/.github/workflows/maven-verify.yml
+++ 
b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject
@@ -1,3 +1,4 @@
+#
 # 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
@@ -6,7 +7,7 @@
 # "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
+#   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
@@ -14,16 +15,5 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
-name: Verify
-
-on:
-  push:
-  pull_request:
-
-jobs:
-  build:
-    name: Verify
-    uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4
-    with:
-      jdk-matrix: '[ "8", "11", "17" ]'
+#
+org.apache.maven.api.plugin.testing.ExpressionEvaluatorTest$ExpressionEvaluatorMojo
\ No newline at end of file
diff --git 
a/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml 
b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
index d442272..6657446 100644
--- a/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
+++ b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
@@ -20,7 +20,7 @@ specific language governing permissions and limitations
 under the License.
 -->
 
-<plugin>
+<plugin xmlns="http://maven.apache.org/PLUGIN/2.0.0"; >
   <name>test-plugin</name>
   <description></description>
   <groupId>test</groupId>
@@ -31,19 +31,31 @@ under the License.
   <inheritedByDefault>true</inheritedByDefault>
   <mojos>
     <mojo>
-      <goal>parameters</goal>
-      <requiresDirectInvocation>false</requiresDirectInvocation>
-      <requiresProject>true</requiresProject>
-      <requiresReports>false</requiresReports>
+      <goal>goal</goal>
+      <projectRequired>true</projectRequired>
       <aggregator>false</aggregator>
-      <requiresOnline>false</requiresOnline>
+      <onlineRequired>false</onlineRequired>
       <inheritedByDefault>true</inheritedByDefault>
       
<implementation>org.apache.maven.plugin.testing.ParametersMojo</implementation>
       <language>java</language>
-      <instantiationStrategy>per-lookup</instantiationStrategy>
-      <executionStrategy>once-per-session</executionStrategy>
-      <threadSafe>false</threadSafe>
       <parameters>
+        <parameter>
+          <name>basedir</name>
+          <type>java.nio.file.Path</type>
+        </parameter>
+        <parameter>
+          <name>workdir</name>
+          <type>java.nio.file.Path</type>
+        </parameter>
+        <parameter>
+          <name>param</name>
+          <type>java.lang.String</type>
+        </parameter>
+        <parameter>
+          <name>param2</name>
+          <type>java.lang.String</type>
+        </parameter>
+        <!--
         <parameter>
           <name>plain</name>
           <type>java.lang.String</type>
@@ -57,6 +69,7 @@ under the License.
           <required>false</required>
           <editable>true</editable>
           <description></description>
+          <defaultValue>default</defaultValue>
         </parameter>
         <parameter>
           <name>withProperty</name>
@@ -64,6 +77,7 @@ under the License.
           <required>false</required>
           <editable>true</editable>
           <description></description>
+          <expression>${property}</expression>
         </parameter>
         <parameter>
           <name>withPropertyAndDefault</name>
@@ -71,14 +85,11 @@ under the License.
           <required>false</required>
           <editable>true</editable>
           <description></description>
+          <defaultValue>default</defaultValue>
+          <expression>${property}</expression>
         </parameter>
+        -->
       </parameters>
-      <configuration>
-        <withDefault implementation="java.lang.String" 
default-value="default"/>
-        <withProperty 
implementation="java.lang.String">${property}</withProperty>
-        <withPropertyAndDefault implementation="java.lang.String" 
default-value="default">${property}</withPropertyAndDefault>
-      </configuration>
     </mojo>
   </mojos>
-  <dependencies/>
 </plugin>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 4cea8e8..6d530ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@ under the License.
   <parent>
     <groupId>org.apache.maven</groupId>
     <artifactId>maven-parent</artifactId>
-    <version>40</version>
+    <version>42</version>
     <relativePath />
   </parent>
 
@@ -65,9 +65,9 @@ under the License.
 
   <properties>
     <surefire.version>3.2.1</surefire.version>
-    <mavenVersion>4.0.0-alpha-8</mavenVersion>
+    <mavenVersion>4.0.0-beta-3</mavenVersion>
     <maven.site.path>plugin-testing-archives/LATEST</maven.site.path>
-    <javaVersion>8</javaVersion>
+    <javaVersion>17</javaVersion>
     
<project.build.outputTimestamp>2023-11-07T21:58:12Z</project.build.outputTimestamp>
   </properties>
 


Reply via email to