Reputation: 2281
I'm trying to use a POJO as CDI producer for injecting the right EJB but I get org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001308.
This is my producer POJO
public class STGatewayUtilProducer {
@Produces
@Chosen
public ISTGatewayUtil getISTGatewayUtil(Instance<STGatewayWSUtil> ws, Instance<STGatewayMQTTUtil> mqtt, ConfigurationManager cm) {
switch(cm.getGatewayProtocol()) {
case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
return mqtt.get();
default:
return ws.get();
}
}
}
This is the qualifier definition:
@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Chosen {}
And those are the EJB declarations:
@Stateless
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
...
}
@Stateless
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
...
}
Finally, this is the way I'm injecting the EJB:
@Inject
@Chosen
private Instance<ISTGatewayUtil> gtwUtil;
I'm facing the problem both with JBoss AS 7 and WildFly 10.
I found the core of my problem! I declared a common abstract
parent class which implements the ejb interface and let my session beans extend it: using this structure the beans can't be resolved.
Instead, if i move the implements
clause on session beans the problem disappear: may someone explain me what's wrong with my class hierarchy?
Upvotes: 2
Views: 2781
Reputation: 3533
Your case scenario here, applies quite well to CDI qualifiers, and you can still maintain the ejb session beans if you require transaction management (if you dont require any transaction logic, then i would do away with the ejbs in the first place).
That said, i would design your scenario thus:
@Qualifier
@Retention(RUNTIME)
@Target(FIELD, METHOD, PARAMETER, TYPE)
public @interface ISTGateway {
ISTGatewayType value()
enum ISTGatewayType {
MQT,
WS
}
}
The usage wil look like this: (NOTE the ejbs have been annotated with @Dependent
to enable CDI container detect them automatically )
@Stateless
@Dependent
@ISTGateway(MQT)
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
...
}
@Stateless
@Dependent
@ISTGateway(WS)
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
...
}
Your producer should look like this: (the good thing about the producer here is that you never need to update it, if you ever add a new ISTGatewayUtil)
@ApplicationScoped
public class STGatewayUtilProducer {
@Any
@Inject
private Instance<ISTGatewayUtil> istGatewayUtils;
@Inject
private ConfigurationManager configurationManager;
@Chosen
@Produces
public ISTGatewayUtil getISTGatewayUtil() {
final ISTGateway istGateway = new ISTGatewayImpl(cm.getGatewayProtocol());
return istGatewayUtils.select(istGateway).get();
}
private static final class ISTGatewayImpl extends AnnotationLiteral<ISTGateway> implements ISTGateway {
private final ISTGatewayType istGatewayType;
private ISTGatewayImpl( final ISTGatewayType istGatewayType) {
this.istGatewayType = istGatewayType;
}
public ISTGatewayType value() {
return istGatewayType;
}
}
}
Upvotes: 0
Reputation: 4980
Quoting the specification
3.2.2. Bean types of a session bean
The unrestricted set of bean types for a session bean contains all local interfaces of the bean and their superinterfaces. If the session bean has a no-interface view, the unrestricted set of bean types contains the bean class and all superclasses. In addition, java.lang.Object is a bean type of every session bean.
Remote interfaces are not included in the set of bean types.
So here as both of your session bean have a local interface, they don't have their classes in their set of bean types. Thus it is normal that you can't resolve them with their class.
You need to add extra info to your session beans definition to be able to distinguish them or declare them as No-Interface view EJB with @LocalBean annotation.
Here's a new version of your code declaring your EJB as NIV
@Stateless
@LocalBean
public class STGatewayMQTTUtil implements Serializable, ISTGatewayUtil {
...
}
@Stateless
@LocalBean
public class STGatewayWSUtil implements Serializable, ISTGatewayUtil {
...
}
Your producer don't need to inject 2 Instances<T>
. You can either inject both beans and return the chosen one.
public class STGatewayUtilProducer {
@Produces
@Chosen
public ISTGatewayUtil getISTGatewayUtil(STGatewayWSUtil ws, STGatewayMQTTUtil mqtt, ConfigurationManager cm) {
switch(cm.getGatewayProtocol()) {
case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
return mqtt;
default:
return ws;
}
}
}
or use Instance<T>
like this
public class STGatewayUtilProducer {
@Produces
@Chosen
public ISTGatewayUtil getISTGatewayUtil(Instance<ISTGatewayUtil> gw, ConfigurationManager cm) {
switch(cm.getGatewayProtocol()) {
case ConfigurationManager.GATEWAY_PROTOCOL_TYPE_MQTT:
return gw.select(STGatewayMQTTUtil.class).get();
default:
return gw.select(STGatewayWSUtil.class).get();
}
}
}
Be careful when you use a bean instance to produce a new bean, it should have the @Dependent
scope (to avoid superposing 2 lifecycle on your produced bean) or be injected with @New
keyword. Here your session beans are in @Dependent
scope since they don't specify any scope.
Upvotes: 2