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

No bean found in case of @LookupIfProperty + @Priority annotations #24961

Closed
balladali opened this issue Apr 15, 2022 · 11 comments
Closed

No bean found in case of @LookupIfProperty + @Priority annotations #24961

balladali opened this issue Apr 15, 2022 · 11 comments
Labels
area/arc Issue related to ARC (dependency injection) kind/bug Something isn't working

Comments

@balladali
Copy link

balladali commented Apr 15, 2022

Describe the bug

I have 2 configurations:

@Singleton
public class ProviderConfiguration {
    @Singleton
    @Unremovable
    @LookupIfProperty(name = "type", stringValue = "one", lookupIfMissing = true)
    public Provider providerOne() {
        return new ProviderOne();
    }

    @Singleton
    @Unremovable
    @LookupIfProperty(name = "type", stringValue = "two")
    public Provider providerTwo() {
        return new ProviderTwo();
    }
}
@Singleton
public class AdditionalProviderConfiguration {

    @Singleton
    @Unremovable
    @Alternative
    @Priority(0)
    @LookupIfProperty(name = "type", stringValue = "one", lookupIfMissing = true)
    public Provider providerThree() {
        return new ProviderThree();
    }
}

According to these configurations and property value I want to use different beans of type Provider. All three providers (ProviderOne, ProviderTwo and ProviderThree implement the same interface Provider). I select the bean using the following code:

Provider provider = Arc.container().select(Provider.class).get();

But when property type is two, I get an exception (see Actual behavior).

Expected behavior

  • when AdditionalProviderConfiguration doesn't exist and property type is one, ProviderOne bean is selected
  • when AdditionalProviderConfiguration exists and property type is one, ProviderThree bean is selected
  • when AdditionalProviderConfiguration exists and type is two, ProviderTwo bean is selected

Actual behavior

  • when AdditionalProviderConfiguration doesn't exist and property type is one, ProviderOne bean is selected - As expected
  • when AdditionalProviderConfiguration exists and property type is one, ProviderThree bean is selected - As expected
  • when AdditionalProviderConfiguration exists and type is two, the following exception:
javax.enterprise.inject.UnsatisfiedResolutionException: No bean found for required type [interface com.test.Provider] and qualifiers [[]]
	at io.quarkus.arc.impl.InstanceImpl.bean(InstanceImpl.java:188)
	at io.quarkus.arc.impl.InstanceImpl.getInternal(InstanceImpl.java:209)
	at io.quarkus.arc.impl.InstanceImpl.get(InstanceImpl.java:95)
	at com.test.GreetingResource.hello(GreetingResource.java:19)
	at com.test.GreetingResource_Subclass.hello$$superforward1(Unknown Source)
	at com.test.GreetingResource_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at com.test.GreetingResource_Subclass.hello(Unknown Source)
	at com.test.GreetingResource$quarkusrestinvoker$hello_e747664148511e1e5212d3e0f4b40d45c56ab8a1.invoke(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:7)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:545)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

"11.0.6" 2020-01-14 LTS

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.8.0.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Maven 3.6.3

Additional information

No response

@balladali balladali added the kind/bug Something isn't working label Apr 15, 2022
@geoand geoand added area/arc Issue related to ARC (dependency injection) and removed triage/needs-triage labels Apr 15, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Apr 15, 2022

/cc @manovotn, @mkouba

@mkouba
Copy link
Contributor

mkouba commented Apr 19, 2022

@balladali How does the Instance injection point look like? From the stack trace it seems that you don't use Arc.container().select() but an injected Instance...

@balladali
Copy link
Author

@mkouba injection point:

@Path("/hello")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return Arc.container().select(Provider.class).get().print();
    }
}

@mkouba
Copy link
Contributor

mkouba commented Apr 19, 2022

I think that it's caused by the fact that CDI type-safe resolution rules are applied first and the @LookupIfProperty is evaluated afterwards. In other words, the container finds three beans of type Provider then attempts to resolve the ambiguity, i.e. removes the providerOne() and providerTwo() producer methods and keeps the alternative providerThree(), but later on the providerThree() is removed as well because of its @LookupIfProperty does not match.

And this works as designed. Because @LookupIfProperty cannot be included in the type-safe resolution rules, it's merely a facade on top of programmatic lookup.

@manovotn WDYT?

@manovotn
Copy link
Contributor

And this works as designed. Because @LookupIfProperty cannot be included in the type-safe resolution rules, it's merely a facade on top of programmatic lookup.

+1, came to the same conclusion.

Looking at expected behavior, it seems that if you just remove @Alternative from the third provider declaration, it might do what you want?

@balladali
Copy link
Author

@manovotn If I remove @Alternative from third provider, the case with type=two works, but another problem appears in case when type=one:

javax.enterprise.inject.AmbiguousResolutionException: Beans: [com.test.AdditionalProviderConfiguration_ProducerMethod_providerThree_add76230713fdad3fee70862417ad2d4efc29217_Bean@45baaaaa, com.test.ProviderConfiguration_ProducerMethod_providerOne_da4da90171efd7ac159cd00e4ab8364603ba2564_Bean@562f0512]
	at io.quarkus.arc.impl.InstanceImpl.bean(InstanceImpl.java:191)
	at io.quarkus.arc.impl.InstanceImpl.getInternal(InstanceImpl.java:209)
	at io.quarkus.arc.impl.InstanceImpl.get(InstanceImpl.java:95)
	at com.test.GreetingResource.hello(GreetingResource.java:16)
	at com.test.GreetingResource_Subclass.hello$$superforward1(Unknown Source)
	at com.test.GreetingResource_Subclass$$function$$1.apply(Unknown Source)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:49)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at com.test.GreetingResource_Subclass.hello(Unknown Source)
	at com.test.GreetingResource$quarkusrestinvoker$hello_e747664148511e1e5212d3e0f4b40d45c56ab8a1.invoke(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
	at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:7)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:545)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

Looks like @Priority doesn't work in this case.

@mkouba
Copy link
Contributor

mkouba commented Apr 19, 2022

Looks like @Priority doesn't work in this case.

It doesn't, the priority is simply ignored. You'd need to either inject the List<Provider> and take the first element (provided you'll set the priority to a value > 0; 0 is the default value for any bean) or inject Instance<Provider> and use instance.iterator().next().

@manovotn
Copy link
Contributor

Yes, the @Priority is just an "enablement annotation" for @Alternative. It has no other purpose and is therefore ignores if you remove @Alternative.

but another problem appears in case when type=one:

Ah I misread your initial code, I can see providerOne and providerThree both have lookupIfMissing = true so that will clash. My bad.

If you had lookupIfMissing = true on only one of them, that would also resolve the case when the attribute is not defined - which is what you stated in Expected Behavior section. However, you will still have an ambiguous resolution in case type exists and type=one. For that, you'd have to use what Martin suggested.

@balladali
Copy link
Author

Is there some ability to get this ordered list of beans without @Inject annotations, e.g. using Arc container?

@mkouba
Copy link
Contributor

mkouba commented Apr 20, 2022

Is there some ability to get this ordered list of beans without @Inject annotations, e.g. using Arc container?

Yes, but you'll need to use a TypeLiteral together with io.quarkus.arc.All.Literal.INSTANCE, i.e. something like Arc.container().select(new TypeLiteral<List<Provider>>() {}, All.Literal.INSTANCE).get()

UPDATE: I've sent #25061 to make the programmatic lookup easier.

@mkouba
Copy link
Contributor

mkouba commented Apr 28, 2022

I'm going to close this one. Feel free to reopen if you want to discuss the problem further.

@mkouba mkouba closed this as completed Apr 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/arc Issue related to ARC (dependency injection) kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants