mirror of https://github.com/apache/maven.git
[MNG-8259] Improve Sisu / DI bridge (#1722)
This commit is contained in:
parent
30f52651a7
commit
deb15be3b6
|
@ -71,6 +71,10 @@ under the License.
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-api-settings</artifactId>
|
<artifactId>maven-api-settings</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-di</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven.resolver</groupId>
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
<artifactId>maven-resolver-api</artifactId>
|
<artifactId>maven-resolver-api</artifactId>
|
||||||
|
@ -132,11 +136,6 @@ under the License.
|
||||||
<groupId>org.assertj</groupId>
|
<groupId>org.assertj</groupId>
|
||||||
<artifactId>assertj-core</artifactId>
|
<artifactId>assertj-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.maven</groupId>
|
|
||||||
<artifactId>maven-di</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven.resolver</groupId>
|
<groupId>org.apache.maven.resolver</groupId>
|
||||||
<artifactId>maven-resolver-named-locks</artifactId>
|
<artifactId>maven-resolver-named-locks</artifactId>
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.impl.di;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
import org.apache.maven.di.Scope;
|
||||||
|
import org.apache.maven.di.impl.DIException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MojoExecutionScope
|
||||||
|
*/
|
||||||
|
public class MojoExecutionScope implements Scope {
|
||||||
|
|
||||||
|
protected static final class ScopeState {
|
||||||
|
private final Map<Key<?>, Supplier<?>> seeded = new HashMap<>();
|
||||||
|
|
||||||
|
private final Map<Key<?>, Object> provided = new HashMap<>();
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, Supplier<T> value) {
|
||||||
|
seeded.put(Key.of(clazz), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Object> provided() {
|
||||||
|
return provided.values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>();
|
||||||
|
|
||||||
|
public MojoExecutionScope() {}
|
||||||
|
|
||||||
|
public static <T> Supplier<T> seededKeySupplier(Class<? extends T> clazz) {
|
||||||
|
return () -> {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No instance of " + clazz.getName() + " is bound to the mojo execution scope.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enter() {
|
||||||
|
LinkedList<ScopeState> stack = values.get();
|
||||||
|
if (stack == null) {
|
||||||
|
stack = new LinkedList<>();
|
||||||
|
values.set(stack);
|
||||||
|
}
|
||||||
|
stack.addFirst(new ScopeState());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ScopeState getScopeState() {
|
||||||
|
LinkedList<ScopeState> stack = values.get();
|
||||||
|
if (stack == null || stack.isEmpty()) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
return stack.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exit() {
|
||||||
|
final LinkedList<ScopeState> stack = values.get();
|
||||||
|
if (stack == null || stack.isEmpty()) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
stack.removeFirst();
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
values.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, Supplier<T> value) {
|
||||||
|
getScopeState().seed(clazz, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, final T value) {
|
||||||
|
seed(clazz, (Supplier<T>) () -> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nonnull
|
||||||
|
public <T> Supplier<T> scope(@Nonnull Key<T> key, @Nonnull Supplier<T> unscoped) {
|
||||||
|
return () -> {
|
||||||
|
LinkedList<ScopeState> stack = values.get();
|
||||||
|
if (stack == null || stack.isEmpty()) {
|
||||||
|
throw new DIException("Cannot access " + key + " outside of a scoping block");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeState state = stack.getFirst();
|
||||||
|
|
||||||
|
Supplier<?> seeded = state.seeded.get(key);
|
||||||
|
|
||||||
|
if (seeded != null) {
|
||||||
|
return (T) seeded.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
T provided = (T) state.provided.get(key);
|
||||||
|
if (provided == null && unscoped != null) {
|
||||||
|
provided = unscoped.get();
|
||||||
|
state.provided.put(key, provided);
|
||||||
|
}
|
||||||
|
|
||||||
|
return provided;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.impl.di;
|
||||||
|
|
||||||
|
import org.apache.maven.di.impl.DIException;
|
||||||
|
|
||||||
|
public class OutOfScopeException extends DIException {
|
||||||
|
public OutOfScopeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutOfScopeException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* 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.internal.impl.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
import org.apache.maven.di.Scope;
|
||||||
|
import org.apache.maven.di.impl.Types;
|
||||||
|
|
||||||
|
public class SessionScope implements Scope {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScopeState
|
||||||
|
*/
|
||||||
|
protected static final class ScopeState {
|
||||||
|
private final Map<Key<?>, CachingProvider<?>> provided = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, Supplier<T> value) {
|
||||||
|
provided.put(Key.of(clazz), new CachingProvider<>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> Supplier<T> scope(Key<T> key, Supplier<T> unscoped) {
|
||||||
|
Supplier<?> provider = provided.computeIfAbsent(key, k -> new CachingProvider<>(unscoped));
|
||||||
|
return (Supplier<T>) provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<CachingProvider<?>> providers() {
|
||||||
|
return provided.values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final List<ScopeState> values = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public void enter() {
|
||||||
|
values.add(0, new ScopeState());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ScopeState getScopeState() {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
throw new OutOfScopeException("Cannot access session scope outside of a scoping block");
|
||||||
|
}
|
||||||
|
return values.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exit() {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
values.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, Supplier<T> value) {
|
||||||
|
getScopeState().seed(clazz, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void seed(Class<T> clazz, T value) {
|
||||||
|
seed(clazz, (Supplier<T>) () -> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Supplier<T> scope(Key<T> key, Supplier<T> unscoped) {
|
||||||
|
// Lazy evaluating provider
|
||||||
|
return () -> {
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
return createProxy(key, unscoped);
|
||||||
|
} else {
|
||||||
|
return getScopeState().scope(key, unscoped).get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected <T> T createProxy(Key<T> key, Supplier<T> unscoped) {
|
||||||
|
InvocationHandler dispatcher = (proxy, method, args) -> dispatch(key, unscoped, method, args);
|
||||||
|
Class<T> superType = (Class<T>) Types.getRawType(key.getType());
|
||||||
|
Class<?>[] interfaces = getInterfaces(superType);
|
||||||
|
return (T) java.lang.reflect.Proxy.newProxyInstance(superType.getClassLoader(), interfaces, dispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> Object dispatch(Key<T> key, Supplier<T> unscoped, Method method, Object[] args) throws Throwable {
|
||||||
|
method.setAccessible(true);
|
||||||
|
try {
|
||||||
|
return method.invoke(getScopeState().scope(key, unscoped).get(), args);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw e.getCause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("The use of session scoped proxies require "
|
||||||
|
+ "a org.eclipse.sisu.Typed or javax.enterprise.inject.Typed annotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isTypeAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return "org.apache.maven.api.di.Typed".equals(annotationType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A provider wrapping an existing provider with a cache
|
||||||
|
* @param <T> the provided type
|
||||||
|
*/
|
||||||
|
protected static class CachingProvider<T> implements Supplier<T> {
|
||||||
|
private final Supplier<T> provider;
|
||||||
|
private volatile T value;
|
||||||
|
|
||||||
|
CachingProvider(Supplier<T> provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
if (value == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (value == null) {
|
||||||
|
value = provider.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Supplier<T> seededKeySupplier(Class<? extends T> clazz) {
|
||||||
|
return () -> {
|
||||||
|
throw new IllegalStateException("No instance of " + clazz.getName() + " is bound to the session scope.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import org.apache.maven.api.Session;
|
||||||
import org.apache.maven.api.Type;
|
import org.apache.maven.api.Type;
|
||||||
import org.apache.maven.api.Version;
|
import org.apache.maven.api.Version;
|
||||||
import org.apache.maven.api.di.Provides;
|
import org.apache.maven.api.di.Provides;
|
||||||
|
import org.apache.maven.api.di.SessionScoped;
|
||||||
import org.apache.maven.api.model.PluginContainer;
|
import org.apache.maven.api.model.PluginContainer;
|
||||||
import org.apache.maven.api.model.Profile;
|
import org.apache.maven.api.model.Profile;
|
||||||
import org.apache.maven.api.services.ArtifactManager;
|
import org.apache.maven.api.services.ArtifactManager;
|
||||||
|
@ -58,6 +59,7 @@ import org.apache.maven.di.Key;
|
||||||
import org.apache.maven.di.impl.DIException;
|
import org.apache.maven.di.impl.DIException;
|
||||||
import org.apache.maven.internal.impl.AbstractSession;
|
import org.apache.maven.internal.impl.AbstractSession;
|
||||||
import org.apache.maven.internal.impl.InternalSession;
|
import org.apache.maven.internal.impl.InternalSession;
|
||||||
|
import org.apache.maven.internal.impl.di.SessionScope;
|
||||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||||
import org.eclipse.aether.RepositorySystem;
|
import org.eclipse.aether.RepositorySystem;
|
||||||
import org.eclipse.aether.RepositorySystemSession;
|
import org.eclipse.aether.RepositorySystemSession;
|
||||||
|
@ -75,7 +77,12 @@ public class ApiRunner {
|
||||||
injector.bindInstance(Injector.class, injector);
|
injector.bindInstance(Injector.class, injector);
|
||||||
injector.bindImplicit(ApiRunner.class);
|
injector.bindImplicit(ApiRunner.class);
|
||||||
injector.discover(ApiRunner.class.getClassLoader());
|
injector.discover(ApiRunner.class.getClassLoader());
|
||||||
return injector.getInstance(Session.class);
|
Session session = injector.getInstance(Session.class);
|
||||||
|
SessionScope scope = new SessionScope();
|
||||||
|
scope.enter();
|
||||||
|
scope.seed(Session.class, session);
|
||||||
|
injector.bindScope(SessionScoped.class, scope);
|
||||||
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DefaultSession extends AbstractSession {
|
static class DefaultSession extends AbstractSession {
|
||||||
|
|
|
@ -18,20 +18,13 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.execution.scope.internal;
|
package org.apache.maven.execution.scope.internal;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.OutOfScopeException;
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Scope;
|
import com.google.inject.Scope;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Named;
|
||||||
import com.google.inject.util.Providers;
|
|
||||||
import org.apache.maven.execution.MojoExecutionEvent;
|
import org.apache.maven.execution.MojoExecutionEvent;
|
||||||
import org.apache.maven.execution.MojoExecutionListener;
|
import org.apache.maven.execution.MojoExecutionListener;
|
||||||
import org.apache.maven.execution.scope.WeakMojoExecutionListener;
|
import org.apache.maven.execution.scope.WeakMojoExecutionListener;
|
||||||
|
@ -40,96 +33,22 @@ import org.apache.maven.plugin.MojoExecutionException;
|
||||||
/**
|
/**
|
||||||
* MojoExecutionScope
|
* MojoExecutionScope
|
||||||
*/
|
*/
|
||||||
public class MojoExecutionScope implements Scope, org.apache.maven.di.Scope, MojoExecutionListener {
|
public class MojoExecutionScope extends org.apache.maven.internal.impl.di.MojoExecutionScope
|
||||||
private static final Provider<Object> SEEDED_KEY_PROVIDER = () -> {
|
implements Scope, MojoExecutionListener {
|
||||||
throw new IllegalStateException();
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final class ScopeState {
|
|
||||||
private final Map<Key<?>, Provider<?>> seeded = new HashMap<>();
|
|
||||||
|
|
||||||
private final Map<Key<?>, Object> provided = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ThreadLocal<LinkedList<ScopeState>> values = new ThreadLocal<>();
|
|
||||||
|
|
||||||
public MojoExecutionScope() {}
|
|
||||||
|
|
||||||
public void enter() {
|
|
||||||
LinkedList<ScopeState> stack = values.get();
|
|
||||||
if (stack == null) {
|
|
||||||
stack = new LinkedList<>();
|
|
||||||
values.set(stack);
|
|
||||||
}
|
|
||||||
stack.addFirst(new ScopeState());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScopeState getScopeState() {
|
|
||||||
LinkedList<ScopeState> stack = values.get();
|
|
||||||
if (stack == null || stack.isEmpty()) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
return stack.getFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exit() throws MojoExecutionException {
|
|
||||||
final LinkedList<ScopeState> stack = values.get();
|
|
||||||
if (stack == null || stack.isEmpty()) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
stack.removeFirst();
|
|
||||||
if (stack.isEmpty()) {
|
|
||||||
values.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void seed(Class<T> clazz, Provider<T> value) {
|
public <T> void seed(Class<T> clazz, Provider<T> value) {
|
||||||
getScopeState().seeded.put(Key.get(clazz), value);
|
getScopeState().seed(clazz, value::get);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void seed(Class<T> clazz, final T value) {
|
public <T> Provider<T> scope(final Key<T> key, Provider<T> unscoped) {
|
||||||
getScopeState().seeded.put(Key.get(clazz), Providers.of(value));
|
Object qualifier = key.getAnnotation() instanceof Named n ? n.value() : key.getAnnotation();
|
||||||
|
org.apache.maven.di.Key<T> k =
|
||||||
|
org.apache.maven.di.Key.ofType(key.getTypeLiteral().getType(), qualifier);
|
||||||
|
return scope(k, unscoped::get)::get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
|
public static <T> Provider<T> seededKeyProvider(Class<? extends T> clazz) {
|
||||||
return () -> {
|
return MojoExecutionScope.<T>seededKeySupplier(clazz)::get;
|
||||||
LinkedList<ScopeState> stack = values.get();
|
|
||||||
if (stack == null || stack.isEmpty()) {
|
|
||||||
throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopeState state = stack.getFirst();
|
|
||||||
|
|
||||||
Provider<?> seeded = state.seeded.get(key);
|
|
||||||
|
|
||||||
if (seeded != null) {
|
|
||||||
return (T) seeded.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
T provided = (T) state.provided.get(key);
|
|
||||||
if (provided == null && unscoped != null) {
|
|
||||||
provided = unscoped.get();
|
|
||||||
state.provided.put(key, provided);
|
|
||||||
}
|
|
||||||
|
|
||||||
return provided;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Supplier<T> scope(org.apache.maven.di.Key<T> key, Annotation scope, Supplier<T> unscoped) {
|
|
||||||
Object qualifier = key.getQualifier();
|
|
||||||
Key k = qualifier != null
|
|
||||||
? Key.get(key.getType(), qualifier instanceof String s ? Names.named(s) : (Annotation) qualifier)
|
|
||||||
: Key.get(key.getType());
|
|
||||||
Provider<T> up = unscoped::get;
|
|
||||||
Provider<T> p = scope(k, up);
|
|
||||||
return p::get;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
|
||||||
public static <T> Provider<T> seededKeyProvider() {
|
|
||||||
return (Provider<T>) SEEDED_KEY_PROVIDER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException {
|
public void beforeMojoExecution(MojoExecutionEvent event) throws MojoExecutionException {
|
||||||
|
@ -154,7 +73,7 @@ public class MojoExecutionScope implements Scope, org.apache.maven.di.Scope, Moj
|
||||||
// the same instance can be provided multiple times under different Key's
|
// the same instance can be provided multiple times under different Key's
|
||||||
// deduplicate instances to avoid redundant beforeXXX/afterXXX callbacks
|
// deduplicate instances to avoid redundant beforeXXX/afterXXX callbacks
|
||||||
IdentityHashMap<WeakMojoExecutionListener, Object> listeners = new IdentityHashMap<>();
|
IdentityHashMap<WeakMojoExecutionListener, Object> listeners = new IdentityHashMap<>();
|
||||||
for (Object provided : getScopeState().provided.values()) {
|
for (Object provided : getScopeState().provided()) {
|
||||||
if (provided instanceof WeakMojoExecutionListener) {
|
if (provided instanceof WeakMojoExecutionListener) {
|
||||||
listeners.put((WeakMojoExecutionListener) provided, null);
|
listeners.put((WeakMojoExecutionListener) provided, null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,17 +40,19 @@ public class MojoExecutionScopeModule extends AbstractModule {
|
||||||
// bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
|
// bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
|
||||||
bind(MojoExecutionScope.class).toInstance(scope);
|
bind(MojoExecutionScope.class).toInstance(scope);
|
||||||
bind(MavenProject.class)
|
bind(MavenProject.class)
|
||||||
.toProvider(MojoExecutionScope.seededKeyProvider())
|
.toProvider(MojoExecutionScope.seededKeyProvider(MavenProject.class))
|
||||||
.in(scope);
|
.in(scope);
|
||||||
bind(MojoExecution.class)
|
bind(MojoExecution.class)
|
||||||
.toProvider(MojoExecutionScope.seededKeyProvider())
|
.toProvider(MojoExecutionScope.seededKeyProvider(MojoExecution.class))
|
||||||
|
.in(scope);
|
||||||
|
bind(Log.class)
|
||||||
|
.toProvider(MojoExecutionScope.seededKeyProvider(Log.class))
|
||||||
.in(scope);
|
.in(scope);
|
||||||
bind(Log.class).toProvider(MojoExecutionScope.seededKeyProvider()).in(scope);
|
|
||||||
bind(org.apache.maven.api.Project.class)
|
bind(org.apache.maven.api.Project.class)
|
||||||
.toProvider(MojoExecutionScope.seededKeyProvider())
|
.toProvider(MojoExecutionScope.seededKeyProvider(org.apache.maven.api.Project.class))
|
||||||
.in(scope);
|
.in(scope);
|
||||||
bind(org.apache.maven.api.MojoExecution.class)
|
bind(org.apache.maven.api.MojoExecution.class)
|
||||||
.toProvider(MojoExecutionScope.seededKeyProvider())
|
.toProvider(MojoExecutionScope.seededKeyProvider(org.apache.maven.api.MojoExecution.class))
|
||||||
.in(scope);
|
.in(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,31 +21,27 @@ package org.apache.maven.internal.impl;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.net.URL;
|
import java.lang.reflect.Field;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.binder.AnnotatedBindingBuilder;
|
import com.google.inject.Binder;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Names;
|
||||||
|
import com.google.inject.spi.ProviderInstanceBinding;
|
||||||
import org.apache.maven.api.di.MojoExecutionScoped;
|
import org.apache.maven.api.di.MojoExecutionScoped;
|
||||||
import org.apache.maven.api.di.SessionScoped;
|
import org.apache.maven.api.di.SessionScoped;
|
||||||
import org.apache.maven.api.services.MavenException;
|
|
||||||
import org.apache.maven.di.Injector;
|
import org.apache.maven.di.Injector;
|
||||||
import org.apache.maven.di.Key;
|
import org.apache.maven.di.Key;
|
||||||
|
import org.apache.maven.di.Scope;
|
||||||
import org.apache.maven.di.impl.Binding;
|
import org.apache.maven.di.impl.Binding;
|
||||||
import org.apache.maven.di.impl.DIException;
|
import org.apache.maven.di.impl.DIException;
|
||||||
import org.apache.maven.di.impl.Dependency;
|
import org.apache.maven.di.impl.Dependency;
|
||||||
|
@ -54,76 +50,147 @@ import org.apache.maven.execution.scope.internal.MojoExecutionScope;
|
||||||
import org.apache.maven.session.scope.internal.SessionScope;
|
import org.apache.maven.session.scope.internal.SessionScope;
|
||||||
import org.codehaus.plexus.PlexusContainer;
|
import org.codehaus.plexus.PlexusContainer;
|
||||||
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
||||||
|
import org.eclipse.sisu.BeanEntry;
|
||||||
|
import org.eclipse.sisu.inject.BeanLocator;
|
||||||
|
|
||||||
@Named
|
@Named
|
||||||
public class SisuDiBridgeModule extends AbstractModule {
|
public class SisuDiBridgeModule extends AbstractModule {
|
||||||
|
|
||||||
InjectorImpl injector;
|
protected final boolean discover;
|
||||||
final Set<String> loaded = new HashSet<>();
|
protected InjectorImpl injector;
|
||||||
|
|
||||||
|
public SisuDiBridgeModule() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SisuDiBridgeModule(boolean discover) {
|
||||||
|
this.discover = discover;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
Provider<PlexusContainer> containerProvider = getProvider(PlexusContainer.class);
|
Provider<PlexusContainer> containerProvider = getProvider(PlexusContainer.class);
|
||||||
|
Provider<BeanLocator> beanLocatorProvider = getProvider(BeanLocator.class);
|
||||||
|
injector = new BridgeInjectorImpl(beanLocatorProvider, binder());
|
||||||
|
bindScope(injector, containerProvider, SessionScoped.class, SessionScope.class);
|
||||||
|
bindScope(injector, containerProvider, MojoExecutionScoped.class, MojoExecutionScope.class);
|
||||||
|
injector.bindInstance(Injector.class, injector);
|
||||||
|
bind(Injector.class).toInstance(injector);
|
||||||
|
bind(SisuDiBridgeModule.class).toInstance(this);
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
if (discover) {
|
||||||
|
injector.discover(classLoader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindScope(
|
||||||
|
InjectorImpl injector,
|
||||||
|
Provider<PlexusContainer> containerProvider,
|
||||||
|
Class<? extends Annotation> sa,
|
||||||
|
Class<? extends Scope> ss) {
|
||||||
|
injector.bindScope(sa, () -> {
|
||||||
|
try {
|
||||||
|
return containerProvider.get().lookup(ss);
|
||||||
|
} catch (ComponentLookupException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BridgeInjectorImpl extends InjectorImpl {
|
||||||
|
final Provider<BeanLocator> locator;
|
||||||
|
final Binder binder;
|
||||||
|
|
||||||
|
BridgeInjectorImpl(Provider<BeanLocator> locator, Binder binder) {
|
||||||
|
this.locator = locator;
|
||||||
|
this.binder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <U> Injector bind(Key<U> key, Binding<U> binding) {
|
||||||
|
super.bind(key, binding);
|
||||||
|
if (key.getQualifier() != null) {
|
||||||
|
com.google.inject.Key<U> k = toGuiceKey(key);
|
||||||
|
this.binder.bind(k).toProvider(new BridgeProvider<>(binding));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <U> com.google.inject.Key<U> toGuiceKey(Key<U> key) {
|
||||||
|
if (key.getQualifier() instanceof String s) {
|
||||||
|
return (com.google.inject.Key<U>) com.google.inject.Key.get(key.getType(), Names.named(s));
|
||||||
|
} 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BindingToBeanEntry<T> extends Binding<T> {
|
||||||
|
private BeanEntry<Annotation, T> beanEntry;
|
||||||
|
|
||||||
|
BindingToBeanEntry(Key<T> elementType) {
|
||||||
|
super(elementType, Set.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BindingToBeanEntry<T> toBeanEntry(BeanEntry<Annotation, T> beanEntry) {
|
||||||
|
this.beanEntry = beanEntry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
injector = new InjectorImpl() {
|
|
||||||
@Override
|
@Override
|
||||||
public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
|
public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
|
||||||
Key<Q> key = dep.key();
|
return beanEntry.getProvider()::get;
|
||||||
Set<Binding<Q>> res = getBindings(key);
|
}
|
||||||
if (res != null && !res.isEmpty()) {
|
}
|
||||||
List<Binding<Q>> bindingList = new ArrayList<>(res);
|
|
||||||
Comparator<Binding<Q>> comparing = Comparator.comparing(Binding::getPriority);
|
class BridgeProvider<T> implements Provider<T> {
|
||||||
bindingList.sort(comparing.reversed());
|
final Binding<T> binding;
|
||||||
Binding<Q> binding = bindingList.get(0);
|
|
||||||
return compile(binding);
|
BridgeProvider(Binding<T> binding) {
|
||||||
}
|
this.binding = binding;
|
||||||
if (key.getRawType() == List.class) {
|
}
|
||||||
Set<Binding<Object>> res2 = getBindings(key.getTypeParameter(0));
|
|
||||||
Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
|
@Override
|
||||||
try {
|
public T get() {
|
||||||
List<Object> l = containerProvider
|
return compile(binding).get();
|
||||||
.get()
|
}
|
||||||
.lookupList(key.getTypeParameter(0).getRawType());
|
}
|
||||||
l.forEach(o -> res3.add(new Binding.BindingToInstance<>(o)));
|
|
||||||
} catch (Throwable e) {
|
@Override
|
||||||
// ignore
|
public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
|
||||||
e.printStackTrace();
|
Key<Q> key = dep.key();
|
||||||
}
|
Class<Q> rawType = key.getRawType();
|
||||||
List<Supplier<Object>> list =
|
if (rawType == List.class) {
|
||||||
res3.stream().map(this::compile).collect(Collectors.toList());
|
return getListSupplier(key);
|
||||||
//noinspection unchecked
|
} else if (rawType == Map.class) {
|
||||||
return () -> (Q) list(list);
|
return getMapSupplier(key);
|
||||||
}
|
} else {
|
||||||
if (key.getRawType() == Map.class) {
|
return getBeanSupplier(dep, key);
|
||||||
Key<?> k = key.getTypeParameter(0);
|
}
|
||||||
Key<Object> v = key.getTypeParameter(1);
|
}
|
||||||
if (k.getRawType() == String.class) {
|
|
||||||
Set<Binding<Object>> res2 = getBindings(v);
|
private <Q> Supplier<Q> getBeanSupplier(Dependency<Q> dep, Key<Q> key) {
|
||||||
Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
|
List<Binding<?>> list = new ArrayList<>();
|
||||||
Map<String, Supplier<Object>> map = res3.stream()
|
// Add DI bindings
|
||||||
.filter(b -> b.getOriginalKey() == null
|
list.addAll(getBindings().getOrDefault(key, Set.of()));
|
||||||
|| b.getOriginalKey().getQualifier() == null
|
// Add Plexus bindings
|
||||||
|| b.getOriginalKey().getQualifier() instanceof String)
|
for (var bean : locator.get().locate(toGuiceKey(key))) {
|
||||||
.collect(Collectors.toMap(
|
if (isPlexusBean(bean)) {
|
||||||
b -> (String)
|
list.add(new BindingToBeanEntry<>(key).toBeanEntry(bean));
|
||||||
(b.getOriginalKey() != null
|
|
||||||
? b.getOriginalKey().getQualifier()
|
|
||||||
: null),
|
|
||||||
this::compile));
|
|
||||||
//noinspection unchecked
|
|
||||||
return () -> (Q) map(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Q t = containerProvider.get().lookup(key.getRawType());
|
|
||||||
return compile(new Binding.BindingToInstance<>(t));
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// ignore
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
if (dep.optional()) {
|
|
||||||
return () -> null;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
list.sort(getBindingComparator());
|
||||||
|
//noinspection unchecked
|
||||||
|
return () -> (Q) getInstance(list.iterator().next());
|
||||||
|
} else if (dep.optional()) {
|
||||||
|
return () -> null;
|
||||||
|
} else {
|
||||||
throw new DIException("No binding to construct an instance for key "
|
throw new DIException("No binding to construct an instance for key "
|
||||||
+ key.getDisplayString() + ". Existing bindings:\n"
|
+ key.getDisplayString() + ". Existing bindings:\n"
|
||||||
+ getBoundKeys().stream()
|
+ getBoundKeys().stream()
|
||||||
|
@ -133,80 +200,78 @@ public class SisuDiBridgeModule extends AbstractModule {
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.joining("\n - ", " - ", "")));
|
.collect(Collectors.joining("\n - ", " - ", "")));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
injector.bindScope(SessionScoped.class, () -> {
|
|
||||||
try {
|
|
||||||
return containerProvider.get().lookup(SessionScope.class);
|
|
||||||
} catch (ComponentLookupException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
injector.bindScope(MojoExecutionScoped.class, () -> {
|
|
||||||
try {
|
|
||||||
return containerProvider.get().lookup(MojoExecutionScope.class);
|
|
||||||
} catch (ComponentLookupException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
injector.bindInstance(Injector.class, injector);
|
|
||||||
bind(Injector.class).toInstance(injector);
|
|
||||||
bind(SisuDiBridgeModule.class).toInstance(this);
|
|
||||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
||||||
if (classLoader == null) {
|
|
||||||
classLoader = getClass().getClassLoader();
|
|
||||||
}
|
}
|
||||||
loadFromClassLoader(classLoader);
|
|
||||||
injector.getBindings().keySet().stream()
|
|
||||||
.filter(k -> k.getQualifier() != null)
|
|
||||||
.sorted(Comparator.comparing(k -> k.getRawType().getName()))
|
|
||||||
.distinct()
|
|
||||||
.forEach(key -> {
|
|
||||||
Class<?> clazz = key.getRawType();
|
|
||||||
Class<Object> itf = (clazz.isInterface()
|
|
||||||
? null
|
|
||||||
: (Class<Object>) (clazz.getInterfaces().length > 0 ? clazz.getInterfaces()[0] : clazz));
|
|
||||||
if (itf != null) {
|
|
||||||
AnnotatedBindingBuilder<Object> binder = bind(itf);
|
|
||||||
if (key.getQualifier() instanceof String s && !s.isEmpty()) {
|
|
||||||
binder.annotatedWith(Names.named(s));
|
|
||||||
} else if (key.getQualifier() instanceof Annotation a) {
|
|
||||||
binder.annotatedWith(a);
|
|
||||||
}
|
|
||||||
binder.toProvider(() -> injector.getInstance(clazz));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFromClassLoader(ClassLoader classLoader) {
|
private <Q> Supplier<Q> getListSupplier(Key<Q> key) {
|
||||||
try {
|
Key<Object> elementType = key.getTypeParameter(0);
|
||||||
for (Iterator<URL> it = classLoader
|
return () -> {
|
||||||
.getResources("META-INF/maven/org.apache.maven.api.di.Inject")
|
List<Binding<?>> list = new ArrayList<>();
|
||||||
.asIterator();
|
// Add DI bindings
|
||||||
it.hasNext(); ) {
|
list.addAll(getBindings().getOrDefault(elementType, Set.of()));
|
||||||
URL url = it.next();
|
// Add Plexus bindings
|
||||||
if (loaded.add(url.toExternalForm())) {
|
for (var bean : locator.get().locate(toGuiceKey(elementType))) {
|
||||||
List<String> lines;
|
if (isPlexusBean(bean)) {
|
||||||
try (InputStream is = url.openStream();
|
list.add(new BindingToBeanEntry<>(elementType).toBeanEntry(bean));
|
||||||
BufferedReader reader =
|
|
||||||
new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
|
||||||
lines = reader.lines()
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(s -> !s.isEmpty() && !s.startsWith("#"))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
for (String className : lines) {
|
|
||||||
try {
|
|
||||||
Class<?> clazz = classLoader.loadClass(className);
|
|
||||||
injector.bindImplicit(clazz);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
// ignore
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (Q) list(list.stream().sorted(getBindingComparator()).toList(), this::getInstance);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private <Q> Supplier<Q> getMapSupplier(Key<Q> key) {
|
||||||
|
Key<?> keyType = key.getTypeParameter(0);
|
||||||
|
Key<Object> valueType = key.getTypeParameter(1);
|
||||||
|
if (keyType.getRawType() != String.class) {
|
||||||
|
throw new DIException("Only String keys are supported for maps: " + key);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
return () -> {
|
||||||
throw new MavenException(e);
|
var comparator = getBindingComparator();
|
||||||
|
Map<String, Binding<?>> map = new HashMap<>();
|
||||||
|
for (Binding<?> b : getBindings().getOrDefault(valueType, Set.of())) {
|
||||||
|
String name =
|
||||||
|
b.getOriginalKey() != null && b.getOriginalKey().getQualifier() instanceof String s
|
||||||
|
? s
|
||||||
|
: "";
|
||||||
|
map.compute(name, (n, ob) -> ob == null || comparator.compare(ob, b) < 0 ? b : ob);
|
||||||
|
}
|
||||||
|
for (var bean : locator.get().locate(toGuiceKey(valueType))) {
|
||||||
|
if (isPlexusBean(bean)) {
|
||||||
|
Binding<?> b = new BindingToBeanEntry<>(valueType)
|
||||||
|
.toBeanEntry(bean)
|
||||||
|
.prioritize(bean.getRank());
|
||||||
|
String name = bean.getKey() instanceof com.google.inject.name.Named n ? n.value() : "";
|
||||||
|
map.compute(name, (n, ob) -> ob == null || ob.getPriority() < b.getPriority() ? b : ob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection unchecked
|
||||||
|
return (Q) map(map, this::getInstance);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private <Q> Q getInstance(Binding<Q> binding) {
|
||||||
|
return compile(binding).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Comparator<Binding<?>> getBindingComparator() {
|
||||||
|
Comparator<Binding<?>> comparing = Comparator.comparing(Binding::getPriority);
|
||||||
|
return comparing.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> boolean isPlexusBean(BeanEntry<Annotation, T> entry) {
|
||||||
|
try {
|
||||||
|
if ("org.eclipse.sisu.inject.LazyBeanEntry"
|
||||||
|
.equals(entry.getClass().getName())) {
|
||||||
|
Field f = entry.getClass().getDeclaredField("binding");
|
||||||
|
f.setAccessible(true);
|
||||||
|
Object b = f.get(entry);
|
||||||
|
return !(b instanceof ProviderInstanceBinding<?> pib)
|
||||||
|
|| !(pib.getUserSuppliedProvider() instanceof BridgeProvider<?>);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,190 +19,36 @@
|
||||||
package org.apache.maven.session.scope.internal;
|
package org.apache.maven.session.scope.internal;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.OutOfScopeException;
|
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Scope;
|
import com.google.inject.Scope;
|
||||||
import com.google.inject.name.Names;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SessionScope
|
* SessionScope
|
||||||
*/
|
*/
|
||||||
public class SessionScope implements Scope, org.apache.maven.di.Scope {
|
public class SessionScope extends org.apache.maven.internal.impl.di.SessionScope implements Scope {
|
||||||
|
|
||||||
private static final Provider<Object> SEEDED_KEY_PROVIDER = () -> {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ScopeState
|
|
||||||
*/
|
|
||||||
protected static final class ScopeState {
|
|
||||||
private final Map<Key<?>, CachingProvider<?>> provided = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public <T> void seed(Class<T> clazz, Provider<T> value) {
|
|
||||||
provided.put(Key.get(clazz), new CachingProvider<>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
|
|
||||||
Provider<?> provider = provided.computeIfAbsent(key, k -> new CachingProvider<>(unscoped));
|
|
||||||
return (Provider<T>) provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<CachingProvider<?>> providers() {
|
|
||||||
return provided.values();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<ScopeState> values = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
public void enter() {
|
|
||||||
values.add(0, new ScopeState());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ScopeState getScopeState() {
|
|
||||||
if (values.isEmpty()) {
|
|
||||||
throw new OutOfScopeException("Cannot access session scope outside of a scoping block");
|
|
||||||
}
|
|
||||||
return values.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exit() {
|
|
||||||
if (values.isEmpty()) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
values.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void seed(Class<T> clazz, Provider<T> value) {
|
public <T> void seed(Class<T> clazz, Provider<T> value) {
|
||||||
getScopeState().seed(clazz, value);
|
getScopeState().seed(clazz, value::get);
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void seed(Class<T> clazz, final T value) {
|
|
||||||
seed(clazz, (Provider<T>) () -> value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
|
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
|
||||||
// Lazy evaluating provider
|
Object qualifier = key.getAnnotation() instanceof Named n ? n.value() : key.getAnnotation();
|
||||||
return () -> {
|
org.apache.maven.di.Key<T> k =
|
||||||
if (values.isEmpty()) {
|
org.apache.maven.di.Key.ofType(key.getTypeLiteral().getType(), qualifier);
|
||||||
return createProxy(key, unscoped);
|
return scope(k, unscoped::get)::get;
|
||||||
} else {
|
|
||||||
return getScopeState().scope(key, unscoped).get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <T> Supplier<T> scope(org.apache.maven.di.Key<T> key, Annotation scope, Supplier<T> unscoped) {
|
|
||||||
Object qualifier = key.getQualifier();
|
|
||||||
Key<?> k = qualifier != null
|
|
||||||
? Key.get(key.getType(), qualifier instanceof String s ? Names.named(s) : (Annotation) qualifier)
|
|
||||||
: Key.get(key.getType());
|
|
||||||
Provider<T> up = unscoped::get;
|
|
||||||
Provider<T> p = scope((Key<T>) k, up);
|
|
||||||
return p::get;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T> T createProxy(Key<T> key, Provider<T> unscoped) {
|
|
||||||
InvocationHandler dispatcher = (proxy, method, args) -> {
|
|
||||||
method.setAccessible(true);
|
|
||||||
try {
|
|
||||||
return method.invoke(getScopeState().scope(key, unscoped).get(), args);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
throw e.getCause();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Class<T> superType = (Class<T>) key.getTypeLiteral().getRawType();
|
|
||||||
Class<?>[] interfaces = getInterfaces(superType);
|
|
||||||
return (T) java.lang.reflect.Proxy.newProxyInstance(superType.getClassLoader(), interfaces, dispatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?>[] getInterfaces(Class<?> superType) {
|
|
||||||
if (superType.isInterface()) {
|
|
||||||
return new Class<?>[] {superType};
|
|
||||||
} else {
|
|
||||||
for (Annotation a : superType.getAnnotations()) {
|
|
||||||
Class<? extends Annotation> annotationType = a.annotationType();
|
|
||||||
if ("org.apache.maven.api.di.Typed".equals(annotationType.getName())
|
|
||||||
|| "org.eclipse.sisu.Typed".equals(annotationType.getName())
|
|
||||||
|| "javax.enterprise.inject.Typed".equals(annotationType.getName())
|
|
||||||
|| "jakarta.enterprise.inject.Typed".equals(annotationType.getName())) {
|
|
||||||
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()).collect(Collectors.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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A provider wrapping an existing provider with a cache
|
|
||||||
* @param <T> the provided type
|
|
||||||
*/
|
|
||||||
protected static class CachingProvider<T> implements Provider<T> {
|
|
||||||
private final Provider<T> provider;
|
|
||||||
private volatile T value;
|
|
||||||
|
|
||||||
CachingProvider(Provider<T> provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T value() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
if (value == null) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (value == null) {
|
|
||||||
value = provider.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
|
||||||
public static <T> Provider<T> seededKeyProvider() {
|
|
||||||
return (Provider<T>) SEEDED_KEY_PROVIDER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Provider<T> seededKeyProvider(Class<? extends T> clazz) {
|
public static <T> Provider<T> seededKeyProvider(Class<? extends T> clazz) {
|
||||||
return () -> {
|
return SessionScope.<T>seededKeySupplier(clazz)::get;
|
||||||
throw new IllegalStateException("No instance of " + clazz.getName() + " is bound to the session scope.");
|
}
|
||||||
};
|
|
||||||
|
protected boolean isTypeAnnotation(Class<? extends Annotation> annotationType) {
|
||||||
|
return "org.apache.maven.api.di.Typed".equals(annotationType.getName())
|
||||||
|
|| "org.eclipse.sisu.Typed".equals(annotationType.getName())
|
||||||
|
|| "javax.enterprise.inject.Typed".equals(annotationType.getName())
|
||||||
|
|| "jakarta.enterprise.inject.Typed".equals(annotationType.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
/*
|
||||||
|
* 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.di;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.Binding;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import org.apache.maven.api.model.Model;
|
||||||
|
import org.apache.maven.api.services.Source;
|
||||||
|
import org.apache.maven.api.spi.ModelParser;
|
||||||
|
import org.apache.maven.api.spi.ModelParserException;
|
||||||
|
import org.apache.maven.internal.impl.SisuDiBridgeModule;
|
||||||
|
import org.codehaus.plexus.DefaultContainerConfiguration;
|
||||||
|
import org.codehaus.plexus.DefaultPlexusContainer;
|
||||||
|
import org.codehaus.plexus.PlexusContainer;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.EnabledIf;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class DiTest {
|
||||||
|
|
||||||
|
// return true to run the test
|
||||||
|
static boolean testShouldNotHaveDuplicates() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class DiTest1 {
|
||||||
|
|
||||||
|
PlexusContainer container;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws Exception {
|
||||||
|
container = new DefaultPlexusContainer(
|
||||||
|
new DefaultContainerConfiguration(),
|
||||||
|
new AbstractModule() {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(ModelParser.class).to(TestModelParser.class);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new SisuDiBridgeModule(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlexus() throws Exception {
|
||||||
|
List<ModelParser> parsers = container.lookupList(ModelParser.class);
|
||||||
|
assertNotNull(parsers);
|
||||||
|
assertEquals(1, parsers.size());
|
||||||
|
Map<String, ModelParser> parsersMap = container.lookupMap(ModelParser.class);
|
||||||
|
assertNotNull(parsersMap);
|
||||||
|
assertEquals(1, parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGuice() throws Exception {
|
||||||
|
List<Binding<ModelParser>> parsers =
|
||||||
|
container.lookup(Injector.class).findBindingsByType(TypeLiteral.get(ModelParser.class));
|
||||||
|
assertNotNull(parsers);
|
||||||
|
assertEquals(1, parsers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDI() throws Exception {
|
||||||
|
DiInjected diInjected = new DiInjected();
|
||||||
|
container.lookup(org.apache.maven.di.Injector.class).injectInstance(diInjected);
|
||||||
|
assertNotNull(diInjected.parser);
|
||||||
|
assertNotNull(diInjected.parsers);
|
||||||
|
assertEquals(1, diInjected.parsers.size());
|
||||||
|
assertNotNull(diInjected.parsersMap);
|
||||||
|
assertEquals(1, diInjected.parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DiInjected {
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
ModelParser parser;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
List<ModelParser> parsers;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
Map<String, ModelParser> parsersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
static class TestModelParser implements ModelParser {
|
||||||
|
@Override
|
||||||
|
public Optional<Source> locate(Path dir) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model parse(Source source, Map<String, ?> options) throws ModelParserException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class DiTest2 {
|
||||||
|
|
||||||
|
PlexusContainer container;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws Exception {
|
||||||
|
container = new DefaultPlexusContainer(new DefaultContainerConfiguration(), new SisuDiBridgeModule(false) {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
injector.bindImplicit(TestModelParser.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlexus() throws Exception {
|
||||||
|
List<ModelParser> parsers = container.lookupList(ModelParser.class);
|
||||||
|
assertNotNull(parsers);
|
||||||
|
assertEquals(1, parsers.size());
|
||||||
|
Map<String, ModelParser> parsersMap = container.lookupMap(ModelParser.class);
|
||||||
|
assertNotNull(parsersMap);
|
||||||
|
assertEquals(1, parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGuice() throws Exception {
|
||||||
|
List<Binding<ModelParser>> parsers2 =
|
||||||
|
container.lookup(Injector.class).findBindingsByType(TypeLiteral.get(ModelParser.class));
|
||||||
|
assertNotNull(parsers2);
|
||||||
|
assertEquals(1, parsers2.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledIf("org.apache.maven.di.DiTest#testShouldNotHaveDuplicates")
|
||||||
|
void testDI() throws Exception {
|
||||||
|
DiInjected diInjected = new DiInjected();
|
||||||
|
container.lookup(org.apache.maven.di.Injector.class).injectInstance(diInjected);
|
||||||
|
assertNotNull(diInjected.parser);
|
||||||
|
assertNotNull(diInjected.parsers);
|
||||||
|
assertEquals(1, diInjected.parsers.size());
|
||||||
|
assertNotNull(diInjected.parsersMap);
|
||||||
|
assertEquals(1, diInjected.parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DiInjected {
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
ModelParser parser;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
List<ModelParser> parsers;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
Map<String, ModelParser> parsersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Named
|
||||||
|
@org.apache.maven.api.di.Singleton
|
||||||
|
static class TestModelParser implements ModelParser {
|
||||||
|
@Override
|
||||||
|
public Optional<Source> locate(Path dir) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model parse(Source source, Map<String, ?> options) throws ModelParserException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class DiTest3 {
|
||||||
|
|
||||||
|
PlexusContainer container;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() throws Exception {
|
||||||
|
container = new DefaultPlexusContainer(new DefaultContainerConfiguration(), new SisuDiBridgeModule(false) {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
injector.bindImplicit(TestModelParser.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testPlexus() throws Exception {
|
||||||
|
List<ModelParser> parsers = container.lookupList(ModelParser.class);
|
||||||
|
assertNotNull(parsers);
|
||||||
|
assertEquals(1, parsers.size());
|
||||||
|
Map<String, ModelParser> parsersMap = container.lookupMap(ModelParser.class);
|
||||||
|
assertNotNull(parsersMap);
|
||||||
|
assertEquals(1, parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGuice() throws Exception {
|
||||||
|
List<Binding<ModelParser>> parsers =
|
||||||
|
container.lookup(Injector.class).findBindingsByType(TypeLiteral.get(ModelParser.class));
|
||||||
|
assertNotNull(parsers);
|
||||||
|
assertEquals(1, parsers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnabledIf("org.apache.maven.di.DiTest#testShouldNotHaveDuplicates")
|
||||||
|
void testDI() throws Exception {
|
||||||
|
DiInjected diInjected = new DiInjected();
|
||||||
|
container.lookup(org.apache.maven.di.Injector.class).injectInstance(diInjected);
|
||||||
|
assertNotNull(diInjected.parser);
|
||||||
|
assertNotNull(diInjected.parsers);
|
||||||
|
assertEquals(1, diInjected.parsers.size());
|
||||||
|
assertNotNull(diInjected.parsersMap);
|
||||||
|
assertEquals(1, diInjected.parsersMap.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DiInjected {
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
ModelParser parser;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
List<ModelParser> parsers;
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Inject
|
||||||
|
Map<String, ModelParser> parsersMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.apache.maven.api.di.Named
|
||||||
|
static class TestModelParser implements ModelParser {
|
||||||
|
@Override
|
||||||
|
public Optional<Source> locate(Path dir) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model parse(Source source, Map<String, ?> options) throws ModelParserException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,15 +39,15 @@ class MojoExecutionScopeTest {
|
||||||
|
|
||||||
Object o1 = new Object();
|
Object o1 = new Object();
|
||||||
scope.seed(Object.class, o1);
|
scope.seed(Object.class, o1);
|
||||||
assertSame(o1, scope.scope(Key.get(Object.class), null).get());
|
assertSame(o1, scope.scope(Key.get(Object.class), () -> null).get());
|
||||||
|
|
||||||
scope.enter();
|
scope.enter();
|
||||||
Object o2 = new Object();
|
Object o2 = new Object();
|
||||||
scope.seed(Object.class, o2);
|
scope.seed(Object.class, o2);
|
||||||
assertSame(o2, scope.scope(Key.get(Object.class), null).get());
|
assertSame(o2, scope.scope(Key.get(Object.class), () -> null).get());
|
||||||
|
|
||||||
scope.exit();
|
scope.exit();
|
||||||
assertSame(o1, scope.scope(Key.get(Object.class), null).get());
|
assertSame(o1, scope.scope(Key.get(Object.class), () -> null).get());
|
||||||
|
|
||||||
scope.exit();
|
scope.exit();
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import com.google.inject.OutOfScopeException;
|
|
||||||
import org.apache.maven.SessionScoped;
|
import org.apache.maven.SessionScoped;
|
||||||
import org.apache.maven.api.Session;
|
import org.apache.maven.api.Session;
|
||||||
|
import org.apache.maven.internal.impl.di.OutOfScopeException;
|
||||||
import org.apache.maven.session.scope.internal.SessionScope;
|
import org.apache.maven.session.scope.internal.SessionScope;
|
||||||
import org.codehaus.plexus.PlexusContainer;
|
import org.codehaus.plexus.PlexusContainer;
|
||||||
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
||||||
|
|
|
@ -36,27 +36,29 @@ public interface Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Injector discover(ClassLoader classLoader);
|
Injector discover(@Nonnull ClassLoader classLoader);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope);
|
Injector bindScope(@Nonnull Class<? extends Annotation> scopeAnnotation, @Nonnull Scope scope);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<Scope> scope);
|
Injector bindScope(@Nonnull Class<? extends Annotation> scopeAnnotation, @Nonnull Supplier<Scope> scope);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Injector bindImplicit(Class<?> cls);
|
Injector bindImplicit(@Nonnull Class<?> cls);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
<T> Injector bindInstance(Class<T> cls, T instance);
|
<T> Injector bindInstance(@Nonnull Class<T> cls, @Nonnull T instance);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Bean access
|
// Bean access
|
||||||
//
|
//
|
||||||
|
|
||||||
<T> void injectInstance(T instance);
|
<T> void injectInstance(@Nonnull T instance);
|
||||||
|
|
||||||
<T> T getInstance(Class<T> key);
|
@Nonnull
|
||||||
|
<T> T getInstance(@Nonnull Class<T> key);
|
||||||
|
|
||||||
<T> T getInstance(Key<T> key);
|
@Nonnull
|
||||||
|
<T> T getInstance(@Nonnull Key<T> key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.apache.maven.di.impl.Utils;
|
||||||
* The key defines an identity of a binding. In any DI, a key is usually a type of the object along
|
* The key defines an identity of a binding. In any DI, a key is usually a type of the object along
|
||||||
* with some optional tag to distinguish between bindings which make objects of the same type.
|
* with some optional tag to distinguish between bindings which make objects of the same type.
|
||||||
* <p>
|
* <p>
|
||||||
* In ActiveJ Inject, a key is also a type token - special abstract class that can store type information
|
* In Maven Inject, a key is also a type token - special abstract class that can store type information
|
||||||
* with the shortest syntax possible in Java.
|
* with the shortest syntax possible in Java.
|
||||||
* <p>
|
* <p>
|
||||||
* For example, to create a key of type Map<String, List<Integer>>, you can just use
|
* For example, to create a key of type Map<String, List<Integer>>, you can just use
|
||||||
|
|
|
@ -18,10 +18,34 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.maven.di;
|
package org.apache.maven.di;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
import org.apache.maven.api.annotations.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@code Scope} defines how visible instances are when managed by a {@link org.apache.maven.di.Injector}.
|
||||||
|
* Typically, instances are created with <i>no scope</i>, meaning they don’t retain any state from the
|
||||||
|
* framework’s perspective: the {@code Injector} generates the instance, injects it into the necessary class,
|
||||||
|
* and then immediately forgets it. By linking a scope to a specific binding, the created instance can be
|
||||||
|
* “remembered” and reused for future injections.
|
||||||
|
* <p>
|
||||||
|
* Instances are associated to a given scope by means of a {@link org.apache.maven.api.di.Scope @Scope}
|
||||||
|
* annotation, usually put on another annotation. For example, the {@code @Singleton} annotation is used
|
||||||
|
* to indicate that a given binding should be scoped as a singleton.
|
||||||
|
* <p>
|
||||||
|
* The following scopes are currently supported:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link org.apache.maven.api.di.Singleton @Singleton}</li>
|
||||||
|
* <li>{@link org.apache.maven.api.di.SessionScoped @SessionScoped}</li>
|
||||||
|
* <li>{@link org.apache.maven.api.di.MojoExecutionScoped @MojoExecutionScoped}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Experimental
|
||||||
public interface Scope {
|
public interface Scope {
|
||||||
|
|
||||||
<T> Supplier<T> scope(Key<T> key, Annotation scope, Supplier<T> unscoped);
|
@Nonnull
|
||||||
|
<T> Supplier<T> scope(@Nonnull Key<T> key, @Nonnull Supplier<T> unscoped);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.apache.maven.di.Injector;
|
||||||
* (missing or cyclic dependencies, incorrect annotations etc.) or in runtime when
|
* (missing or cyclic dependencies, incorrect annotations etc.) or in runtime when
|
||||||
* you ask an {@link Injector} for an instance it does not have a {@link Binding binding} for.
|
* you ask an {@link Injector} for an instance it does not have a {@link Binding binding} for.
|
||||||
*/
|
*/
|
||||||
public final class DIException extends RuntimeException {
|
public class DIException extends RuntimeException {
|
||||||
public DIException(String message) {
|
public DIException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ public class InjectorImpl implements Injector {
|
||||||
|
|
||||||
private final Map<Key<?>, Set<Binding<?>>> bindings = new HashMap<>();
|
private final Map<Key<?>, Set<Binding<?>>> bindings = new HashMap<>();
|
||||||
private final Map<Class<? extends Annotation>, Supplier<Scope>> scopes = new HashMap<>();
|
private final Map<Class<? extends Annotation>, Supplier<Scope>> scopes = new HashMap<>();
|
||||||
|
private final Set<String> loadedUrls = new HashSet<>();
|
||||||
|
|
||||||
public InjectorImpl() {
|
public InjectorImpl() {
|
||||||
bindScope(Singleton.class, new SingletonScope());
|
bindScope(Singleton.class, new SingletonScope());
|
||||||
|
@ -87,12 +88,16 @@ public class InjectorImpl implements Injector {
|
||||||
try {
|
try {
|
||||||
Enumeration<URL> enumeration = classLoader.getResources("META-INF/maven/org.apache.maven.api.di.Inject");
|
Enumeration<URL> enumeration = classLoader.getResources("META-INF/maven/org.apache.maven.api.di.Inject");
|
||||||
while (enumeration.hasMoreElements()) {
|
while (enumeration.hasMoreElements()) {
|
||||||
try (InputStream is = enumeration.nextElement().openStream();
|
URL url = enumeration.nextElement();
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
|
if (loadedUrls.add(url.toExternalForm())) {
|
||||||
for (String line :
|
try (InputStream is = url.openStream();
|
||||||
reader.lines().filter(l -> !l.startsWith("#")).toList()) {
|
BufferedReader reader =
|
||||||
Class<?> clazz = classLoader.loadClass(line);
|
new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
|
||||||
bindImplicit(clazz);
|
for (String line :
|
||||||
|
reader.lines().filter(l -> !l.startsWith("#")).toList()) {
|
||||||
|
Class<?> clazz = classLoader.loadClass(line);
|
||||||
|
bindImplicit(clazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +200,7 @@ public class InjectorImpl implements Injector {
|
||||||
if (res2 != null) {
|
if (res2 != null) {
|
||||||
List<Supplier<Object>> list = res2.stream().map(this::compile).collect(Collectors.toList());
|
List<Supplier<Object>> list = res2.stream().map(this::compile).collect(Collectors.toList());
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return () -> (Q) list(list);
|
return () -> (Q) list(list, Supplier::get);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (key.getRawType() == Map.class) {
|
if (key.getRawType() == Map.class) {
|
||||||
|
@ -214,7 +219,7 @@ public class InjectorImpl implements Injector {
|
||||||
: null),
|
: null),
|
||||||
this::compile));
|
this::compile));
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return () -> (Q) map(map);
|
return () -> (Q) map(map, Supplier::get);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dep.optional()) {
|
if (dep.optional()) {
|
||||||
|
@ -241,7 +246,7 @@ public class InjectorImpl implements Injector {
|
||||||
.orElseThrow(() -> new DIException("Scope not bound for annotation "
|
.orElseThrow(() -> new DIException("Scope not bound for annotation "
|
||||||
+ binding.getScope().annotationType()))
|
+ binding.getScope().annotationType()))
|
||||||
.get();
|
.get();
|
||||||
compiled = scope.scope((Key<Q>) binding.getOriginalKey(), binding.getScope(), compiled);
|
compiled = scope.scope((Key<Q>) binding.getOriginalKey(), compiled);
|
||||||
}
|
}
|
||||||
return compiled;
|
return compiled;
|
||||||
}
|
}
|
||||||
|
@ -307,8 +312,8 @@ public class InjectorImpl implements Injector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <K, V> Map<K, V> map(Map<K, Supplier<V>> map) {
|
protected <K, V, T> Map<K, V> map(Map<K, T> map, Function<T, V> mapper) {
|
||||||
return new WrappingMap<>(map, Supplier::get);
|
return new WrappingMap<>(map, mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
|
private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
|
||||||
|
@ -349,8 +354,8 @@ public class InjectorImpl implements Injector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> List<T> list(List<Supplier<T>> bindingList) {
|
protected <Q, T> List<Q> list(List<T> bindingList, Function<T, Q> mapper) {
|
||||||
return new WrappingList<>(bindingList, Supplier::get);
|
return new WrappingList<>(bindingList, mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class WrappingList<Q, T> extends AbstractList<Q> {
|
private static class WrappingList<Q, T> extends AbstractList<Q> {
|
||||||
|
@ -379,8 +384,7 @@ public class InjectorImpl implements Injector {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <T> java.util.function.Supplier<T> scope(
|
public <T> java.util.function.Supplier<T> scope(Key<T> key, java.util.function.Supplier<T> unscoped) {
|
||||||
Key<T> key, Annotation scope, java.util.function.Supplier<T> unscoped) {
|
|
||||||
return (java.util.function.Supplier<T>)
|
return (java.util.function.Supplier<T>)
|
||||||
cache.computeIfAbsent(key, k -> new java.util.function.Supplier<T>() {
|
cache.computeIfAbsent(key, k -> new java.util.function.Supplier<T>() {
|
||||||
volatile T instance;
|
volatile T instance;
|
||||||
|
|
|
@ -79,6 +79,7 @@ import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
|
||||||
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
|
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
|
||||||
import org.apache.maven.cli.transfer.SimplexTransferListener;
|
import org.apache.maven.cli.transfer.SimplexTransferListener;
|
||||||
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
|
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
|
||||||
|
import org.apache.maven.di.Injector;
|
||||||
import org.apache.maven.eventspy.internal.EventSpyDispatcher;
|
import org.apache.maven.eventspy.internal.EventSpyDispatcher;
|
||||||
import org.apache.maven.exception.DefaultExceptionHandler;
|
import org.apache.maven.exception.DefaultExceptionHandler;
|
||||||
import org.apache.maven.exception.ExceptionHandler;
|
import org.apache.maven.exception.ExceptionHandler;
|
||||||
|
@ -95,7 +96,6 @@ import org.apache.maven.execution.scope.internal.MojoExecutionScope;
|
||||||
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
|
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
|
||||||
import org.apache.maven.extension.internal.CoreExports;
|
import org.apache.maven.extension.internal.CoreExports;
|
||||||
import org.apache.maven.extension.internal.CoreExtensionEntry;
|
import org.apache.maven.extension.internal.CoreExtensionEntry;
|
||||||
import org.apache.maven.internal.impl.SisuDiBridgeModule;
|
|
||||||
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
import org.apache.maven.jline.JLineMessageBuilderFactory;
|
||||||
import org.apache.maven.jline.MessageUtils;
|
import org.apache.maven.jline.MessageUtils;
|
||||||
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
import org.apache.maven.lifecycle.LifecycleExecutionException;
|
||||||
|
@ -732,10 +732,20 @@ public class MavenCli {
|
||||||
for (CoreExtensionEntry extension : extensions) {
|
for (CoreExtensionEntry extension : extensions) {
|
||||||
container.discoverComponents(
|
container.discoverComponents(
|
||||||
extension.getClassRealm(),
|
extension.getClassRealm(),
|
||||||
|
new AbstractModule() {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
try {
|
||||||
|
container.lookup(Injector.class).discover(extension.getClassRealm());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// ignore
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
new SessionScopeModule(container.lookup(SessionScope.class)),
|
new SessionScopeModule(container.lookup(SessionScope.class)),
|
||||||
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
|
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
|
||||||
new ExtensionConfigurationModule(extension, extensionSource));
|
new ExtensionConfigurationModule(extension, extensionSource));
|
||||||
container.lookup(SisuDiBridgeModule.class).loadFromClassLoader(extension.getClassRealm());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
customizeContainer(container);
|
customizeContainer(container);
|
||||||
|
|
Loading…
Reference in New Issue