Search code examples
javacdiquarkus

CDI: 2 Producers with different qualifiers


I have 2 producer-methods, one has only 1 qualifier and one has 2 qualifiers, the one from the first producer and an additional one:

@ApplicationScoped
public class MyProducer {

  @Produces
  @Qualifier1
  MyBean produce1(InjectionPoint injectionPoint) {
    ...
  }

  @Produces
  @Qualifier1
  @Qualifier2
  MyBean produce2(InjectionPoint injectionPoint) {
    ...
  }
}

If I try to @Inject MyBean with both qualifiers, everything works fine, produce2 is called. But when injecting MyBean with only @Qualifier1, I get an AmbiguousResolutionException. In my opinion, produce2 should only be called when injection point has both qualifiers. Why isn't that the case and what can I do to make it work?

Unfortunately I can not change producer-method 1, because it is in a library. The basic plan is to call my own producer if the injection point has my additional qualifier and if not, call the one from the library.


Solution

  • I think the observed behavior is clearly specified in CDI 2.0, ch.2.3.4 "Specifying qualifiers of an injected field":

    A bean may only be injected to an injection point if it has all the qualifiers of the injection point.

    Assuming an injection point:

    @Inject @Qualifier1 MyBean myBean;
    

    Then the beans produced by both produce1 and produce1 "have all the qualifiers of the injection point", i.e. @Qualifier1, so both are eligible for injection there, resulting in the AmbiguousResolutionException.

    In fact, the spec gives an example exactly as the one in the question in ch.2.3.6 "Repeating qualifiers".


    So what can you do?

    For starters, do you really need to qualify produce2 with @Qualifier1? If you have no good reason, just drop @Qualifier1 from produce2 and you are done - injection points with @Qualifier1 select MyBean from the library, injection points with @Qualifier2 select the one from your code. Your injection points cannot have both @Qualifier1 and @Qualifier2.

    If you still want to inject MyBean from produce2 to injection points that have both @Qualifier1 and @Qualifier2, you can still do it [DISCLAIMER: Haven't actually tried this, it will probably need tweaking]. It's a bit more involved: You have to write a portable extension, which is not a hard thing at all. Brief implementation points:

    • The producer2 must be annotated ONLY with @Qualifier2
    • In you extension, observe the ProcessInjectionPoint event
    • If event.getInjectionPoint().getQualifiers() contains both @Qualifier1 and @Qualifier2
      • Use event.configureInjectionPoint().qualifiers(...) to remove @Qualifier1 from the set of qualifiers.

    The effect is that you remove the @Qualifier1 when it is present together with @Qualifier2 at runtime.