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?
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)
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With