Weld CDI: Producers in superclasses of alternatives are effective despite cdi-spec - cdi

Given
public class BeanContainer {
#Produces
Bean bean = new Bean();
}
and its alternative:
#Alternative
public class BeanContainerAlt extends BeanContainer {
#Produces
int producerInt = 10;
}
where Bean is
public class Bean {
boolean didPostConstruct = false;
#PostConstruct
public void postConstruct() {
didPostConstruct = true;
}
}
injected into MainClass:
public class MainClass {
#Inject
Bean bean;
#Inject
Integer producedInt;
}
Then:
Weld weld = new Weld()
.disableDiscovery()
.addBeanClass(MainClass.class)
.addBeanClass(BeanContainer.class)
.addBeanClass(BeanContainerAlt.class)
.alternatives(BeanContainerAlt.class);
WeldContainer container = weld.initialize();
final MainClass mainClass = container.select(MainClass.class).get();
assertFalse(mainClass.bean.didPostConstruct);
assertEquals(10, (long)mainClass.producedInt);
BeanContainer containerObject = container.select(BeanContainer.class).get();
assertEquals(BeanContainerAlt.class, containerObject.getClass());
gives no error. I would have expected that Bean.class would have to be added using addBeanClass to be able to satisfy the inject in MainClass. The explanation is that the Superclass of BeanContainerAlt whose Producers should imO not be effective, produces the Bean-Object.
Is that behavior intended or even according to the spec (I did not find it), is it probably defined in the weld-documentation?
The sourcecode can be found in
examplesrc
mvn clean install -Dtest=ReproProducersInSuperclasses
in that project should make it run

Indeed, the fields and methods annotated with #Producer are not inherited - as discussed in the accepted answer to Why are Producers not inherited in CDI
However, according to the CDI specification:
5.1.2. Enabled and disabled beans
A bean is said to be enabled if:
(E1) it is deployed in a bean archive, and
(E2) it is not a producer method or field of a disabled bean, and
(E3) it is not specialized by any other enabled bean, as defined in Specialization, and either
(E4) it is not an alternative, or it is a selected alternative of at least one bean archive or the application.
Otherwise, the bean is said to be disabled.
According to these definitions and code above:
BeanContainer is not an alternative (E4) and therefore is enabled managed bean
BeanContainerAlt is a selected alternative (E4) and therefore is enabled managed bean
Bean and int are enabled because they are not a producer method or field of a disabled bean (E2), as BeanContainer and BeanContainerAlt are both enabled (E4)
Therefore producer fields in both BeanContainer and BeanContainerAlt are used to resolve dependencies.
The test fails as shown below when BeanContainer is not deployed (E1):
WELD-001408: Unsatisfied dependencies for type Bean with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject
net.oneandone.iocunit.testalt.MainClass.bean
The test fails as shown below when BeanContainerAlt is not selected (E4):
WELD-001408: Unsatisfied dependencies for type Integer with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject
net.oneandone.iocunit.testalt.MainClass.producedInt

Related

Ambiguous dependencies when I added a producer class

I was reading/testing the following tutorial about CDI: [link].
And I got an exception when I added a producer class to the code.
Basically, there's an interface with a default implementation:
public interface ATMTransport {
public void communicateWithBank(byte[] datapacket);
}
#Default
public class StandardAtmTransport implements ATMTransport {
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Standard transport");
}
}
Next, there's another class which injects the ATMTransport interface:
#Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
#Inject
private ATMTransport transport;
public void deposit(BigDecimal bd) {
transport.communicateWithBank(null);
}
}
Everything is OK so far. So in the section about 'producers', they show a new class:
public class TransportFactory {
#Produces ATMTransport createTransport() {
System.out.println("ATMTransport created with producer");
return new StandardAtmTransport();
}
}
Then, by adding the producer class I got this exception:
WELD-001409: Ambiguous dependencies for type ATMTransport with qualifiers #Default
at injection point [BackedAnnotatedField] #Inject private AutomatedTellerMachineImpl.transport at AutomatedTellerMachineImpl.transport(AutomatedTellerMachineImpl.java:0)
Possible dependencies:
- Managed Bean [class StandardAtmTransport] with qualifiers [#Default #Any],
- Producer Method [ATMTransport] with qualifiers [#Any #Default] declared as [[BackedAnnotatedMethod] #Produces TransportFactory.createTransport()]
I solved the problem by using qualifiers, but I really don't know why.
My question is, why does the producer class cause that exception? (They didn't mention anything about it on their tutorial).
I really need a little explanation.
Based on the code you supplied, you ended up creating a default implementation of ATMTransport that is a managed bean. The exception is caused by having both beans available with the same injection targets. #Default is the qualifier added to all CDI beans. Basically, there is no need for the producer method since the one provided by the class definition by itself is the same.
I guess the bigger question - what were you trying to do with the producer method?

Weld exception when value for producer annotation is defined

I tried to implement solution from another SO question:
https://stackoverflow.com/a/10059245/1943765 but it is not working in all cases.
When I put this code in CDI bean (bean code is shown on bottom):
#Inject #HttpParam
private String code;
everything works fine. But when I try to define value for #HttpParam annotation (so, not default one) Weld is unable to start:
#Inject #HttpParam(value="code")
private String token;
I get this exception:
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type String with qualifiers #HttpParam
at injection point [BackedAnnotatedField] #Inject #HttpParam private org.test.site.jsf.beans.request.ActivationBean.token
at org.test.site.jsf.beans.request.ActivationBean.token(ActivationBean.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
- Producer Method [String] with qualifiers [#HttpParam #Any] declared as [[BackedAnnotatedMethod] #Produces #HttpParam public org.test.site.cdi.producer.HttpParamProducer.getHttpParameter(InjectionPoint)]
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:359)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:281)
at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:155)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:518)
at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:63)
at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:56)
... 4 more
The code I used is similar to linked SO question.
The custom #HttpParam annotation:
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER})
public #interface HttpParam {
public String value() default "";
}
The annotation value producer:
public class HttpParamProducer {
#Inject
FacesContext facesContext;
#Produces
#HttpParam
String getHttpParameter(InjectionPoint ip) {
String name = ip.getAnnotated().getAnnotation(HttpParam.class).value();
if ("".equals(name)) name = ip.getMember().getName();
return facesContext.getExternalContext()
.getRequestParameterMap()
.get(name);
}
}
And CDI bean using it is something like:
#Named("activation")
#RequestScoped
public class ActivationBean implements Serializable{
#Inject
#HttpParam(value="code")
private String token;
// rest of valid class code
}
Also I am using Tomcat 8 server with Weld Servlet 2.3.1.Final.
So.. what am I doing wrong? :-/
You need to add #Nonbinding to your value attribute so that its value does not necessarily select which producer method to invoke, but allows for a single factory style producer inspecting your injection point.
You may also want to review what is the purpose of #Nonbinding annotation in a Qualifier supposed to be in Java EE7?
By default , attributes in CDI Qualifier also are used to determine which bean to be injected.
Since you inject #HttpParam(value="code") , but your producer just produces #HttpParam(value=""). They are not matched and CDI don't know what to inject.
To make CDI ignore attributes in CDI Qualifier when determining which bean to be injected, you can mark #Nonbinding on these attributes
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER})
public #interface HttpParam {
#Nonbinding
public String value() default "";
}
I am late to add this, but for the sake of others who may be using this question to write similar parameter based injections, let me add this.
Adding a specific which I did not find above. I faced the same situation, but with a twist in the flavor.My Qualifier and the Producer method were in a module (say injector-module) different from the one in which I was doing the injection (say injected-module). All NonBinding annotations were given.
However, I had the beans.xml only in the injected-module.
The injection failed.
Adding the beans.xml in the META-INF of the injector-module did the trick.

CDI #Specializes and Constructor Injection with #Postconstruct

I have the following classes:
#Named
#ViewScoped
public class BaseClass {
private SomeDependency dep;
public BaseClass(){}
#Inject
public BaseClass(SomeDependency dep) {
this.dep = dep;
}
#PostConstruct
private void initialize() {
dep.doSomething(); // Point "A"
}
public String getProperty() {
return "BaseClass-Property";
}
#Specializes
public class SpecialClass extends BaseClass() {
#Override
public String getProperty() {
return "SpecialClass-Property";
}
}
Now in some .xhtml I have something like
<h:outputText value="#{baseClass.property}" />
This works fine without the SpecialClass. It breaks with a NullPointerException at Point "A" if I include the SpecialClass in the project.
Well, according to the Weld specification, this is more or less intended behavior:
When an enabled bean specializes another bean, the other bean is never
instantiated or called by the container.
Nevertheless, now I have to make sure that every #Specializes bean implements the complete constructor like
public SpecialClass() {}
#Inject
public SpecialClass(SomeDependency dep) { super(dep); }
which IMHO is kind of counter-intuitive and produces a lot of duplicated, boilerplate code, especially with something like 5-6 arguments for every constructor. Also, this is never caught when creating a new specialized bean since the project is always still compile clean.
Am I doing something wrong or is there no alternative to implementing the constructors over and over again?
BTW, I do use Constructor Injection to create easily testable classes where I can just use the constructor to "Inject" dummy implementations of the dependencies.
CDI 1.1 spec at section 4.3 says:
"The only way one bean can completely override a second bean at all
injection points is if it implements all the bean types and declares
all the qualifiers of the second bean."
Your base class is annotated with the Named qualifier and the specializing class is not. You should also mark it with Alternative and enable it in beans.xml. There's also the ViewScoped annotation. Unless it's the Omnifaces' ViewScoped, it looks like you're mixing up JSF managed beans with CDI beans.

Deltaspike Data module: no bean matches the injection point

I am trying DeltaSpike Data Module on Wildfly i followed the things mentioned in the document, when I try to run a Servlet having a Repository i am getting a NullPointerException while using the repository
#Inject
private OrdersRepository orderRep;
List<OrderDto> dao = orderRep.findByRetailer("MyRetail"); // NullPointer
Code
#Repository(forEntity = Order.class)
#MappingConfig(OrderMapper.class)
#EntityManagerConfig(entityManagerResolver = MyDBResolver.class)
public abstract class OrdersRepository extends AbstractEntityRepository<OrderDto, String> {
#Query(named = Order.ORDER_BY_RETAILER, max = 1)
public abstract List<OrderDto> findByRetailer(String retailer);
}
...
Am I missing anything here ?
Try adding #Dependent to your repository classes.
CDI 1.1 used in WildFly has implicit bean archives by default, i.e. candidate bean classes require a bean defining annotation.

FacesConverter Inject workaround - constructor alternative

This is in relation to the workaround for the known issue of JSF FacesConverter not being an eligible #Inject target for CDI. I followed the workaround at CDI Injection into a FacesConverter. However, in my FacesConverter, I had been utilizing the feature of having the Class of the object passed into a constructor.
From the javadoc - "If a converter has a single argument constructor that takes a Class instance and the Class of the data to be converted is known at converter instantiation time, this constructor must be used to instantiate the converter instead of the zero-argument version." This is in direct conflict with the CDI requirement for "normal scope" beans where a no-arg or #Inject annotated constructor is all that's allowed.
So in summary, I want to use a Converter that can take CDI Injections, and has access to the Class of the object being converted at runtime.
I am using Mojarra 2.2.4 on Glassfish 4 with Weld 2.0.4.
For those who might be interested in this alternative, I was able to replace the Inject annotations with programmatic lookup via BeanManager in the constructor. The condensed code is below. I've not performance tested, and suspect that may be a downside. If time permits I'll compare to the Omnifaces solution.
EDIT: The cost of the BeanManager lookup turned out to be minimal. The constructor takes on average <1ms.
#FacesConverter(forClass = AbstractEntity.class)
public class EntityConverter implements Converter {
private LocationService locationService;
private Class entityClass;
//Special parameterized constructor for #FacesConverter described in the original question
public EntityConverter(Class entityClass) throws NamingException {
this.entityClass = entityClass;
this.locationService = loadManagedBean(LocationService.class, "locationService");
}
//Generic lookup method for #Named managed beans
protected <T> T loadManagedBean(Class<T> clazz, String beanName) throws NamingException {
InitialContext initialContext = new InitialContext();
BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");
Bean<T> bean = (Bean<T>) bm.getBeans(beanName).iterator().next();
CreationalContext<T> cc = bm.createCreationalContext(bean);
T beanInstance = (T) bm.getReference(bean, clazz, cc);
return beanInstance;
}
}

Resources