2

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;

}
Stephen
  • 1,072
  • 1
  • 19
  • 33
Jin Tran
  • 23
  • 2

1 Answers1

1

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.

Steve C
  • 18,876
  • 5
  • 34
  • 37
  • Thank you for your explanation. I followed this thread to create the coding. https://stackoverflow.com/questions/7920123/inject-ejb-bean-based-on-conditions/7923159#7923159 Perhaps it was outdated and I assumed because I use the @Produce annotation I didn't have to use a qualifier. – Jin Tran Jan 29 '19 at 01:25
  • Yes, that solution does not work as noted in one of the comments – Steve C Jan 29 '19 at 03:12