Search code examples
ejbjava-ee-7

Why do I get an "Ambiguous dependencies for interface" Exception when I'm already uses the @Produces annotation?


I'm using two Messaging Oriented Middleware in my project. RabbitMQ and Apache Kafka. I have an consumer interface IConsume which are implemented by ConsumerRabbitMQ and ConsumerKafka. At startup going through some conditions I use the @Produces annotation to choose an implementation for the Interface Bean that I will inject, but it gives me this error.

Exception 1:

org.jboss.weld.exceptions.DeploymentException: WELD-001409: Ambiguous dependencies for type IConsume with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private com.mycompany.chatapp.startup.RunConsumers.ct
  at com.mycompany.chatapp.startup.RunConsumers.ct(RunConsumers.java:0)
  Possible dependencies: 
  - Session bean [class com.mycompany.chatapp.messagegateway.ConsumerRabbitMQ with qualifiers [@Any @Default]; local interfaces are [IConsume],
  - Producer Method [IConsume] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @Produces public com.mycompany.chatapp.startup.MOMConfigBean.produceIConsume()],
  - Session bean [class com.mycompany.chatapp.messagegateway.ConsumerKafka with qualifiers [@Any @Default]; local interfaces are [IConsume]

@Default and @Alternative works, but I want it to choose by checking which of the Middleware is running.

The lookup works, I also tried beanName. I think the problem is with the @Produces, but I can't find to seem what.

import javax.enterprise.inject.Produces;

@Singleton
@Startup
public class MOMConfigBean {
private String mom;


@PostConstruct
public void init() {
    mom = "Kafka";
}

@EJB(lookup = "java:global/Chatapp/ConsumerKafka!com.mycompany.chatapp.messagegateway.IConsume")
IConsume kafkaConsumer;

@EJB(lookup = "java:global/Chatapp/ConsumerRabbitMQ!com.mycompany.chatapp.messagegateway.IConsume")
IConsume rabbitConsumer;

@Produces
public IConsume produceIConsume() {
    if ("Kafka".equals(mom)) {
        return kafkaConsumer;
    } else {
        return rabbitConsumer;
    }
}


public interface IConsume {
// some code
}

@Stateless
public class ConsumerKafka implements IConsume{
// some code
}

@Stateless
public class ConsumerRabbitMQ implements IConsume {
// some code
}

public class runConsumers{

@Inject
private IConsume ct;

}

Solution

  • You have three ambiguous sources of IConsume instances:

    1. a ConsumerKafka EJB
    2. a ConsumerRabbitMQ EJB
    3. an @Produces public IConsume produceIConsume() method.

    You need to disambiguate the source of the IConsume instances using a qualifier.

    This qualifier would look something like:

    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    import javax.inject.Qualifier;
    
    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD, FIELD})
    public @interface ConditionalMom {
    }
    

    Then qualify the producer:

    @Produces
    @ConditionalMom
    public IConsume produceIConsume() {
        if ("Kafka".equals(mom)) {
            return kafkaConsumer;
        } else {
            return rabbitConsumer;
        }
    }
    

    and the injection site:

    public class runConsumers{
    
        @Inject
        @ConditionalMom
        private IConsume ct;
    
    }
    

    Now you have a single source of @ConditionalMom IConsume instances so it is no longer ambiguous.

    You will find that you will be using qualifiers all over the place as you start to further exploit CDI features.