Showing posts with label empty. Show all posts
Showing posts with label empty. Show all posts

Friday, December 25, 2009

Validating empty text field using JSF

JSF has a pretty comprehensive support for validations, but it is lack of a validation for an empty field. The built-in “required” property of JSF is not so usable, since it validates empty fields only. If a field has a space in it, JSF will accept it. In most scenarios, when inputting text in a form, a space (or several spaces) is considered to be an empty field. Since JSF doesn’t support “out of the box” validation for empty field, we will write our own Validator that will do the job.

Writing a JSF Validator mainly involves 3 things:

  1. Writing the Validator code by implementing a JSF Validator interface.
  2. Registering Validator in JSF faces-config.xml.
  3. Using the Validator in a JSF page.

In order to write the Validator we have to implement “validate” method of Validator interface. This method will be called automatically by JSF, when we use the Validator in some of our input fields. Our Validator class simply checks that the field value is not empty. If the field is empty a ValidationException is thrown from the Validator. JSF mechanism knows to treat this exception as a validation error, when it checks the form inputs for validation errors. Let’s have a look at the Validator code:

package com.bashan.blog.jsf.validator;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
/**
 * @author Bashan
 */
public class RequiredValidator implements Validator {
  public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
    if (value == null || "".equals(value.toString().trim())) {
      FacesMessage message = new FacesMessage();
      String messageStr = (String)component.getAttributes().get("message");
      if (messageStr == null) {
        messageStr = "Please enter data";
      }
      message.setDetail(messageStr);
      message.setSummary(messageStr);
      message.setSeverity(FacesMessage.SEVERITY_ERROR);
      throw new ValidatorException(message);
    }
  }
}

Note, that the Validator tries to get an attribute named “message” from the component for which it is attached. The “message” attribute should contain a custom error message to show to the user. If “message” attribute is not used, a default error message: “Please enter data” is shown to the user.

Now, let’s register the Validator in JSF faces-config.xml file. This file should be located under the “WEB-INF” directory by default:

<?xml version="1.0" encoding="windows-1255"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config> 
  <validator>
    <validator-id>RequiredValidator</validator-id>
    <validator-class>com.bashan.blog.jsf.validator.RequiredValidator</validator-class>
  </validator>
</faces-config>

You can see in this “faces-config.xml” file the validator id, which is used to call the validator from a JSF page, and the JSF class corresponds to this validator id.

Finally, let’s have a look how this validator is used in a JSF page:

<h:inputTextarea id="someText" value="#{support.message}" styleClass="textarea1" required="true" requiredMessage="Please write something">
  <f:attribute name="message" value="Please write something" />
  <f:validator validatorId="RequiredValidator" />             
</h:inputTextarea>
<div>
  <h:message for="someText" styleClass="error"/>
</div>

This is only a fragment of a JSF page, showing a text area control and a message under it. Note, that the control can be any JSF input like <h:inputText />. Also note for the <f:attribue /> control used for sending a custom message to the Validator. Another important thing worth mentioning, it that the “required” attribute of the <h:inputTextArea /> control is also used with the same error message (using the “requiredMessage” property). One can say, using 2 validations is redundant, and that only the “RequiredValidator” could have been used. This was true, unless JSF mechanism had a problematic issue with empty fields: When field is empty (has no value and no spaces) validators and converters are not invoked. For this reason, both “required” and “RequiredValidtor” are needed to be used.

Monday, May 18, 2009

JSF input - empty value converted to Zero

When running a JSF application under Tomcat, you might encounter a weird case in which an empty value of a field bidden with numeric property (like Integer) is converted to zero instead of remaining "null".

Consider the following case, in which text field is used to input Integer value:

<h:inputText id="myInput" value="#{myBean.myInteger}" converter="IntegerConverter" />

And the bean holding the value:

package com.bashan.blog.jsf;
public class MyBean {
  private Integer myInteger;
  public Integer getMyInteger() {
    return myInteger;
  }
  public void setMyInteger(Integer myInteger) {
    this.myInteger = myInteger;
  }
}
Apparently, Tomcat is coercing “null” values to zeroes. This behavior can be disabled by setting the following JVM parameter to "false":
-Dorg.apache.el.parser.COERCE_TO_ZERO=false

By default the “true” value is used. By setting this value to “false” Tomcat will no longer try to coerce value expressions.

You can also set this parameter in Java code. This may be a better idea, if you don’t want to remember to set this parameter on each new environment you deploy your web application. Here is how it can be done:

System.getProperties().put("org.apache.el.parser.COERCE_TO_ZERO", "false");

You can run this code when your server first loads in some initialization Servlet.

If I recall correct, this behavior was changed sometime along the development of Tomcat. That means you can experience different behaviors on different environments of Tomcat. For example, Production environment behaves differently from Development environment, since there are 2 distinct versions of Tomcat. This may cause a bit confusion, when trying to identify the problem.