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

Fix issue with default beans resolution #20263

Merged
merged 2 commits into from
Sep 21, 2021
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 @@ -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";
}
}

}