Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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 () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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())));

Expand Down
15 changes: 15 additions & 0 deletions impl/maven-di/src/main/java/org/apache/maven/di/Injector.java
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
23 changes: 23 additions & 0 deletions impl/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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 =
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
}
Loading