Skip to content

Commit

Permalink
Merge pull request #20263 from stuartwdouglas/default-beans-resolution
Browse files Browse the repository at this point in the history
Fix issue with default beans resolution
  • Loading branch information
gastaldi authored Sep 21, 2021
2 parents 37a270d + 7d7c9fb commit ebbd26f
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;

import java.lang.annotation.Retention;
Expand Down Expand Up @@ -41,11 +42,11 @@ public void testHealth() {
when().get("/q/health/live").then()
.body("status", is("UP"),
"checks.status", contains("UP", "UP"),
"checks.name", contains("noScope", "noScopeStereotype"));
"checks.name", containsInAnyOrder("noScope", "noScopeStereotype"));
when().get("/q/health/live").then()
.body("status", is("DOWN"),
"checks.status", contains("DOWN", "DOWN"),
"checks.name", contains("noScope", "noScopeStereotype"));
"checks.name", containsInAnyOrder("noScope", "noScopeStereotype"));
} finally {
RestAssured.reset();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.arc;

import java.lang.annotation.Annotation;
import java.util.Iterator;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Instance;
import javax.enterprise.util.TypeLiteral;
Expand Down Expand Up @@ -33,4 +34,19 @@ public interface InjectableInstance<T> extends Instance<T> {
*/
void clearCache();

/**
* This method attempts to resolve ambiguities.
* <p>
* In general, if multiple beans are eligible then the container eliminates all beans that are:
* <ul>
* <li>not alternatives, except for producer methods and fields of beans that are alternatives,</li>
* <li>default beans.</li>
* </ul>
*
* @return an iterator over the contextual references of the disambiguated beans
* @see DefaultBean
*/
@Override
Iterator<T> iterator();

}
Original file line number Diff line number Diff line change
Expand Up @@ -565,30 +565,38 @@ private static Set<InjectableBean<?>> resolve(List<InjectableBean<?>> matching)
if (matching.isEmpty()) {
return Collections.emptySet();
} else if (matching.size() == 1) {
return Collections.singleton(matching.get(0));
}

// Try to resolve the ambiguity
List<InjectableBean<?>> resolved = new ArrayList<>(matching);

resolved.removeIf(InjectableBean::isDefaultBean);
if (resolved.size() == 1) {
return Collections.singleton(resolved.get(0));
}

resolved.removeIf(not(ArcContainerImpl::isAlternativeOrDeclaredOnAlternative));
if (resolved.size() == 1) {
return Collections.singleton(resolved.get(0));
} else if (resolved.size() > 1) {
resolved.sort(ArcContainerImpl::compareAlternativeBeans);
// Keep only the highest priorities
Integer highest = getAlternativePriority(resolved.get(0));
resolved.removeIf(injectableBean -> !highest.equals(getAlternativePriority(injectableBean)));
if (resolved.size() == 1) {
return Collections.singleton(resolved.get(0));
return Set.of(matching.get(0));
}
// Try to resolve the ambiguity and return the set of disambiguated beans

// First remove the default beans
List<InjectableBean<?>> nonDefault = new ArrayList<>(matching);
nonDefault.removeIf(InjectableBean::isDefaultBean);
if (nonDefault.isEmpty()) {
// All the matching beans were default
return Set.copyOf(matching);
} else if (nonDefault.size() == 1) {
return Set.of(nonDefault.get(0));
}

// More than one non-default bean remains - eliminate beans that don't have a priority
List<InjectableBean<?>> priorityBeans = new ArrayList<>(nonDefault);
priorityBeans.removeIf(not(ArcContainerImpl::isAlternativeOrDeclaredOnAlternative));
if (priorityBeans.isEmpty()) {
// No alternative/priority beans are present
return Set.copyOf(nonDefault);
} else if (priorityBeans.size() == 1) {
return Set.of(priorityBeans.get(0));
} else {
// Keep only the highest priorities
priorityBeans.sort(ArcContainerImpl::compareAlternativeBeans);
Integer highest = getAlternativePriority(priorityBeans.get(0));
priorityBeans.removeIf(bean -> !highest.equals(getAlternativePriority(bean)));
if (priorityBeans.size() == 1) {
return Set.of(priorityBeans.get(0));
}
return Set.copyOf(priorityBeans);
}
return new HashSet<>(matching);
}

private static boolean isAlternativeOrDeclaredOnAlternative(InjectableBean<?> bean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,40 @@
import io.quarkus.arc.Arc;
import io.quarkus.arc.DefaultBean;
import io.quarkus.arc.test.ArcTestContainer;
import java.util.Set;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.CDI;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

public class DefaultClassBeanTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(Producer.class,
GreetingBean.class, Hello.class, PingBean.class);
GreetingBean.class, Hello.class, PingBean.class, Author.class, SciFi.class, Fantasy.class, Detective.class);

@Test
public void testInjection() {
Hello hello = Arc.container().instance(Hello.class).get();
assertEquals("hello", hello.hello());
assertEquals("pong", hello.ping());
var result = hello.instance();
StringBuilder sb = new StringBuilder();
for (var i : result) {
i.write(sb);
}
Assertions.assertTrue(sb.toString().contains("SciFi"));
Assertions.assertTrue(sb.toString().contains("Fantasy"));
Assertions.assertFalse(sb.toString().contains("Detective"));
Set<Detective> detectives = Arc.container().beanManager().createInstance().select(Detective.class).stream()
.collect(Collectors.toSet());
Assertions.assertEquals(1, detectives.size());
}

@Test
Expand All @@ -40,6 +55,9 @@ static class Hello {
@Inject
PingBean ping;

@Inject
Instance<Author> instance;

String hello() {
return bean.greet();
}
Expand All @@ -48,6 +66,9 @@ String ping() {
return ping.ping();
}

Instance<Author> instance() {
return instance;
}
}

@DefaultBean // This one is overriden by Producer.greetingBean()
Expand Down Expand Up @@ -85,4 +106,35 @@ String greet() {

}

interface Author {
void write(StringBuilder sb);
}

@Singleton
static class SciFi implements Author {

@Override
public void write(StringBuilder sb) {
sb.append("SciFi");
}
}

@Singleton
static class Fantasy implements Author {

@Override
public void write(StringBuilder sb) {
sb.append("Fantasy");
}
}

@Singleton
@DefaultBean
static class Detective implements Author {

@Override
public void write(StringBuilder sb) {
sb.append("Detective");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package io.quarkus.arc.test.defaultbean;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import io.quarkus.arc.Arc;
import io.quarkus.arc.DefaultBean;
import io.quarkus.arc.test.ArcTestContainer;
import java.util.List;
import java.util.stream.Collectors;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.CDI;
import javax.inject.Inject;
Expand All @@ -17,7 +21,7 @@ public class DefaultProducerFieldTest {

@RegisterExtension
public ArcTestContainer container = new ArcTestContainer(Producer.class,
GreetingBean.class, Hello.class);
GreetingBean.class, Hello.class, Fantasy.class);

@Test
public void testInjection() {
Expand All @@ -29,16 +33,32 @@ public void testSelect() {
assertEquals("hola", CDI.current().select(GreetingBean.class).get().greet());
}

@Test
public void testInstanceIterator() {
List<Author> authors = Arc.container().instance(Hello.class).get().instance().stream().collect(Collectors.toList());
assertEquals(2, authors.size());
String result = authors.stream().map(Author::get).collect(Collectors.joining());
assertTrue(result.contains("SciFi"));
assertTrue(result.contains("Fantasy"));
}

@ApplicationScoped
static class Hello {

@Inject
GreetingBean bean;

@Inject
Instance<Author> instance;

String hello() {
return bean.greet();
}

Instance<Author> instance() {
return instance;
}

}

@Singleton
Expand All @@ -63,6 +83,31 @@ String greet() {

};

@Produces
@Singleton
@DefaultBean
Author sciFi = new Author() {

@Override
public String get() {
return "SciFi";
}
};

}

interface Author {
String get();
}

@Singleton
@DefaultBean
static class Fantasy implements Author {

@Override
public String get() {
return "Fantasy";
}
}

}

0 comments on commit ebbd26f

Please sign in to comment.