Skip to content

Commit

Permalink
Final step
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Sep 17, 2024
1 parent 908ef33 commit c0d5f54
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -35,7 +35,6 @@

import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import com.google.inject.spi.ProviderInstanceBinding;
import org.apache.maven.api.di.MojoExecutionScoped;
Expand All @@ -51,8 +50,8 @@
import org.apache.maven.session.scope.internal.SessionScope;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.eclipse.sisu.plexus.PlexusBean;
import org.eclipse.sisu.plexus.PlexusBeanLocator;
import org.eclipse.sisu.BeanEntry;
import org.eclipse.sisu.inject.BeanLocator;

@Named
public class SisuDiBridgeModule extends AbstractModule {
Expand All @@ -71,7 +70,8 @@ public SisuDiBridgeModule(boolean discover) {
@Override
protected void configure() {
Provider<PlexusContainer> containerProvider = getProvider(PlexusContainer.class);
injector = new BridgeInjectorImpl(containerProvider, binder());
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);
Expand Down Expand Up @@ -101,11 +101,11 @@ private void bindScope(
}

static class BridgeInjectorImpl extends InjectorImpl {
final Provider<PlexusContainer> containerProvider;
final Provider<BeanLocator> locator;
final Binder binder;

BridgeInjectorImpl(Provider<PlexusContainer> containerProvider, Binder binder) {
this.containerProvider = containerProvider;
BridgeInjectorImpl(Provider<BeanLocator> locator, Binder binder) {
this.locator = locator;
this.binder = binder;
}

Expand All @@ -114,7 +114,6 @@ 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);
// System.out.println("Bind di -> guice: " + k);
this.binder.bind(k).toProvider(new BridgeProvider<>(binding));
}
return this;
Expand All @@ -131,6 +130,24 @@ private static <U> com.google.inject.Key<U> toGuiceKey(Key<U> key) {
}
}

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;
}

@Override
public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return beanEntry.getProvider()::get;
}
}

class BridgeProvider<T> implements Provider<T> {
final Binding<T> binding;

Expand All @@ -147,104 +164,114 @@ public T get() {
@Override
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);
Comparator<Binding<Q>> comparing = Comparator.comparing(Binding::getPriority);
bindingList.sort(comparing.reversed());
Binding<Q> binding = bindingList.get(0);
return compile(binding);
}
if (key.getRawType() == List.class) {
return () -> {
Key<Object> elementType = key.getTypeParameter(0);
Set<Binding<Object>> res2 = getBindings(elementType);
Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
try {
PlexusContainer container = containerProvider.get();
PlexusBeanLocator locator = container.lookup(PlexusBeanLocator.class);
for (PlexusBean<Object> bean : locator.locate(TypeLiteral.get(elementType.getRawType()))) {
if (!isDiBean(bean)) {
res3.add(new Binding<>(elementType, Set.of()) {
@Override
public Supplier<Object> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return bean::getValue;
}
});
}
}
} catch (Throwable e) {
// ignore
}
List<Supplier<Object>> list =
res3.stream().map(this::compile).collect(Collectors.toList());
//noinspection unchecked
return (Q) list(list);
};
Class<Q> rawType = key.getRawType();
if (rawType == List.class) {
return getListSupplier(key);
} else if (rawType == Map.class) {
return getMapSupplier(key);
} else {
return getBeanSupplier(dep, key);
}
if (key.getRawType() == Map.class) {
Key<?> k = key.getTypeParameter(0);
Key<Object> v = key.getTypeParameter(1);
if (k.getRawType() == String.class) {
return () -> {
Set<Binding<Object>> res2 = getBindings(v);
Set<Binding<Object>> res3 = res2 != null ? new HashSet<>(res2) : new HashSet<>();
Map<String, Supplier<Object>> map = res3.stream()
.filter(b -> b.getOriginalKey() == null
|| b.getOriginalKey().getQualifier() == null
|| b.getOriginalKey().getQualifier() instanceof String)
.collect(Collectors.toMap(
b -> (String)
(b.getOriginalKey() != null
? b.getOriginalKey().getQualifier()
: null),
this::compile));
//noinspection unchecked
return (Q) map(map);
};
}

private <Q> Supplier<Q> getBeanSupplier(Dependency<Q> dep, Key<Q> key) {
List<Binding<?>> list = new ArrayList<>();
// Add DI bindings
list.addAll(getBindings().getOrDefault(key, Set.of()));
// Add Plexus bindings
for (var bean : locator.get().locate(toGuiceKey(key))) {
if (isPlexusBean(bean)) {
list.add(new BindingToBeanEntry<>(key).toBeanEntry(bean));
}
}
try {
Q t = containerProvider.get().lookup(key.getRawType());
return compile(new Binding.BindingToInstance<>(t));
} catch (Throwable e) {
// ignore
}
if (dep.optional()) {
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 "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
.map(Key::toString)
.map(String::trim)
.sorted()
.distinct()
.collect(Collectors.joining("\n - ", " - ", "")));
}
}

private <Q> Supplier<Q> getListSupplier(Key<Q> key) {
Key<Object> elementType = key.getTypeParameter(0);
return () -> {
List<Binding<?>> list = new ArrayList<>();
// Add DI bindings
list.addAll(getBindings().getOrDefault(elementType, Set.of()));
// Add Plexus bindings
for (var bean : locator.get().locate(toGuiceKey(elementType))) {
if (isPlexusBean(bean)) {
list.add(new BindingToBeanEntry<>(elementType).toBeanEntry(bean));
}
}
//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);
}
throw new DIException("No binding to construct an instance for key "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
.map(Key::toString)
.map(String::trim)
.sorted()
.distinct()
.collect(Collectors.joining("\n - ", " - ", "")));
return () -> {
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 boolean isDiBean(PlexusBean<Object> bean) throws IllegalAccessException {
private <T> boolean isPlexusBean(BeanEntry<Annotation, T> entry) {
try {
if ("org.eclipse.sisu.plexus.LazyPlexusBean"
.equals(bean.getClass().getName())) {
Field f = bean.getClass().getDeclaredField("bean");
if ("org.eclipse.sisu.inject.LazyBeanEntry"
.equals(entry.getClass().getName())) {
Field f = entry.getClass().getDeclaredField("binding");
f.setAccessible(true);
Object entry = f.get(bean);
if ("org.eclipse.sisu.inject.LazyBeanEntry"
.equals(entry.getClass().getName())) {
f = entry.getClass().getDeclaredField("binding");
f.setAccessible(true);
Object b = f.get(entry);
if (b instanceof ProviderInstanceBinding<?> pib
&& pib.getUserSuppliedProvider() instanceof BridgeProvider<?>) {
return true;
}
}
Object b = f.get(entry);
return !(b instanceof ProviderInstanceBinding<?> pib)
|| !(pib.getUserSuppliedProvider() instanceof BridgeProvider<?>);
}
} catch (NoSuchFieldException e) {
} catch (Exception e) {
// ignore
}
return false;
return true;
}
}
}
42 changes: 36 additions & 6 deletions maven-core/src/test/java/org/apache/maven/di/DiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,39 @@ 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 =
List<Binding<ModelParser>> parsers =
container.lookup(Injector.class).findBindingsByType(TypeLiteral.get(ModelParser.class));
assertNotNull(parsers2);
assertEquals(1, parsers2.size());
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
Expand Down Expand Up @@ -135,6 +147,9 @@ 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
Expand All @@ -150,11 +165,17 @@ void testGuice() throws Exception {
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;

Expand Down Expand Up @@ -198,26 +219,35 @@ 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 =
List<Binding<ModelParser>> parsers =
container.lookup(Injector.class).findBindingsByType(TypeLiteral.get(ModelParser.class));
assertNotNull(parsers2);
assertEquals(1, parsers2.size());
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;

Expand Down
Loading

0 comments on commit c0d5f54

Please sign in to comment.