4

I have a question here:

Scenario: I have a JSF-2, Spring (Beans wiring) Application. I have written a custom validator, which I want to execute.

@FacesValidator("com.test.vali")
@Named("com.test.vali")
public class TestValidator implements Validator {

    @Override
    public void validate(FacesContext arg0, UIComponent arg1, Object arg2) throws ValidatorException {
        System.out.println("dhkdfkbhdfbkdfksdfdfk");

    }

}

I was trying to inject the validator using the following ways:

Way#1:

  <h:inputText value="#{helloWorld.name}">
      <f:validator binding="#{com.test.vali}" />
  </h:inputText>

Output

When tried to render the page, it threw an exception.

javax.servlet.ServletException: /testRichFaces.xhtml @17,48 <f:validator> A validator id was not specified. Typically the validator id is set in the constructor ValidateHandler(ValidatorConfig)

Searched a lot on this, and verified few ways like:

  1. Java file is in a package.

Way#2

 <f:validator validatorId="com.test.vali" />

Output

javax.servlet.ServletException: Expression Error: Named Object: com.test.vali not found.

So from way#1 and way#2, I could interpret that none of the annotations were working for me.

Then, I tried to move to the last approach:

Way#3: Adding validator in faces-config.xml, just to show I am using 2.0 compliance:

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">

And validator config is:

 <validator>
       <validator-id>com.test.vali</validator-id>
       <validator-class>teet.TestValidator</validator-class>
 </validator>

Output

Works

Now the question arises, even using JSF-2.0, I had to resort to faces-config.xml.

What might be the mistake am doing?

Let know if any more configurations are required.

Himanshu Bhardwaj
  • 4,038
  • 3
  • 17
  • 36
  • The @Named annotation is not necessary. How are you managing your backing bean. If they are annotated with Spring annotations then you have to have org.springframework.web.jsf.el.SpringBeanFacesELResolver configured in your faces-config.xml. – Ravi Kadaboina Apr 25 '13 at 13:19
  • Also see http://stackoverflow.com/questions/12470512/scoperequest-not-working – Ravi Kadaboina Apr 25 '13 at 13:20

1 Answers1

10

First of all, you're basically mixing 2 ways of registering a validator instance which work completely independently from each other: registering as faces validator via @FacesValidator, or registering as CDI managed bean via @Named. Those annotations do not know each other, nor do they take each other into account. You basically end up with two completely distinct instances which do not share each others data. To avoid future confusion, it's therefore strongly recommended to remove one of those annotations so that you can guarantee that you always use the one and the same instance.

As to why way 1 failed:

@Named("com.test.vali")
public class TestValidator implements Validator {
    // ...
}
<f:validator binding="#{com.test.vali}" />

This is because the period . is a special operator in EL representing a bean property access or bean method call. Using #{com.test.vali} would only look for a bean #{com} and then its test property and then in turn its vali property. In other words, it's basically trying to get the validator via com.getTest().getVali() where com is a CDI managed bean like so @Named("com").

This is not what you intented. Get rid of those periods in the name. Better, just stick to the default instance name, testValidator. It's the most sane choice, for sure if you give your classes sensible names.

@Named
public class TestValidator implements Validator {
    // ...
}
<f:validator binding="#{testValidator}" />

As to why way 2 failed:

@FacesValidator("com.test.vali")
public class TestValidator implements Validator {
    // ...
}
<f:validator validatorId="com.test.vali" />

That may happen when the @FacesValidator isn't been properly picked up during startup. That may in turn happen when the class in question is not inside the WAR, but instead inside e.g. EAR or EJB. In this case, you'd need to explicitly register the validator in faces-config.xml. But better is to put the class in the WAR, you should namely absolutely not have any JSF artifacts in the EAR or EJB part of the project. It would tight-couple the model/service logic (JPA, EJB, etc) to the view (JSF) and make them not reusable anymore for other views (front-ends) such as JAX-RS, Spring MVC, Struts2, etc.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • +1 for catching the two annotations and "." operator fact in EL expressions. Didn't strike my mind at all about "." – Himanshu Bhardwaj Apr 25 '13 at 16:31
  • I removed the FacesValidator and stayed with @Named to maintain consistency, but one thing I couldn't clear. I made a custom control extending UIInput, and made a validator for the same. Using , I tried to register the validator. But while testing I wasn't able to execute it. On debugging, i could see the validator with the custom component. But for some reason it was not executing. Then I override the validate method of the component, to get the validators and execute the validate method of each validator binded. Is it right way? – Himanshu Bhardwaj Apr 25 '13 at 16:36
  • Validators are by default or by configuration not invoked when the value is empty. For that, either the `required` attribute should be used, or the `javax.faces.VALIDATE_EMPTY_FIELDS` context param should be set with a value of `true`. – BalusC Apr 25 '13 at 17:40
  • @BalusC I tried your CDI managed validator but it only worked when I used f:validateBean instead of f:validator. – Ced Sep 13 '15 at 21:58