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

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


The following commit(s) were added to refs/heads/maven-4.0.x by this push:
     new acd66898b5 GH-11055: Inject all services into mojos and enable easy 
real-session mojo testing (#11103) (#11139)
acd66898b5 is described below

commit acd66898b5590be7bbac6b8088f4405cbee12a3d
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Mon Sep 22 10:05:03 2025 +0200

    GH-11055: Inject all services into mojos and enable easy real-session mojo 
testing (#11103) (#11139)
    
    Highlights
    - All services are injectable into mojos
      - Add InternalSession#getAllServices() to expose all DI service suppliers 
for the session (Map<Class<? extends Service>, Supplier<? extends Service>>)
      - Add Injector#bindSupplier(Class<T>, Supplier<T>) to bind a type 
directly to a Supplier
      - Adjust DI bootstrap/bindings (Injector/Binding/InjectorImpl, 
SisuDiBridgeModule) and touch minor call sites to respect the wiring
    - Mojos can be easily tested with a real session
      - @MojoTest(realSession=true) supported by MojoExtension; creates a real 
InternalSession via ApiRunner, otherwise uses SessionMock
      - New MojoRealSessionTest covers default/custom mock vs real-session paths
    
    Details
    - SessionScope alignment: when @Typed is empty, include only the class’s 
direct interfaces (not super-interfaces)
    - Testing support: SecDispatcherProvider for encrypted password handling 
without Sonatype dispatcher; expanded MojoTest for evaluator coverage
    - IT: add gh-11055-di-service-injection verifying DI service injection 
end-to-end with pre-populated resources under its/core-it-suite; integrated 
into TestSuiteOrdering
    
    (cherry picked from commit 6b7c9f2bd6a9c49a9e23508a96da0c4031bab74d)
---
 .../internal/impl/DefaultArtifactManager.java      |   9 +-
 .../maven/internal/impl/DefaultProjectManager.java |   8 +-
 .../maven/internal/impl/SisuDiBridgeModule.java    |  19 +++-
 .../maven/plugin/DefaultBuildPluginManager.java    |   7 ++
 .../plugin/internal/DefaultMavenPluginManager.java |   6 ++
 .../main/java/org/apache/maven/di/Injector.java    |  15 +++
 .../java/org/apache/maven/di/impl/Binding.java     |  23 +++++
 .../org/apache/maven/di/impl/InjectorImpl.java     |  11 +++
 .../org/apache/maven/impl/AbstractSession.java     |  47 +++++++++
 .../org/apache/maven/impl/InternalSession.java     |  11 +++
 .../org/apache/maven/impl/di/SessionScope.java     |  44 ++++-----
 .../org/apache/maven/impl/di/SessionScopeTest.java |  87 ++++++++++++++++
 impl/maven-testing/pom.xml                         |  12 +--
 .../maven/api/plugin/testing/MojoExtension.java    |  13 +++
 .../apache/maven/api/plugin/testing/MojoTest.java  |   8 +-
 .../api/plugin/testing/SecDispatcherProvider.java  |  85 ++++++++++++++++
 .../api/plugin/testing/MojoRealSessionTest.java    | 110 +++++++++++++++++++++
 .../it/MavenITgh11055DIServiceInjectionTest.java   |  46 +++++++++
 .../org/apache/maven/it/TestSuiteOrdering.java     |   1 +
 .../gh-11055-di-service-injection/pom.xml          | 101 +++++++++++++++++++
 .../src/it/inject-service/.mvn/maven.config        |   1 +
 .../src/it/inject-service/pom.xml                  |  50 ++++++++++
 .../src/it/settings.xml                            |  35 +++++++
 .../com/gitlab/tkslaw/ditests/DITestsMojoBase.java |  47 +++++++++
 .../gitlab/tkslaw/ditests/InjectServiceMojo.java   |  52 ++++++++++
 .../tkslaw/ditests/InjectServiceMojoTests.java     |  54 ++++++++++
 .../com/gitlab/tkslaw/ditests/TestProviders.java   |  76 ++++++++++++++
 27 files changed, 945 insertions(+), 33 deletions(-)

diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactManager.java
 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactManager.java
index 0f20222060..2b8692a86c 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactManager.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactManager.java
@@ -30,17 +30,24 @@
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.ProducedArtifact;
+import org.apache.maven.api.Service;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.di.SessionScoped;
 import org.apache.maven.api.services.ArtifactManager;
 import org.apache.maven.impl.DefaultArtifact;
+import org.apache.maven.impl.InternalSession;
 import org.apache.maven.project.MavenProject;
 import org.eclipse.sisu.Typed;
 
 import static java.util.Objects.requireNonNull;
 
+/**
+ * This implementation of {@code ArtifactManager} is explicitly bound to
+ * both {@code ArtifactManager} and {@code Service} interfaces so that it can 
be retrieved using
+ * {@link InternalSession#getAllServices()}.
+ */
 @Named
-@Typed
+@Typed({ArtifactManager.class, Service.class})
 @SessionScoped
 public class DefaultArtifactManager implements ArtifactManager {
 
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectManager.java
 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectManager.java
index c7d7505724..7c495a7344 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectManager.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectManager.java
@@ -38,6 +38,7 @@
 import org.apache.maven.api.Project;
 import org.apache.maven.api.ProjectScope;
 import org.apache.maven.api.RemoteRepository;
+import org.apache.maven.api.Service;
 import org.apache.maven.api.SourceRoot;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.di.SessionScoped;
@@ -52,8 +53,13 @@
 import static java.util.Objects.requireNonNull;
 import static org.apache.maven.internal.impl.CoreUtils.map;
 
+/**
+ * This implementation of {@code ProjectManager} is explicitly bound to
+ * both {@code ProjectManager} and {@code Service} interfaces so that it can 
be retrieved using
+ * {@link InternalSession#getAllServices()}.
+ */
 @Named
-@Typed
+@Typed({ProjectManager.class, Service.class})
 @SessionScoped
 public class DefaultProjectManager implements ProjectManager {
 
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
index c1e020b3ed..eeb6215f93 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
@@ -25,6 +25,7 @@
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -127,7 +128,7 @@ private static <U> com.google.inject.Key<U> 
toGuiceKey(Key<U> key) {
             } else if (key.getQualifier() instanceof Annotation a) {
                 return (com.google.inject.Key<U>) 
com.google.inject.Key.get(key.getType(), a);
             } else {
-                return (com.google.inject.Key<U>) 
com.google.inject.Key.get(key.getType());
+                return (com.google.inject.Key<U>) 
com.google.inject.Key.get(key.getType(), Named.class);
             }
         }
 
@@ -203,6 +204,22 @@ private <Q> Supplier<Q> getBeanSupplier(Dependency<Q> dep, 
Key<Q> key) {
             }
         }
 
+        @Override
+        public <T> Set<Binding<T>> getAllBindings(Class<T> clazz) {
+            Key<T> key = Key.of(clazz);
+            Set<Binding<T>> bindings = new HashSet<>();
+            Set<Binding<T>> diBindings = super.getBindings(key);
+            if (diBindings != null) {
+                bindings.addAll(diBindings);
+            }
+            for (var bean : locator.get().locate(toGuiceKey(key))) {
+                if (isPlexusBean(bean)) {
+                    bindings.add(new 
BindingToBeanEntry<>(Key.of(bean.getImplementationClass())).toBeanEntry(bean));
+                }
+            }
+            return bindings;
+        }
+
         private <Q> Supplier<Q> getListSupplier(Key<Q> key) {
             Key<Object> elementType = key.getTypeParameter(0);
             return () -> {
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
 
b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
index 6d9c76100d..e395d1ed00 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultBuildPluginManager.java
@@ -25,8 +25,11 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.maven.api.Project;
+import org.apache.maven.api.Service;
 import org.apache.maven.api.services.MavenException;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.execution.MojoExecutionEvent;
@@ -128,6 +131,10 @@ public void executeMojo(MavenSession session, 
MojoExecution mojoExecution)
             scope.seed(org.apache.maven.api.MojoExecution.class, new 
DefaultMojoExecution(sessionV4, mojoExecution));
 
             if (mojoDescriptor.isV4Api()) {
+                // For Maven 4 plugins, register a service so that they can be 
directly injected into plugins
+                Map<Class<? extends Service>, Supplier<? extends Service>> 
services = sessionV4.getAllServices();
+                services.forEach((itf, svc) -> scope.seed((Class<Service>) 
itf, (Supplier<Service>) svc));
+
                 org.apache.maven.api.plugin.Mojo mojoV4 = 
mavenPluginManager.getConfiguredMojo(
                         org.apache.maven.api.plugin.Mojo.class, session, 
mojoExecution);
                 mojo = new MojoWrapper(mojoV4);
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
 
b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 7d55730f5d..93c0d5a123 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Supplier;
 import java.util.jar.JarFile;
 import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
@@ -47,6 +48,7 @@
 import org.apache.maven.api.PathScope;
 import org.apache.maven.api.PathType;
 import org.apache.maven.api.Project;
+import org.apache.maven.api.Service;
 import org.apache.maven.api.Session;
 import org.apache.maven.api.plugin.descriptor.Resolution;
 import org.apache.maven.api.services.DependencyResolver;
@@ -565,6 +567,10 @@ private <T> T loadV4Mojo(
             injector.bindInstance(Project.class, project);
             injector.bindInstance(org.apache.maven.api.MojoExecution.class, 
execution);
             injector.bindInstance(org.apache.maven.api.plugin.Log.class, log);
+
+            Map<Class<? extends Service>, Supplier<? extends Service>> 
services = sessionV4.getAllServices();
+            services.forEach((itf, svc) -> 
injector.bindSupplier((Class<Service>) itf, (Supplier<Service>) svc));
+
             mojo = mojoInterface.cast(injector.getInstance(
                     Key.of(mojoDescriptor.getImplementationClass(), 
mojoDescriptor.getRoleHint())));
 
diff --git a/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java 
b/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java
index 8a95f0fd6f..e908b8e2fe 100644
--- a/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java
+++ b/impl/maven-di/src/main/java/org/apache/maven/di/Injector.java
@@ -123,6 +123,21 @@ static Injector create() {
     @Nonnull
     <T> Injector bindInstance(@Nonnull Class<T> cls, @Nonnull T instance);
 
+    /**
+     * Binds a specific instance supplier to a class type.
+     * <p>
+     * This method allows pre-created instances to be used for injection 
instead of
+     * having the injector create new instances.
+     *
+     * @param <T> the type of the instance
+     * @param cls the class to bind to
+     * @param supplier the supplier to use for injection
+     * @return this injector instance for method chaining
+     * @throws NullPointerException if either parameter is null
+     */
+    @Nonnull
+    <T> Injector bindSupplier(@Nonnull Class<T> cls, @Nonnull Supplier<T> 
supplier);
+
     /**
      * Performs field and method injection on an existing instance.
      * <p>
diff --git a/impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java 
b/impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
index a5da997d75..4caa7311b8 100644
--- a/impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
+++ b/impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
@@ -53,6 +53,10 @@ public static <T> Binding<T> toInstance(T instance) {
         return new BindingToInstance<>(instance);
     }
 
+    public static <T> Binding<T> toSupplier(Supplier<T> supplier) {
+        return new BindingToSupplier<>(supplier);
+    }
+
     public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> 
constructor, Class<?>[] types) {
         return Binding.to(
                 originalKey,
@@ -168,6 +172,25 @@ public String toString() {
         }
     }
 
+    public static class BindingToSupplier<T> extends Binding<T> {
+        final Supplier<T> supplier;
+
+        public BindingToSupplier(Supplier<T> supplier) {
+            super(null, Collections.emptySet());
+            this.supplier = supplier;
+        }
+
+        @Override
+        public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> 
compiler) {
+            return supplier;
+        }
+
+        @Override
+        public String toString() {
+            return "BindingToSupplier[" + supplier + "]" + getDependencies();
+        }
+    }
+
     public static class BindingToConstructor<T> extends Binding<T> {
         final TupleConstructorN<T> constructor;
         final Dependency<?>[] args;
diff --git 
a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java 
b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
index c3a069bca5..f2963b911b 100644
--- a/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
+++ b/impl/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
@@ -137,6 +137,13 @@ public <U> Injector bindInstance(@Nonnull Class<U> clazz, 
@Nonnull U instance) {
         return doBind(key, binding);
     }
 
+    @Override
+    public <U> Injector bindSupplier(@Nonnull Class<U> clazz, @Nonnull 
Supplier<U> supplier) {
+        Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
+        Binding<U> binding = Binding.toSupplier(supplier);
+        return doBind(key, binding);
+    }
+
     @Nonnull
     @Override
     public Injector bindImplicit(@Nonnull Class<?> clazz) {
@@ -195,6 +202,10 @@ public Map<Key<?>, Set<Binding<?>>> getBindings() {
         return bindings;
     }
 
+    public <T> Set<Binding<T>> getAllBindings(Class<T> clazz) {
+        return getBindings(Key.of(clazz));
+    }
+
     public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
         Key<Q> key = dep.key();
         Supplier<Q> originalSupplier = doGetCompiledBinding(dep);
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java 
b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
index 605a36b903..68e2d6b2a7 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
@@ -23,17 +23,20 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.ArtifactCoordinates;
@@ -96,6 +99,10 @@
 import org.apache.maven.api.services.VersionRangeResolver;
 import org.apache.maven.api.services.VersionResolver;
 import org.apache.maven.api.services.VersionResolverException;
+import org.apache.maven.di.Injector;
+import org.apache.maven.di.Key;
+import org.apache.maven.di.impl.Binding;
+import org.apache.maven.di.impl.InjectorImpl;
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositorySystem;
 import org.eclipse.aether.RepositorySystemSession;
@@ -112,6 +119,7 @@ public abstract class AbstractSession implements 
InternalSession {
     protected final RepositorySystem repositorySystem;
     protected final List<RemoteRepository> repositories;
     protected final Lookup lookup;
+    protected final Injector injector;
     private final Map<Class<? extends Service>, Service> services = new 
ConcurrentHashMap<>();
     private final List<Listener> listeners = new CopyOnWriteArrayList<>();
     private final Map<org.eclipse.aether.graph.DependencyNode, Node> allNodes =
@@ -138,6 +146,24 @@ public AbstractSession(
         this.repositorySystem = repositorySystem;
         this.repositories = getRepositories(repositories, 
resolverRepositories);
         this.lookup = lookup;
+        this.injector = lookup != null ? 
lookup.lookupOptional(Injector.class).orElse(null) : null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Stream<Class<? extends Service>> 
collectServiceInterfaces(Class<?> clazz) {
+        if (clazz == null) {
+            return Stream.empty();
+        } else if (clazz.isInterface()) {
+            return Stream.concat(
+                            Service.class.isAssignableFrom(clazz) ? 
Stream.of((Class<Service>) clazz) : Stream.empty(),
+                            
Stream.of(clazz.getInterfaces()).flatMap(AbstractSession::collectServiceInterfaces))
+                    .filter(itf -> itf != Service.class);
+        } else {
+            return Stream.concat(
+                            
Stream.of(clazz.getInterfaces()).flatMap(AbstractSession::collectServiceInterfaces),
+                            collectServiceInterfaces(clazz.getSuperclass()))
+                    .filter(itf -> itf != Service.class);
+        }
     }
 
     @Override
@@ -370,6 +396,27 @@ public <T extends Service> T getService(Class<T> clazz) 
throws NoSuchElementExce
         return t;
     }
 
+    @Override
+    public Map<Class<? extends Service>, Supplier<? extends Service>> 
getAllServices() {
+        Map<Class<? extends Service>, Supplier<? extends Service>> allServices 
= new HashMap<>(services.size());
+        // In case the injector is known, lazily populate the map to avoid 
creating all services upfront.
+        if (injector instanceof InjectorImpl injector) {
+            Set<Binding<Service>> bindings = 
injector.getAllBindings(Service.class);
+            bindings.stream()
+                    .map(Binding::getOriginalKey)
+                    .map(Key::getRawType)
+                    .flatMap(AbstractSession::collectServiceInterfaces)
+                    .distinct()
+                    .forEach(itf -> allServices.put(itf, () -> 
injector.getInstance(Key.of(itf))));
+        } else {
+            List<Service> services = this.injector.getInstance(new 
Key<List<Service>>() {});
+            for (Service service : services) {
+                collectServiceInterfaces(service.getClass()).forEach(itf -> 
allServices.put(itf, () -> service));
+            }
+        }
+        return allServices;
+    }
+
     private Service lookup(Class<? extends Service> c) {
         try {
             return lookup.lookup(c);
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java 
b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
index e39836e255..b3ce36d47b 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java
@@ -20,7 +20,9 @@
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.ArtifactCoordinates;
@@ -30,6 +32,7 @@
 import org.apache.maven.api.Node;
 import org.apache.maven.api.RemoteRepository;
 import org.apache.maven.api.Repository;
+import org.apache.maven.api.Service;
 import org.apache.maven.api.Session;
 import org.apache.maven.api.WorkspaceRepository;
 import org.apache.maven.api.annotations.Nonnull;
@@ -138,4 +141,12 @@ List<org.eclipse.aether.graph.Dependency> toDependencies(
      * @see RequestTraceHelper#enter(Session, Object) For the recommended way 
to manage traces
      */
     RequestTrace getCurrentTrace();
+
+    /**
+     * Retrieves a map of all services.
+     *
+     * @see #getService(Class)
+     */
+    @Nonnull
+    Map<Class<? extends Service>, Supplier<? extends Service>> 
getAllServices();
 }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/impl/di/SessionScope.java 
b/impl/maven-impl/src/main/java/org/apache/maven/impl/di/SessionScope.java
index b47c5acde8..cb8e818ea0 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/di/SessionScope.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/di/SessionScope.java
@@ -119,32 +119,32 @@ protected <T> Object dispatch(Key<T> key, Supplier<T> 
unscoped, Method method, O
     protected Class<?>[] getInterfaces(Class<?> superType) {
         if (superType.isInterface()) {
             return new Class<?>[] {superType};
-        } else {
-            for (Annotation a : superType.getAnnotations()) {
-                Class<? extends Annotation> annotationType = 
a.annotationType();
-                if (isTypeAnnotation(annotationType)) {
-                    try {
-                        Class<?>[] value =
-                                (Class<?>[]) 
annotationType.getMethod("value").invoke(a);
-                        if (value.length == 0) {
-                            value = superType.getInterfaces();
-                        }
-                        List<Class<?>> nonInterfaces =
-                                Stream.of(value).filter(c -> 
!c.isInterface()).toList();
-                        if (!nonInterfaces.isEmpty()) {
-                            throw new IllegalArgumentException(
-                                    "The Typed annotation must contain only 
interfaces but the following types are not: "
-                                            + nonInterfaces);
-                        }
-                        return value;
-                    } catch (NoSuchMethodException | IllegalAccessException | 
InvocationTargetException e) {
-                        throw new IllegalStateException(e);
+        }
+        for (Annotation a : superType.getAnnotations()) {
+            Class<? extends Annotation> annotationType = a.annotationType();
+            if (isTypeAnnotation(annotationType)) {
+                try {
+                    Class<?>[] value =
+                            (Class<?>[]) 
annotationType.getMethod("value").invoke(a);
+                    if (value.length == 0) {
+                        // Only direct interfaces implemented by the class
+                        value = superType.getInterfaces();
+                    }
+                    List<Class<?>> nonInterfaces =
+                            Stream.of(value).filter(c -> 
!c.isInterface()).toList();
+                    if (!nonInterfaces.isEmpty()) {
+                        throw new IllegalArgumentException(
+                                "The Typed annotation must contain only 
interfaces but the following types are not: "
+                                        + nonInterfaces);
                     }
+                    return value;
+                } catch (NoSuchMethodException | IllegalAccessException | 
InvocationTargetException e) {
+                    throw new IllegalStateException(e);
                 }
             }
-            throw new IllegalArgumentException("The use of session scoped 
proxies require "
-                    + "a org.eclipse.sisu.Typed or 
javax.enterprise.inject.Typed annotation");
         }
+        throw new IllegalArgumentException(
+                "The use of session scoped proxies require a 
org.apache.maven.api.di.Typed, org.eclipse.sisu.Typed or 
javax.enterprise.inject.Typed annotation");
     }
 
     protected boolean isTypeAnnotation(Class<? extends Annotation> 
annotationType) {
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/impl/di/SessionScopeTest.java 
b/impl/maven-impl/src/test/java/org/apache/maven/impl/di/SessionScopeTest.java
new file mode 100644
index 0000000000..043770e39d
--- /dev/null
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/impl/di/SessionScopeTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.impl.di;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.maven.api.di.Typed;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for SessionScope#getInterfaces behaviour with @Typed and 
session-scoped proxies.
+ */
+class SessionScopeTest {
+
+    interface A {}
+
+    interface B extends A {}
+
+    interface C {}
+
+    static class Base implements B {}
+
+    @Typed // no explicit interfaces: should collect all from hierarchy (B, A, 
C)
+    static class Impl extends Base implements C {}
+
+    @Typed({C.class}) // explicit interface list
+    static class ImplExplicit extends Base implements C {}
+
+    static class NoTyped extends Base implements C {}
+
+    static class ExposedSessionScope extends SessionScope {
+        Class<?>[] interfacesOf(Class<?> type) {
+            return getInterfaces(type);
+        }
+    }
+
+    @Test
+    void typedWithoutValuesIncludesOnlyDirectInterfaces() {
+        ExposedSessionScope scope = new ExposedSessionScope();
+        Class<?>[] itfs = scope.interfacesOf(Impl.class);
+        Set<Class<?>> set = Arrays.stream(itfs).collect(Collectors.toSet());
+        assertTrue(set.contains(C.class), "Should include only direct 
interfaces implemented by the class");
+        assertFalse(set.contains(B.class), "Should NOT include interfaces from 
superclass");
+        assertFalse(set.contains(A.class), "Should NOT include 
super-interfaces");
+        // Proxy should not include concrete classes
+        assertFalse(set.contains(Base.class));
+        assertFalse(set.contains(Impl.class));
+    }
+
+    @Test
+    void typedWithExplicitValuesRespectsExplicitInterfacesOnly() {
+        ExposedSessionScope scope = new ExposedSessionScope();
+        Class<?>[] itfs = scope.interfacesOf(ImplExplicit.class);
+        assertArrayEquals(new Class<?>[] {C.class}, itfs, "Only explicitly 
listed interfaces should be used");
+    }
+
+    @Test
+    void missingTypedAnnotationThrows() {
+        ExposedSessionScope scope = new ExposedSessionScope();
+        IllegalArgumentException ex =
+                assertThrows(IllegalArgumentException.class, () -> 
scope.interfacesOf(NoTyped.class));
+        assertTrue(ex.getMessage().contains("Typed"));
+    }
+}
diff --git a/impl/maven-testing/pom.xml b/impl/maven-testing/pom.xml
index 5e8d31ad02..6bef6bb4b0 100644
--- a/impl/maven-testing/pom.xml
+++ b/impl/maven-testing/pom.xml
@@ -76,21 +76,19 @@ under the License.
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <classifier>classes</classifier>
+    </dependency>
 
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
-      <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
     </dependency>
-    <dependency>
-      <groupId>com.google.inject</groupId>
-      <artifactId>guice</artifactId>
-      <classifier>classes</classifier>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 </project>
diff --git 
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
index e06ea7c58e..1a9bfff9c5 100644
--- 
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
+++ 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java
@@ -444,6 +444,19 @@ class Foo {
             @Singleton
             @Priority(-10)
             private InternalSession createSession() {
+                MojoTest mojoTest = 
context.getRequiredTestClass().getAnnotation(MojoTest.class);
+                if (mojoTest != null && mojoTest.realSession()) {
+                    // Try to create a real session using ApiRunner without 
compile-time dependency
+                    try {
+                        Class<?> apiRunner = 
Class.forName("org.apache.maven.impl.standalone.ApiRunner");
+                        Object session = 
apiRunner.getMethod("createSession").invoke(null);
+                        return (InternalSession) session;
+                    } catch (Throwable t) {
+                        // Explicit request: do not fall back; abort the test 
with details instead of mocking
+                        throw new org.opentest4j.TestAbortedException(
+                                "@MojoTest(realSession=true) requested but 
could not create a real session.", t);
+                    }
+                }
                 return SessionMock.getMockSession(getBasedir());
             }
 
diff --git 
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
index 81ff117de6..16ced3b9e2 100644
--- 
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
+++ 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
@@ -85,4 +85,10 @@
 @Retention(RetentionPolicy.RUNTIME)
 @ExtendWith(MojoExtension.class)
 @Target(ElementType.TYPE)
-public @interface MojoTest {}
+public @interface MojoTest {
+    /**
+     * If true, the test harness will provide a real Maven Session created by 
ApiRunner.createSession(),
+     * instead of the default mock session. Default is false.
+     */
+    boolean realSession() default false;
+}
diff --git 
a/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/SecDispatcherProvider.java
 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/SecDispatcherProvider.java
new file mode 100644
index 0000000000..3f7c676f4d
--- /dev/null
+++ 
b/impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/SecDispatcherProvider.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+
+import java.util.Map;
+
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Provides;
+import org.codehaus.plexus.components.secdispatcher.Cipher;
+import org.codehaus.plexus.components.secdispatcher.Dispatcher;
+import org.codehaus.plexus.components.secdispatcher.MasterSource;
+import 
org.codehaus.plexus.components.secdispatcher.internal.cipher.AESGCMNoPadding;
+import 
org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher;
+import 
org.codehaus.plexus.components.secdispatcher.internal.dispatchers.MasterDispatcher;
+import 
org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterSource;
+import 
org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterSource;
+import 
org.codehaus.plexus.components.secdispatcher.internal.sources.PinEntryMasterSource;
+import 
org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterSource;
+
+/**
+ * Delegate that offers just the minimal surface needed to decrypt settings.
+ */
+@SuppressWarnings("unused")
+@Named
+public class SecDispatcherProvider {
+
+    @Provides
+    @Named(LegacyDispatcher.NAME)
+    public static Dispatcher legacyDispatcher() {
+        return new LegacyDispatcher();
+    }
+
+    @Provides
+    @Named(MasterDispatcher.NAME)
+    public static Dispatcher masterDispatcher(
+            Map<String, Cipher> masterCiphers, Map<String, MasterSource> 
masterSources) {
+        return new MasterDispatcher(masterCiphers, masterSources);
+    }
+
+    @Provides
+    @Named(AESGCMNoPadding.CIPHER_ALG)
+    public static Cipher aesGcmNoPaddingCipher() {
+        return new AESGCMNoPadding();
+    }
+
+    @Provides
+    @Named(EnvMasterSource.NAME)
+    public static MasterSource envMasterSource() {
+        return new EnvMasterSource();
+    }
+
+    @Provides
+    @Named(GpgAgentMasterSource.NAME)
+    public static MasterSource gpgAgentMasterSource() {
+        return new GpgAgentMasterSource();
+    }
+
+    @Provides
+    @Named(PinEntryMasterSource.NAME)
+    public static MasterSource pinEntryMasterSource() {
+        return new PinEntryMasterSource();
+    }
+
+    @Provides
+    @Named(SystemPropertyMasterSource.NAME)
+    public static MasterSource systemPropertyMasterSource() {
+        return new SystemPropertyMasterSource();
+    }
+}
diff --git 
a/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/MojoRealSessionTest.java
 
b/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/MojoRealSessionTest.java
new file mode 100644
index 0000000000..114a649199
--- /dev/null
+++ 
b/impl/maven-testing/src/test/java/org/apache/maven/api/plugin/testing/MojoRealSessionTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+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.di.Singleton;
+import org.apache.maven.api.plugin.testing.stubs.SessionMock;
+import org.apache.maven.impl.standalone.ApiRunner;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests for @MojoTest(realSession=...) support.
+ */
+class MojoRealSessionTest {
+
+    @Nested
+    @MojoTest
+    class DefaultMock {
+        @Inject
+        Session session;
+
+        @Test
+        void hasMockSession() {
+            assertNotNull(session);
+            assertTrue(org.mockito.Mockito.mockingDetails(session).isMock());
+        }
+    }
+
+    @Nested
+    @MojoTest(realSession = true)
+    class RealSession {
+        @Inject
+        Session session;
+
+        @Test
+        void hasRealSession() {
+            assertNotNull(session);
+            // Real session must not be a Mockito mock
+            assertFalse(Mockito.mockingDetails(session).isMock());
+        }
+    }
+
+    @Nested
+    @MojoTest
+    class CustomMock {
+        @Inject
+        Session session;
+
+        @Provides
+        @Singleton
+        static Session createSession() {
+            return SessionMock.getMockSession("target/local-repo");
+        }
+
+        @Test
+        void hasCustomMockSession() {
+            assertNotNull(session);
+            assertTrue(Mockito.mockingDetails(session).isMock());
+        }
+    }
+
+    @Nested
+    @MojoTest(realSession = true)
+    class CustomRealOverridesFlag {
+        @Inject
+        Session session;
+
+        @Provides
+        @Singleton
+        static Session createSession() {
+            Path basedir = Paths.get(System.getProperty("basedir", ""));
+            Path localRepoPath = basedir.resolve("target/local-repo");
+            // Rely on DI discovery for SecDispatcherProvider to avoid 
duplicate bindings
+            return ApiRunner.createSession(null, localRepoPath);
+        }
+
+        @Test
+        void customProviderWinsOverFlag() {
+            assertNotNull(session);
+            assertFalse(Mockito.mockingDetails(session).isMock());
+        }
+    }
+}
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11055DIServiceInjectionTest.java
 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11055DIServiceInjectionTest.java
new file mode 100644
index 0000000000..199704bb60
--- /dev/null
+++ 
b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11055DIServiceInjectionTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.it;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test set for <a 
href="https://github.com/apache/maven/issues/11055";>gh-11055</a>.
+ *
+ * It reproduces the behavior difference between using Session::getService and 
field injection via @Inject
+ * for some core services.
+ */
+class MavenITgh11055DIServiceInjectionTest extends 
AbstractMavenIntegrationTestCase {
+
+    MavenITgh11055DIServiceInjectionTest() {
+        super("[4.0.0-rc-4,)");
+    }
+
+    @Test
+    void testGetServiceSucceeds() throws Exception {
+        File testDir = extractResources("/gh-11055-di-service-injection");
+
+        Verifier verifier = newVerifier(testDir.getAbsolutePath());
+        verifier.addCliArgument("verify");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+    }
+}
diff --git 
a/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java 
b/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
index 0f0dcecce7..cc0ee65b99 100644
--- a/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
@@ -103,6 +103,7 @@ public TestSuiteOrdering() {
          * the tests are to finishing. Newer tests are also more likely to 
fail, so this is
          * a fail fast technique as well.
          */
+        suite.addTestSuite(MavenITgh11055DIServiceInjectionTest.class);
         
suite.addTestSuite(MavenITgh11084ReactorReaderPreferConsumerPomTest.class);
         
suite.addTestSuite(MavenITgh10312TerminallyDeprecatedMethodInGuiceTest.class);
         suite.addTestSuite(MavenITgh10937QuotedPipesInMavenOptsTest.class);
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/pom.xml 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/pom.xml
new file mode 100644
index 0000000000..040b0b29ac
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0"; root="true">
+  <modelVersion>4.1.0</modelVersion>
+
+  <groupId>com.gitlab.tkslaw</groupId>
+  <artifactId>ditests-maven-plugin</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+  <packaging>maven-plugin</packaging>
+
+  <name>ditests-maven-plugin (IT gh-11055)</name>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <mavenVersion>4.0.0-SNAPSHOT</mavenVersion>
+    <mavenPluginPluginVersion>4.0.0-beta-1</mavenPluginPluginVersion>
+    <mavenCompilerPluginVersion>3.13.0</mavenCompilerPluginVersion>
+    <javaVersion>17</javaVersion>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-api-core</artifactId>
+      <version>${mavenVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-api-di</artifactId>
+      <version>${mavenVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-api-annotations</artifactId>
+      <version>${mavenVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-testing</artifactId>
+      <version>${mavenVersion}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>${mavenCompilerPluginVersion}</version>
+        <configuration>
+          <release>${javaVersion}</release>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>${mavenPluginPluginVersion}</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-invoker-plugin</artifactId>
+        <configuration>
+          <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+          <settingsFile>${project.basedir}/src/it/settings.xml</settingsFile>
+          
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
+        </configuration>
+        <executions>
+          <execution>
+            <id>integration-test</id>
+            <goals>
+              <goal>install</goal>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/.mvn/maven.config
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/.mvn/maven.config
new file mode 100644
index 0000000000..db6119c689
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/.mvn/maven.config
@@ -0,0 +1 @@
+-ntp
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/pom.xml
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/pom.xml
new file mode 100644
index 0000000000..d8a2995bfc
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/inject-service/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.1.0"; root="true">
+  <modelVersion>4.1.0</modelVersion>
+
+  <groupId>com.gitlab.tkslaw</groupId>
+  <artifactId>inject-service</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>3.6.1</version>
+        <configuration>
+          <rules>
+            <requireMavenVersion>
+              <version>@requiredMavenVersion@</version>
+            </requireMavenVersion>
+          </rules>
+        </configuration>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <phase>validate</phase>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>com.gitlab.tkslaw</groupId>
+        <artifactId>ditests-maven-plugin</artifactId>
+        <version>@project.version@</version>
+        <executions>
+          <execution>
+            <id>inject-service</id>
+            <goals>
+              <goal>inject-service</goal>
+            </goals>
+            <phase>validate</phase>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/settings.xml
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/settings.xml
new file mode 100644
index 0000000000..531c1fc24a
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/it/settings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<settings>
+  <profiles>
+    <profile>
+      <id>it-repo</id>
+      <repositories>
+        <repository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </repository>
+      </repositories>
+      <pluginRepositories>
+        <pluginRepository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </pluginRepository>
+      </pluginRepositories>
+    </profile>
+  </profiles>
+  <activeProfiles>
+    <activeProfile>it-repo</activeProfile>
+  </activeProfiles>
+</settings>
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/DITestsMojoBase.java
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/DITestsMojoBase.java
new file mode 100644
index 0000000000..813d18ddfd
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/DITestsMojoBase.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.gitlab.tkslaw.ditests;
+
+import org.apache.maven.api.Project;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.plugin.Log;
+import org.apache.maven.api.plugin.Mojo;
+
+public class DITestsMojoBase implements Mojo {
+    @Inject
+    protected Log log;
+
+    @Inject
+    protected Session session;
+
+    @Inject
+    protected Project project;
+
+    @Override
+    public void execute() {
+        log.info(() -> "log = " + log);
+        log.info(() -> "session = " + session);
+        log.info(() -> "project = " + project);
+    }
+
+    protected void logService(String name, Object service) {
+        log.info(() -> "   | %s = %s".formatted(name, service));
+    }
+}
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/InjectServiceMojo.java
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/InjectServiceMojo.java
new file mode 100644
index 0000000000..7cebc84ff0
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/main/java/com/gitlab/tkslaw/ditests/InjectServiceMojo.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.gitlab.tkslaw.ditests;
+
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.plugin.annotations.Mojo;
+import org.apache.maven.api.services.ArtifactManager;
+import org.apache.maven.api.services.DependencyResolver;
+import org.apache.maven.api.services.OsService;
+import org.apache.maven.api.services.ToolchainManager;
+
+@Mojo(name = "inject-service")
+public class InjectServiceMojo extends DITestsMojoBase {
+    @Inject
+    protected ArtifactManager artifactManager;
+
+    @Inject
+    protected DependencyResolver dependencyResolver;
+
+    @Inject
+    protected ToolchainManager toolchainManager;
+
+    @Inject
+    protected OsService osService;
+
+    @Override
+    public void execute() {
+        super.execute();
+
+        log.info("Logging services injected via @Inject");
+        logService("artifactManager", artifactManager);
+        logService("dependencyResolver", dependencyResolver);
+        logService("toolchainManager", toolchainManager);
+        logService("osService", osService);
+    }
+}
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/InjectServiceMojoTests.java
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/InjectServiceMojoTests.java
new file mode 100644
index 0000000000..0d66702097
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/InjectServiceMojoTests.java
@@ -0,0 +1,54 @@
+/*
+ * 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 com.gitlab.tkslaw.ditests;
+
+import org.apache.maven.api.plugin.testing.InjectMojo;
+import org.apache.maven.api.plugin.testing.MojoTest;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@MojoTest
+@DisplayName("Test InjectServiceMojo")
+class InjectServiceMojoTests {
+
+    @Test
+    @InjectMojo(goal = "inject-service")
+    @DisplayName("had its services injected by the DI container")
+    void testServicesNotNull(InjectServiceMojo mojo) {
+        // Preconditions
+        assertAll(
+                "Log, Session, and/or Project were not injected. This should 
not happen!",
+                () -> assertNotNull(mojo.log, "log"),
+                () -> assertNotNull(mojo.session, "session"),
+                () -> assertNotNull(mojo.project, "project"));
+
+        // Actual test
+        assertDoesNotThrow(mojo::execute, "InjectServiceMojo::execute");
+        assertAll(
+                "Services not injected by DI container",
+                () -> assertNotNull(mojo.artifactManager, "artifactManager"),
+                () -> assertNotNull(mojo.dependencyResolver, 
"dependencyResolver"),
+                () -> assertNotNull(mojo.toolchainManager, "toolchainManager"),
+                () -> assertNotNull(mojo.osService, "osService"));
+    }
+}
diff --git 
a/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/TestProviders.java
 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/TestProviders.java
new file mode 100644
index 0000000000..0933a15248
--- /dev/null
+++ 
b/its/core-it-suite/src/test/resources/gh-11055-di-service-injection/src/test/java/com/gitlab/tkslaw/ditests/TestProviders.java
@@ -0,0 +1,76 @@
+/*
+ * 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 com.gitlab.tkslaw.ditests;
+
+import java.util.Map;
+
+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.plugin.testing.stubs.SessionMock;
+import org.apache.maven.api.services.DependencyResolver;
+import org.apache.maven.api.services.OsService;
+import org.apache.maven.api.services.ToolchainManager;
+import org.apache.maven.impl.DefaultToolchainManager;
+import org.apache.maven.impl.InternalSession;
+import org.apache.maven.impl.model.DefaultOsService;
+import org.mockito.Mockito;
+
+import static org.apache.maven.api.plugin.testing.MojoExtension.getBasedir;
+
+@Named
+public class TestProviders {
+
+    @Provides
+    @Singleton
+    @SuppressWarnings("unused")
+    private static InternalSession getMockSession(
+            DependencyResolver dependencyResolver, ToolchainManager 
toolchainManager, OsService osService) {
+
+        InternalSession session = SessionMock.getMockSession(getBasedir());
+        
Mockito.when(session.getService(DependencyResolver.class)).thenReturn(dependencyResolver);
+        
Mockito.when(session.getService(ToolchainManager.class)).thenReturn(toolchainManager);
+        
Mockito.when(session.getService(OsService.class)).thenReturn(osService);
+        return session;
+    }
+
+    @Provides
+    @Priority(100)
+    @Singleton
+    @SuppressWarnings("unused")
+    private static DependencyResolver getMockDependencyResolver() {
+        return Mockito.mock(DependencyResolver.class);
+    }
+
+    @Provides
+    @Singleton
+    @SuppressWarnings("unused")
+    private static ToolchainManager getToolchainManager() {
+        return new DefaultToolchainManager(Map.of());
+    }
+
+    @Provides
+    @Singleton
+    @Priority(100)
+    @SuppressWarnings("unused")
+    private static OsService getOsService() {
+        return new DefaultOsService();
+    }
+}

Reply via email to