33

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?

Roland
  • 160
  • 1
  • 14
Mahmoud Saleh
  • 31,861
  • 113
  • 313
  • 484

2 Answers2

67

Before JSF 2.3, the @FacesValidator isn't managed by the injection container. You 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 JSF's @ManagedBean:

@ManagedBean
@RequestScoped
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}" />

or

<f:validator binding="#{emailExistValidator}" />

instead of

<h:inputText ... validator="emailExistValidator" />

or

<f:validator validatorId="emailExistValidator" />

This is indeed awkward. The JSF guys have confirmed this embarrassing oversight and they have made the @FacesValidator (and @FacesConverter) an eligible injection target in since JSF 2.3, see also JSF spec issue 763. 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 the CDI extension MyFaces CODI, then you can also solve it by putting @Advanced annotation on the class.

See also:


If you happen to use JSF utility library OmniFaces, since version 1.6 is 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.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • what do you think about using @Component("emailExistValidator") @Scope("request") instead to make spring takes control since i forgot to mention that my project is spring jsf ? – Mahmoud Saleh Sep 27 '11 at 16:28
  • and i have two question though, why to define that the scope is request ? and what is difference between binding and validatorId ? – Mahmoud Saleh Sep 27 '11 at 16:29
  • 2
    1) Sorry, I don't do Spring. I do EJB and know something about CDI. But I really can't go in Spring details. 2) Because you don't want to share the DAO state among different requests. 3) The `binding` expects a concrete instance anywhere in the EL scope (which is been prepared by `@ManagedBean` or `@Named`). The `validatorId` expects a `@FacesValidator` id (but since we removed it, it don't exist anymore). – BalusC Sep 27 '11 at 16:34
  • thanks for detailed answer, but i am still missing the part of the use of @RequestScoped, i need an example please. – Mahmoud Saleh Sep 27 '11 at 16:49
  • 1
    If your DAO class holds a state and you make it session or application scoped, then it is going to be shared among multiple requests or users. This may result in undesireable results/behaviour (i.e. not threadsafe). If your DAO class does not hold any state (also not from the persistence context for the case you're using JPA!), then you could safely make it session or application scoped. – BalusC Sep 27 '11 at 16:54
  • 1
    what do you mean by holds a state, small example please ? – Mahmoud Saleh Sep 27 '11 at 17:25
  • 3
    An instance variable which is sensitive to changes and is been used by one or more of the methods. E.g. `public class FooDAO { private Bar bar; }` here, `bar` is the state of `FooDAO` class. If it is in session or app scope and one request changes it, then it will be reflected back on all other requests/sessions. This may not be desireable. In such case, you should keep it in request scope so that it's not shared by all other requests/sessions. But if it does not hold state and all work is done based on variables inside the very same method block, then you can safely put in session/app scope. – BalusC Sep 27 '11 at 17:36
  • my DAO contains hibernate methods that deals with database (select entity or entities/ add entity/ remove entity) so do you think that it should use the request scope ? – Mahmoud Saleh Sep 27 '11 at 19:21
  • 2
    If the DAO is stateless (i.e. does not have any fields/properties), then you can safely keep it in a wide scope. But if it is stateful (i.e. has any fields/properties which are sensitive for request-based changes), then you'd rather like to keep it in request scope. – BalusC Sep 27 '11 at 19:22
  • As a clarification to your answer and something I discovered after a lot of frustration, your example doesn't work unless the ManagedBean and RequestScoped annotations are imported from the javax.faces.bean package(NOT javax.annotation). – GreenieMeanie Apr 29 '14 at 19:35
  • 1
    It was just a suggestion - no need to be a greenie meanie about it. Just by looking at the example code provided and not clicking/hovering the links, it is not apparent. When using an IDE's "auto-import" features, it could very easily automatically import them from the wrong package (such as in my case), which can be further exacerbated when imports are auto-collapsed. – GreenieMeanie Apr 29 '14 at 20:18
2

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.

DavidS
  • 4,384
  • 2
  • 23
  • 51
  • Funny enough, the Annotation `@FacesConfig(version = FacesConfig.Version.JSF_2_3)` has to be present, even if `version="2.3"` is already set in faces-config.xml. That took some time to figure out. – SilverNak Apr 14 '21 at 06:40