How to inject in #FacesValidator with #EJB, #PersistenceContext, #Inject, #Autowired - spring

How can I inject a dependency like #EJB, #PersistenceContext, #Inject, #AutoWired, etc in a #FacesValidator? In my specific case I need to inject a Spring managed bean via #AutoWired:
#FacesValidator("emailExistValidator")
public class EmailExistValidator implements Validator {
#Autowired
private UserDao userDao;
// ...
}
However, it didn't get injected and it remains null, resulting in java.lang.NullPointerException.
It seems that #EJB, #PersistenceContext and #Inject also doesn't work.
How do I inject a service dependency in my validator so that I can access the DB?

JSF 2.3+
If you're already on JSF 2.3 or newer, and want to inject CDI-supported artifacts via e.g. #EJB, #PersistenceContext or #Inject, then simply add managed=true to the #FacesValidator annotation to make it CDI-managed.
#FacesValidator(value="emailExistValidator", managed=true)
JSF 2.2-
If you're not on JSF 2.3 or newer yet, then you basically need to make it a managed bean. Use Spring's #Component, CDI's #Named or JSF's #ManagedBean instead of #FacesValidator in order to make it a managed bean and thus eligible for dependency injection.
E.g., assuming that you want to use CDI's #Named:
#Named
#ApplicationScoped
public class EmailExistValidator implements Validator {
// ...
}
You also need to reference it as a managed bean by #{name} in EL instead of as a validator ID in hardcoded string. Thus, so
<h:inputText ... validator="#{emailExistValidator.validate}" />
instead of
<h:inputText ... validator="emailExistValidator" />
or
<f:validator binding="#{emailExistValidator}" />
instead of
<f:validator validatorId="emailExistValidator" />
For EJBs there's a workaround by manually grabbing it from JNDI, see also Getting an #EJB in #FacesConverter and #FacesValidator.
If you happen to use JSF utility library OmniFaces, since version 1.6 it adds transparent support for using #Inject and #EJB in a #FacesValidator class without any additional configuration or annotations. See also the CDI #FacesValidator showcase example.
See also:
CDI Injection into a FacesConverter
What's new in JSF 2.2 - Injection

You can now inject into JSF validators if you're using Java EE 8 and/or JSF 2.3.
Tested using Mojarra 2.3.9.payara-p2 on Payara Server 5.192 #badassfish.
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:body>
Hello from Facelets
<h:form>
<h:messages/>
<h:inputText value="#{someBean.txtField}" validator="someValidator"/>
</h:form>
</h:body>
</html>
import javax.inject.Named;
import javax.enterprise.context.Dependent;
#Named(value = "someBean")
#Dependent
public class SomeBean {
private String txtField;
public String getTxtField() {
return txtField;
}
public void setTxtField(String txtField) {
this.txtField = txtField;
}
}
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.inject.Inject;
#FacesValidator(value = "someValidator", managed = true)
public class CustomValidator implements Validator<String> {
#Inject
NewClass newClass;
#Override
public void validate(FacesContext context, UIComponent component, String value)
throws ValidatorException {
System.out.println("validator running");
System.out.println("injected bean: " + newClass);
if (value != null && value.equals("badvalue")) {
throw new ValidatorException(new FacesMessage(newClass.getMessage()));
}
}
}
public class NewClass {
public String getMessage() {
return "secret message";
}
}
import javax.faces.annotation.FacesConfig;
// WITHOUT THIS INJECTION WILL NOT WORK!
#FacesConfig(version = FacesConfig.Version.JSF_2_3)
public class ConfigurationBean {
}
Should render something like:
I banged my head on the wall for about an hour before realizing the need for ConfigurationBean. From the documentation:
FacesConfig.Version.JSF_2_3
This value indicates CDI should be used for EL resolution as well as enabling JSF CDI injection, as specified in Section 5.6.3 "CDI for EL Resolution" and Section 5.9 "CDI Integration"
And from this GitHub issue, https://github.com/eclipse-ee4j/glassfish/issues/22094:
By default, JSF 2.3 runs in a compatibility mode with previous releases of JSF, unless a CDI managed bean is included in the application with the annotation #javax.faces.annotation.FacesConfig. To switch into a JSF 2.3 mode you will need a configuration bean like below: (shows ConfigurationBean)
...
The fact that JSF needs to be switched into the "current version" was highly controversial. Pretty much the entire EG voted against that, but eventually we could not get around the backwards compatibility requirements that the JCP sets for Java EE and the spec lead enforces.

Related

#Autowired service inside a #ManagedBean #Component class is null during JSF request [duplicate]

This question already has answers here:
Spring JSF integration: how to inject a Spring component/service in JSF managed bean?
(4 answers)
Closed 6 years ago.
I tried union Spring 3(MVC) with JSF 2. I have some experience in Spring and JSF, but never tried to join them before. In the end I have 2 files
#ManagedBean(name = "userBean")
#Scope
#Component
public class someBean {
#Autowired
private TestService testService;
public void printString() {
System.out.println(testService.getString());
}
}
and
#ManagedBean(name = "studentBean")
#Scope
#Component
public class StudentBean {
#Autowired
private TestService testService;
public void printString() {
System.out.println(testService.getString());
}
}
For these file I have right configuration for spring, jsf, and web.xml. And have .xhtml page where I start printString() for 'someBean' and for 'StudentBean'. I have the NPE in first case and 'some string' in the console in second case.
The reason is simple - different bean names in the Spring context and JSF. all problems finished after
#Component => #Component("userBean")
public class someBean {
In the debug I saw that
private TestService testService;
#Autowired
public void setTestService(TestService testservice) {
this.testService = testService;
}
When JSF bean is creating testService sets not null, but it is null during JSF lifecycle when
public void pringString() {
testService.blah();
}
testService is null. It is what I can't understand. Has someone deep knowledge the Spring and JSF to describe this situation in details?
Both JSF and Spring can act as bean containers. The #ManagedBean annotation instructs the JSF managed bean facility to create a new instance of the class, and manage it under the given name. The #Component annotation instructs the Spring ApplicationContext to create a new instance of the class, and manage it under the given name. That is, both JSF and Spring create an instance of that class, the JSF one is reachable through EL, but the Spring one gets its dependencies injected (because, being a spring annotation, #Autowired is not understood by the JSF managed bean facility).
So you have a choice: Use the JSF managed bean facility for everything (which I would not recommend, as it is rather limited), use CDI for everything (which is an option, but does not use Spring), or use Spring for everything (which I usually do), by removing the #ManagedBean annotation, and making Spring beans accessible through EL by registering a SpringBeanFacesELResolver in your faces-config.xml. The Spring reference manual describes this in section 19.3.1.
I had the same issue, it was due to the property name of #ManagedBean annotation.My backing bean looks like this
#Component
#ManagedBean(name="mainBean")
#SessionScoped
public class MainManagedBean{...}
As you can see, the name given to the bean (mainBean) is different to the default name (mainManagedBean) that the backing bean should have.
I have fixed the issue by setting the property name to "mainManagedBean". My bean becomes like this:
#Component
#ManagedBean(name="mainManagedBean")
#SessionScoped
public class MainManagedBean{...}
I wish this can help you
I believe the reason why testService is null is because testService has not been instantiated yet when managed bean is being constructed. So you can use #PostConstruct to inject Spring beans into a managed bean.
#ManagedBean(name = "userBean")
#Scope
#Component
public class someBean {
#Autowired
private TestService testService;
public void printString() {
System.out.println(testService.getString());
}
#PostConstruct
private void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
ServletContext servletContext = (ServletContext) externalContext.getContext();
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).
getAutowireCapableBeanFactory().
autowireBean(this);
}
}
#Service
public class TestService {
......
}

Using JSF2 #ManagedProperty in Spring managed backing bean

I have a JSF backing bean that is Spring managed but I would like to be able to make use of the #ManagedProperty from JSF. The following does not work:
#Component
#Scope(Scopes.REQUEST)
public class MyRequestBean {
#ManagedProperty(value="#{param.bcIndex}")
private int bcIndex;
public int getBcIndex() {
return bcIndex;
}
public void setBcIndex(int bcIndex) {
this.bcIndex = bcIndex;
}
}
Suggestions?
Actually it is quite simple. I know of three ways to do your injection:
Use Spring's #Value annotation together with implicit El #{param} object:
#Value("#{param.bcIndex}")
private int bcIndex;
Make use of ExternalContext#getRequestParameterMap in a #PostConstruct / preRenderView listener:
//#PostConstruct
public void init() {
bcIndex = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("bcIndex");
}
Make a binding in your view utilizing <f:viewParam>:
<f:metadata>
<f:viewParam name="index" value="#{myRequestBean.bcIndex}" />
</f:metadata>

JSF/Spring Session is shared between users

I have a JSF-managed session-scopped bean. It is also a spring component so that I can inject some fields:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.springframework.stereotype.Component;
#ManagedBean
#SessionScoped
#Component
public class EpgBean {...}
The problem is that the session is shared between users! If a user does some stuff and another user from another computer connects, he sees the SessionScoped data of the other user.
Is it due to the spring #Component which would force the bean to be a singleton? What is a correct approach to this matter?
I solved the problem using spring scope annotation #Scope("session") instead of JSF #SessionScopped. I guess that since spring is configured as FacesEl resolver, it is spring scope that matters, while JSF scope is ignored.
The approach I use is to keep the managed beans inside JSF container, and inject the Spring beans into them via EL on a managed property. See related question.
To do that, set up SpringBeanFacesELResolver in faces-config.xml, so JSF EL can resolve Spring beans:
<application>
...
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
...
</application>
After that, you can inject Spring beans in your #ManagedBean annotated beans like this:
#ManagedBean
#ViewScoped
public class SomeMB {
// this will inject Spring bean with id someSpringService
#ManagedProperty("#{someSpringService}")
private SomeSpringService someSpringService;
// getter and setter for managed-property
public void setSomeSpringService(SomeSpringService s){
this.someSpringService = s;
}
public SomeSpringService getSomeSpringService(){
return this.someSpringService;
}
}
There may be better approachs than this, but this is what I've been using lately.

How to inject Spring bean into JSF converter [duplicate]

This question already has answers here:
How to inject #EJB, #PersistenceContext, #Inject, #Autowired, etc in #FacesConverter?
(5 answers)
Closed 7 years ago.
I need to inject Spring beans into a JSF (Primefaces) converter. I tried to inject beans by using EL resolver. However, the beans are null inside the converters.
My JSF converter:
public class DepartmentConverter implements Converter {
private DepartmentService departmentService;
//getter setter for this property
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
//codes
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
//Codes
}
}
faces-config.xml:
<converter>
<converter-id>DepartmentConverter</converter-id>
<converter-class>com.studinfo.jsf.converter.DepartmentConverter</converter-class>
<property>
<property-name>departmentService</property-name>
<property-class>com.studinfo.services.DepartmentService</property-class>
<default-value>#{DepartmentService}</default-value>
</property>
</converter>
EL resolver:
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
When I debug my code, the departmentService property is null. I can access the Spring beans inside a managed JSF bean the same way.
Until JSF 2.3, converters are no injection targets. Make the converter a JSF or Spring managed bean instead. The below example makes it a JSF managed bean:
#ManagedBean
#RequestScoped
public class DepartmentConverter implements Converter {
// ...
}
And use it as #{departmentConverter} instead of DepartmentConverter.
E.g.
<h:inputSome ... converter="#{departmentConverter}" />
or
<h:someComponent>
<f:converter binding="#{departmentConverter}" />
</h:someComponent>
Don't forget to remove the <converter> from faces-config.xml (which was at its own already unnecessary if you used the #FacesConverter annotation, but that aside).

Injecting JSF bean into Spring bean - impossible?

I have a JSF 2.0 bean:
#ManagedBean
#SessionScoped
public class LoginBean implements Serializable
{
protected String name;
public String getName()
{
return name;
}
//....
}
I have a Spring 3.0 bean:
#Repository
public class Logins
{
#ManagedProperty(value="#{loginBean}")
protected LoginBean loginBean;
public void recordLogin()
{
//... record in db that loginBean.getName() just logged in
}
}
This code doesn't work, Logins.loginBean is never set.
Alternatively (its the same question, simplified) - would the following code ever work?
#Repository
public class SpringBean
{
#ManagedProperty(value="#{session.id}")
protected String id;
//....
}
The ContextLoaderListener and RequestLoaderListener are declared in web.xml.
Is it possible at all to inject a JSF bean into a Spring bean? (Without using yet another extra framework)
Or should I rather convert my JSF bean into a Spring bean and use the DelegatingVariableResolver trick in faces-config.xml? I have already tested this with a test Spring bean, and it works.
Using JSF annotations in spring-managed beans doesn't work. And it shouldn't - you should not inject things from the web layer in the other layers. It should be the other way around - inject spring services (or repositories) into web components (jsf managed beans), and invoke methods on them, passing the managed bean properties as arguments

Resources