FacesConverter Inject workaround - constructor alternative - jsf

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;
}
}

Related

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

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

JSF Converter - NullPointerException [duplicate]

How can I inject a dependency like #EJB, #PersistenceContext, #Inject, #AutoWired, etc in a #FacesConverter? In my specific case I need to inject an EJB via #EJB:
#FacesConverter
public class MyConverter implements Converter {
#EJB
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// myService.doSomething
}
}
However, it didn't get injected and it remains null, resulting in NPEs. It seems that #PersistenceContext and #Inject also doesn't work.
How do I inject a service dependency in my converter so that I can access the DB?
Can I use #EJB to inject my service into a #FacesConverter?
No, not until JSF 2.3 is released. The JSF/CDI guys are working on that for JSF 2.3. See also JSF spec issue 1349 and this related "What's new in JSF 2.3?" article of my fellow Arjan Tijms. Only then dependency injection like #EJB, #PersistenceContext, #Inject, etc will work in a #FacesConverter when you explicitly add managed=true attribute to the annotation.
#FacesConverter(value="yourConverter", managed=true)
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
If not, what's the "correct" way to do this?
Before JSF 2.3, you have several options:
Make it a managed bean instead. You can make it a JSF, CDI or Spring managed bean via #ManagedBean, #Named or #Component. The below example makes it a JSF managed bean.
#ManagedBean
#RequestScoped
public class YourConverter implements Converter {
#EJB
private YourService service;
// ...
}
And the below example makes it a CDI managed bean.
#Named
#RequestScoped
public class YourConverter implements Converter {
#Inject
private YourService service;
// ...
}
Reference it as <h:inputXxx converter="#{yourConverter}"> instead of <h:inputXxx converter="yourConverter">, or as <f:converter binding="#{yourConverter}"> instead of <f:converter converterId="yourConverter">. Don't forget to remove the #FacesConverter annotation!
The disadvantage is that you cannot specify forClass and thus need to manually define the converter everywhere in the view where necessary.
Inject it in a regular managed bean instead.
#ManagedBean
#RequestScoped
public class YourBean {
#EJB
private YourService service;
// ...
}
And in your converter, grab or call it via EL.
YourBean yourBean = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
// Then e.g. either
YourEntity yourEntity = yourBean.getService().findByStringId(value);
// Or
YourEntity yourEntity = yourBean.findEntityByStringId(value);
This way you can keep using #FacesConverter.
Manually grab the EJB from JNDI.
YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
The disadvantage is that there is a certain risk that this is not entirely portable. See also Inject EJB bean from JSF managed bean programmatically.
Install OmniFaces. Since version 1.6, it transparently adds support for #EJB (and #Inject) in a #FacesConverter without any further modification. See also the showcase. If you happen to need the converter for <f:selectItem(s)>, then the alternative is to use its SelectItemsConverter which will automatically perform the conversion job based on select items without the need for any database interaction.
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
See also Conversion Error setting value for 'null Converter'.
See also:
How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired
CDI Injection into a FacesConverter
Getting an #EJB in a #FacesValidator and #FacesConverter
The answer is Yes if you can accommodate Seam Faces module in your web application. Please check this post Injection of EntityManager or CDI Bean in FacesConverter. You can use #EJB in similar fashion.
You could access it indirectly through FacesContext, which is a parameter in both Converter methods.
The converter could be also annotated CDI Named with Application scope. When accessing the facade, two instances of the same class are used. One is the converter instance itself, dumb, without knowing EJB annotation. Another instance retains in application scope and could be accessed via the FacesContext. That instance is a Named object, thus it knows the EJB annotation. As everything is done in a single class, access could be kept protected.
See the following example:
#FacesConverter(forClass=Product.class)
#Named
#ApplicationScoped
public class ProductConverter implements Converter{
#EJB protected ProductFacade facade;
protected ProductFacade getFacadeFromConverter(FacesContext ctx){
if(facade==null){
facade = ((ProductConverter) ctx.getApplication()
.evaluateExpressionGet(ctx,"#{productConverter}",ProductConverter.class))
.facade;
}
return facade;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
return getFacadeFromConverter(context).find(Long.parseLong(value));
}
...
#Inject will only works in CDI managed instances
This only works at least Java EE 7 and CDI 1.1 server:
#FacesConverter
public class MyConverter implements Converter {
protected MyService myService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
myService = CDI.current().select(MyService .class).get();
myService.doSomething();
}
}
https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html
https://stackoverflow.com/a/33017416/5626568
By Luis Chacon, Sv
Works fine, tested
definition EJB :
#Stateless
#LocalBean
public class RubroEJB {
#PersistenceContext(unitName = "xxxxx")
private EntityManager em;
public List<CfgRubroPres> getAllCfgRubroPres(){
List<CfgRubroPres> rubros = null;
Query q = em.createNamedQuery("xxxxxxx");
rubros = q.getResultList();
return rubros;
}
}
define bean with the Aplication bean scope, for get the EJB Object
#ManagedBean(name="cuentaPresService", eager = true)
#ApplicationScoped
public class CuentaPresService {
#EJB
private RubroEJB cfgCuentaEJB;
public RubroEJB getCfgCuentaEJB() {
return cfgCuentaEJB;
}
public void setCfgCuentaEJB(RubroEJB cfgCuentaEJB) {
this.cfgCuentaEJB = cfgCuentaEJB;
}
}
final Access to Ejb Object from Converter:
#FacesConverter("cuentaPresConverter")
public class CuentaPresConverter implements Converter {
#EJB
RubroEJB rubroEJB;
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
try {
CuentaPresService service = (CuentaPresService) fc.getExternalContext().getApplicationMap().get("cuentaPresService");
List<CfgCuentaPres> listCuentas=service.getCfgCuentaEJB().getAllCfgCuentaPres();
................

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.

Resources