Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired

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?

like image 361
Mahmoud Saleh Avatar asked Sep 27 '11 16:09

Mahmoud Saleh


2 Answers

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

like image 117
BalusC Avatar answered Oct 12 '22 09:10

BalusC


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:

enter image description here

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.

like image 37
DavidS Avatar answered Oct 12 '22 10:10

DavidS