Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Validator class not working

Tags:

java

jsf-2

cdi

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.

like image 482
Himanshu Bhardwaj Avatar asked Dec 27 '22 04:12

Himanshu Bhardwaj


1 Answers

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.

like image 182
BalusC Avatar answered Jan 11 '23 13:01

BalusC