Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DI improvements #1717

Merged
merged 6 commits into from
Sep 12, 2024
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 @@ -29,6 +29,13 @@
/**
* Indicates that the annotated bean has a lifespan limited to a given mojo execution,
* which means each mojo execution will result in a different instance being injected.
* <p>
* The following objects will be bound to the mojo execution scope:
* <ul>
* <li>{@code org.apache.maven.api.MojoExecution}</li>
* <li>{@code org.apache.maven.api.Project}</li>
* <li>{@code org.apache.maven.api.plugin.Log}</li>
* </ul>
*
* @since 4.0.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
/**
* Indicates that annotated component should be instantiated before session execution starts
* and discarded after session execution completes.
* <p>
* A {@code org.apache.maven.api.Session} object is available in the scope of this annotation.
*
* @since 4.0.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@
*/
@Experimental
@Documented
@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.RUNTIME)
public @interface Nullable {}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.maven.di.Key;
import org.apache.maven.di.impl.Binding;
import org.apache.maven.di.impl.DIException;
import org.apache.maven.di.impl.Dependency;
import org.apache.maven.di.impl.InjectorImpl;
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
import org.apache.maven.session.scope.internal.SessionScope;
Expand All @@ -66,7 +67,8 @@ protected void configure() {

injector = new InjectorImpl() {
@Override
public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
Key<Q> key = dep.key();
Set<Binding<Q>> res = getBindings(key);
if (res != null && !res.isEmpty()) {
List<Binding<Q>> bindingList = new ArrayList<>(res);
Expand Down Expand Up @@ -119,6 +121,9 @@ public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
// ignore
e.printStackTrace();
}
if (dep.optional()) {
return () -> null;
}
throw new DIException("No binding to construct an instance for key "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
Expand Down
7 changes: 7 additions & 0 deletions maven-di/src/main/java/org/apache/maven/di/Injector.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.lang.annotation.Annotation;
import java.util.function.Supplier;

import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.di.impl.InjectorImpl;

public interface Injector {
Expand All @@ -29,18 +30,24 @@ public interface Injector {
// Builder API
//

@Nonnull
static Injector create() {
return new InjectorImpl();
}

@Nonnull
Injector discover(ClassLoader classLoader);

@Nonnull
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope);

@Nonnull
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<Scope> scope);

@Nonnull
Injector bindImplicit(Class<?> cls);

@Nonnull
<T> Injector bindInstance(Class<T> cls, T instance);

//
Expand Down
43 changes: 25 additions & 18 deletions maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@
import static java.util.stream.Collectors.joining;

public abstract class Binding<T> {
private final Set<Key<?>> dependencies;
private final Set<Dependency<?>> dependencies;
private Annotation scope;
private int priority;
private Key<?> originalKey;

protected Binding(Key<? extends T> originalKey, Set<Key<?>> dependencies) {
protected Binding(Key<? extends T> originalKey, Set<Dependency<?>> dependencies) {
this(originalKey, dependencies, null, 0);
}

protected Binding(Key<?> originalKey, Set<Key<?>> dependencies, Annotation scope, int priority) {
protected Binding(Key<?> originalKey, Set<Dependency<?>> dependencies, Annotation scope, int priority) {
this.originalKey = originalKey;
this.dependencies = dependencies;
this.scope = scope;
Expand All @@ -56,15 +56,18 @@ public static <T> Binding<T> toInstance(T instance) {

public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> constructor, Class<?>[] types) {
return Binding.to(
originalKey, constructor, Stream.of(types).map(Key::of).toArray(Key<?>[]::new));
originalKey,
constructor,
Stream.of(types).map(c -> new Dependency<>(Key.of(c), false)).toArray(Dependency<?>[]::new));
}

public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> constructor, Key<?>[] dependencies) {
public static <R> Binding<R> to(
Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies) {
return to(originalKey, constructor, dependencies, 0);
}

public static <R> Binding<R> to(
Key<R> originalKey, TupleConstructorN<R> constructor, Key<?>[] dependencies, int priority) {
Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies, int priority) {
return new BindingToConstructor<>(originalKey, constructor, dependencies, priority);
}

Expand Down Expand Up @@ -94,13 +97,17 @@ public Binding<T> initializeWith(BindingInitializer<T> bindingInitializer) {
this.scope,
this.priority) {
@Override
public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
final Supplier<T> compiledBinding = Binding.this.compile(compiler);
final Consumer<T> consumer = bindingInitializer.compile(compiler);
return () -> {
T instance = compiledBinding.get();
consumer.accept(instance);
return instance;
try {
T instance = compiledBinding.get();
consumer.accept(instance);
return instance;
} catch (DIException e) {
throw new DIException("Error while initializing binding " + Binding.this, e);
}
};
}

Expand All @@ -111,9 +118,9 @@ public String toString() {
};
}

public abstract Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler);
public abstract Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler);

public Set<Key<?>> getDependencies() {
public Set<Dependency<?>> getDependencies() {
return dependencies;
}

Expand All @@ -122,7 +129,7 @@ public Annotation getScope() {
}

public String getDisplayString() {
return dependencies.stream().map(Key::getDisplayString).collect(joining(", ", "[", "]"));
return dependencies.stream().map(Dependency::getDisplayString).collect(joining(", ", "[", "]"));
}

public Key<?> getOriginalKey() {
Expand Down Expand Up @@ -152,7 +159,7 @@ public BindingToInstance(T instance) {
}

@Override
public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return () -> instance;
}

Expand All @@ -164,17 +171,17 @@ public String toString() {

public static class BindingToConstructor<T> extends Binding<T> {
final TupleConstructorN<T> constructor;
final Key<?>[] args;
final Dependency<?>[] args;

BindingToConstructor(
Key<? extends T> key, TupleConstructorN<T> constructor, Key<?>[] dependencies, int priority) {
Key<? extends T> key, TupleConstructorN<T> constructor, Dependency<?>[] dependencies, int priority) {
super(key, new HashSet<>(Arrays.asList(dependencies)), null, priority);
this.constructor = constructor;
this.args = dependencies;
}

@Override
public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return () -> {
Object[] args =
Stream.of(this.args).map(compiler).map(Supplier::get).toArray();
Expand All @@ -184,7 +191,7 @@ public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {

@Override
public String toString() {
return "BindingToConstructor[" + constructor + "]" + getDependencies();
return "BindingToConstructor[" + getOriginalKey() + "]" + getDependencies();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,30 @@
import java.util.function.Function;
import java.util.function.Supplier;

import org.apache.maven.di.Key;

import static java.util.stream.Collectors.toSet;

public abstract class BindingInitializer<T> {

private final Set<Key<?>> dependencies;
private final Set<Dependency<?>> dependencies;

protected BindingInitializer(Set<Key<?>> dependencies) {
protected BindingInitializer(Set<Dependency<?>> dependencies) {
this.dependencies = dependencies;
}

public Set<Key<?>> getDependencies() {
public Set<Dependency<?>> getDependencies() {
return dependencies;
}

public abstract Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler);
public abstract Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler);

public static <T> BindingInitializer<T> combine(List<BindingInitializer<T>> bindingInitializers) {
Set<Key<?>> deps = bindingInitializers.stream()
Set<Dependency<?>> deps = bindingInitializers.stream()
.map(BindingInitializer::getDependencies)
.flatMap(Collection::stream)
.collect(toSet());
return new BindingInitializer<T>(deps) {
return new BindingInitializer<>(deps) {
@Override
public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
public Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return instance -> bindingInitializers.stream()
.map(bindingInitializer -> bindingInitializer.compile(compiler))
.forEach(i -> i.accept(instance));
Expand Down
31 changes: 31 additions & 0 deletions maven-di/src/main/java/org/apache/maven/di/impl/Dependency.java
Original file line number Diff line number Diff line change
@@ -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.di.impl;

import org.apache.maven.di.Key;

public record Dependency<T>(Key<T> key, boolean optional) {
public String getDisplayString() {
String s = key.getDisplayString();
if (optional) {
s = "?" + s;
}
return s;
}
}
22 changes: 15 additions & 7 deletions maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ public InjectorImpl() {
bindScope(Singleton.class, new SingletonScope());
}

@Override
public <T> T getInstance(Class<T> key) {
return getInstance(Key.of(key));
}

@Override
public <T> T getInstance(Key<T> key) {
return getCompiledBinding(key).get();
return getCompiledBinding(new Dependency<>(key, false)).get();
}

@SuppressWarnings("unchecked")
Expand All @@ -88,7 +90,7 @@ public Injector discover(ClassLoader classLoader) {
try (InputStream is = enumeration.nextElement().openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
for (String line :
reader.lines().filter(l -> !l.startsWith("#")).collect(Collectors.toList())) {
reader.lines().filter(l -> !l.startsWith("#")).toList()) {
Class<?> clazz = classLoader.loadClass(line);
bindImplicit(clazz);
}
Expand All @@ -100,10 +102,12 @@ public Injector discover(ClassLoader classLoader) {
return this;
}

@Override
public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
return bindScope(scopeAnnotation, () -> scope);
}

@Override
public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<Scope> scope) {
if (scopes.put(scopeAnnotation, scope) != null) {
throw new DIException(
Expand All @@ -112,6 +116,7 @@ public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<
return this;
}

@Override
public <U> Injector bindInstance(Class<U> clazz, U instance) {
Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
Binding<U> binding = Binding.toInstance(instance);
Expand All @@ -133,7 +138,7 @@ public Injector bindImplicit(Class<?> clazz) {
return this;
}

private LinkedHashSet<Key<?>> current = new LinkedHashSet<>();
private final LinkedHashSet<Key<?>> current = new LinkedHashSet<>();

private Injector doBind(Key<?> key, Binding<?> binding) {
if (!current.add(key)) {
Expand Down Expand Up @@ -175,7 +180,8 @@ public Map<Key<?>, Set<Binding<?>>> getBindings() {
return bindings;
}

public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
Key<Q> key = dep.key();
Set<Binding<Q>> res = getBindings(key);
if (res != null && !res.isEmpty()) {
List<Binding<Q>> bindingList = new ArrayList<>(res);
Expand Down Expand Up @@ -211,6 +217,9 @@ public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
return () -> (Q) map(map);
}
}
if (dep.optional()) {
return () -> null;
}
throw new DIException("No binding to construct an instance for key "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
Expand Down Expand Up @@ -312,14 +321,13 @@ private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
this.mapper = mapper;
}

@SuppressWarnings("NullableProblems")
@Override
public Set<Entry<K, V>> entrySet() {
return new AbstractSet<Entry<K, V>>() {
return new AbstractSet<>() {
@Override
public Iterator<Entry<K, V>> iterator() {
Iterator<Entry<K, T>> it = delegate.entrySet().iterator();
return new Iterator<Entry<K, V>>() {
return new Iterator<>() {
@Override
public boolean hasNext() {
return it.hasNext();
Expand Down
Loading