Apache Struts 2 Documentation > Validation Examples
Added by jcarreira, last edited by Ted Husted on Feb 12, 2007  (view change)

Included in the Home example war file is an example of using the [XW:Validation Framework] in WebWork2. This example consists of links which all use the same Action Class and view pages (Velocity, Freemarker and Jsp).

The sources

First, I had to add the validators.xml file to the root of the source tree for the example app

<validators>
    <validator name="required" class="com.opensymphony.xwork.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork.validator.validators.IntRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork.validator.validators.VisitorFieldValidator"/>
    <validator name="regex" class="com.opensymphony.xwork.validator.validators.RegexFieldValidator"/>
</validators>

The Action class used by all of the validation examples is ValidatedAction

package com.opensymphony.webwork.example;

import com.opensymphony.xwork.ActionSupport;

/**
 * ValidatedAction
 * @author Jason Carreira
 * Created Sep 12, 2003 9:23:38 PM
 */
public class ValidatedAction extends ActionSupport {
    private ValidatedBean bean = new ValidatedBean();
    private String name;
    private String validationAction = "basicValidation.action";

    public ValidatedBean getBean() {
        return bean;
    }

    public void setBean(ValidatedBean bean) {
        this.bean = bean;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValidationAction() {
        return validationAction;
    }

    public void setValidationAction(String validationAction) {
        this.validationAction = validationAction;
    }
}

The base validation file for the ValidatedAction is ValidatedAction-validation.xml

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <validator type="expression">
	<param name="expression"><![CDATA[(name!=null && name.startsWith("A"))]]></param>
	<message>Name Must Not Be Null And Starts With A</message>
    </validator>
    <field name="name">
        <field-validator type="requiredstring">
            <message>You must enter a name.</message>
        </field-validator>
    </field>
</validators>

This and all other validation files are placed in the same package as the classes to which they apply.

The form for this Action is validationForm.vm

<html>
    <head><title>Webwork Validation Example</title></head>
    <body>
    #if( $actionErrors.size() > 0 )
      <p>
        <font color="red">
        <b>ERRORS:</b><br>
        <ul>
           #foreach( $error in $actionErrors )
               <li>$error</li>
           #end
        </ul>
        </font>
      </p>
    #end
      <p>
      <form name="myForm" action="${validationAction}" method="POST">
         <input type="hidden" name="validationAction" value="${validationAction}"/>
         Action Properties:
         <br>
         <table>
            #tag( TextField "label=Name" "name=name" "value=name" )
         </table>
            Bean Properties:
            #if( $stack.findValue("fieldErrors") )
               #set( $beanErrors = $stack.findValue("fieldErrors.get('bean')") )
               #if( $beanErrors.size() > 0 )
                 <br>
                 <font color="red">
                 <b>Bean Errors:</b><br>
                 <ul>
                 #foreach( $beanError in $beanErrors )
                    <li>$beanError</li>
                 #end
                 </ul>
                 </font>
               #end
            #end
         <table>
            #tag( TextField "label=Bean.Text" "name=bean.text" "value=bean.text" )<br>
            #tag( TextField "label=Bean.Date" "name=bean.date" "value=bean.date" )<br>
            #tag( TextField "label=Bean.Number" "name=bean.number" "value=bean.number" )<br>
            #tag( TextField "label=Bean.Number2" "name=bean.number2" "value=bean.number2" )<br>
         </table>
         <input type="submit" value="Test Validation"/>
      </form>
</body>

The success page for these examples is a very simple page, valid.vm

<html>
   <head><title>WebWork Validation Test: Valid</title></head>
   <body>
      Input was valid!
    </body>
</html>

The form page in Freemarker syntax

<html>
	<head><title>webwork validation</head>
	<body>	
		<#if (actionErrors.size() > 0)>
			<p>
			<font color="red">
			   <b>ERRORS:</b><br/>
			   <ul>
				<#list actionErrors as error>
					<li>${error}</li>
				</#list>
			   </ul>
			</font>
			</p>
		</#if>
		<p>
		<form action="${validationAction}" method="POST">
			<input type="hidden" name="validationAction" value="${validationAction}" />
			<#if (fieldErrors?keys?seq_contains("name"))>
			   <br/>
			   <font color="red">
			      <b>Field Errors:</b><br/>
			      <ul>
			      <#list fieldErrors.name as fieldError>
			         <li>${fieldError}</li>
			      </#list>
			      </ul>
			   </font>
			</#if>
			Action Properties:
			<br/>
			<table>
				<tr><td>
					Name:<input type="text" name="name" />
				</td></tr>
			</table>
			Bean Properties:
			<#if ((fieldErrors?keys?seq_contains("bean.text")) || 
			     (fieldErrors?keys?seq_contains("bean.date")) ||
			     (fieldErrors?keys?seq_contains("bean.number")))> 
				<br/>
				<font color="red">
					<b>Bean Errors:</b><br/>
					<ul>
					<#if fieldErrors?keys?seq_contains("bean.text")>
				   	<#list fieldErrors["bean.text"] as beanTextError>
				   		<li>${beanTextError}</li>
				   	</#list>
				   	</#if>
				   	
				   	<#if fieldErrors?keys?seq_contains("bean.date")>
				   	<#list fieldErrors["bean.date"] as beanDateError>
				   	    <li>${beanDateError}</li>
				   	</#list>
				   	</#if>
				   	
				   	<#if fieldErrors?keys?seq_contains("bean.number")>
				   	<#list fieldErrors["bean.number"] as beanNumberError>
				   		<li>${beanNumberError}</li>
				   	</#list>
				   	</#if>
					</ul>
				</font>
			</#if>
			<table>
			   <tr><td>
			   		Bean Text:<input type="input" name="bean.text" />
			   </td></tr>
			   <tr><td>
			   		Bean Date:<input type="input" name="bean.date" />
			   </td></tr>
			   <tr><td>
			   		Bean Number:<input type="input" name="bean.number" />
			   </td></tr>
			   <tr><td>
			   		Bean Number2:<input type="input" name="bean.number2" />
			   </td></tr>
			</table>
			<input type="submit" />
		</form>
		</p>
	</body>
</html>

The success page in Freemarker syntax

<html>
	<head><title>webwork validation</head>
	<body>	
		Input was valid!
	</body>
</html>

The form page in Jsp syntax

<%@taglib prefix="ww" uri="/WEB-INF/webwork.tld" %>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Webwork Validation</title>
</head>
<body>

<ww:if test="%{actionErrors.size > 0}">
	<p>
		<font color="red">
		   <b>ERRORS:</b><br/>
		   <ul>
		   	<ww:iterator value="%{actionErrors}">
		   		<li><ww:property /></li>
		   	</ww:iterator>
		   </ul>
		</font>
	</p>
</ww:if>


<p>
	<form action='<ww:property value="%{validationAction}"/>' method="POST">
		<input type="hidden" name="validationAction" value='<ww:property value="%{validationAction}" />' />
		<ww:if test="%{fieldErrors.get('name')!=null && fieldErrors.get('name').size() > 0}">
			<br/>
			<font color="red">
				<b>Field Errors:</b><br/>
				<ul>
					<ww:iterator value="%{fieldErrors.get('name')}">
						<li><ww:property /></li>
					</ww:iterator>
				</ul>
			</font>
		</ww:if>
		Action Properties:
		<br/>
		<table>
			<tr><td>
				Name:<input type="text" name="name" />
			</td></tr>
		</table>
		Bean Properties:	
		<ww:if test="%{ (fieldErrors.get('bean.text') != null && fieldErrors.get('bean.text').size() > 0) ||
						(fieldErrors.get('bean.date') != null && fieldErrors.get('bean.date').size() > 0) ||
						(fieldErrors.get('bean.number') != null && fieldErrors.get('bean.number').size() > 0)
					 }">
			<br/>
			<font color="red">
				<b>Bean Errors:</b><br/>
				<ul>
					<ww:if test="%{fieldErrors.get('bean.text') != null && fieldErrors.get('bean.text').size() > 0}">
						<ww:iterator value="%{fieldErrors.get('bean.text')}">
							<li><ww:property /></li>
						</ww:iterator>
					</ww:if>
					<ww:if test="%{fieldErrors.get('bean.date') != null && fieldErrors.get('bean.date').size() > 0}">
						<ww:iterator value="%{fieldErrors.get('bean.date')}">
							<li><ww:property /></li>
						</ww:iterator>
					</ww:if>
					<ww:if test="%{fieldErrors.get('bean.number') != null && fieldErrors.get('bean.number').size() > 0}">
						<ww:iterator value="%{fieldErrors.get('bean.number')}">
							<li><ww:property /></li>
						</ww:iterator>
					</ww:if>
				</ul>
			</font>
		</ww:if>
		<table>
			<tr><td>
			 		Bean Text:<input type="input" name="bean.text" />
			</td></tr>
			<tr><td>
			   		Bean Date:<input type="input" name="bean.date" />
			</td></tr>
			<tr><td>
			   		Bean Number:<input type="input" name="bean.number" />
			</td></tr>
			<tr><td>
			   		Bean Number2:<input type="input" name="bean.number2" />
			</td></tr>
			</table>
			<input type="submit" />
		</table>
    </form>
</p>

</body>
</html>

The success page in Jsp Syntax

<%@taglib prefix="ww" uri="/WEB-INF/webwork.tld" %>

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Webwork Validation</title>
</head>
<body>
   Input was valid!
</body>
</html>

We'll look at any other example-specific configuration files as we get to them.

Basic Validation

The BasicValidation example is defined in the example xwork.xml file like this (for Velocity)

<action name="basicValidation" class="com.opensymphony.webwork.example.ValidatedAction">
            <interceptor-ref name="validationWorkflowStack"/>

            <result name="success" type="velocity">valid.vm</result>
            <result name="input" type="velocity">validationForm.vm</result>
            <result name="error" type="velocity">validationForm.vm</result>
        </action>

The xwork.xml file if freemarker is used

<action name="basicValidation" class="com.opensymphony.webwork.example.ValidatedAction">
            <interceptor-ref name="validationWorkflowStack"/>

            <result name="success" type="freemarker">valid.ftl</result>
            <result name="input" type="freemarker">validationForm.ftl</result>
            <result name="error" type="freemarker">validationForm.ftl</result>
        </action>

The xwork.xml file if jsp is used

<action name="basicValidation" class="com.opensymphony.webwork.example.ValidatedAction">
            <interceptor-ref name="validationWorkflowStack"/>

            <result name="success" type="dispatcher">valid.jsp</result>
            <result name="input" type="dispatcher">validationForm.jsp</result>
            <result name="error" type="dispatcher">validationForm.jsp</result>
        </action>

The interceptor-ref here, to "validationWorkflowStack", is defined in webwork-default.xml and provides the parameter interceptors as well as the ValidationInterceptor (see [XW:Validation Framework] and the DefaultWorkFlowInterceptor (see [XW:Interceptors#DefaultWorkflow]). All of the parameters from the configuration file (there are none in this case) followed by the parameters from the request will be set onto the Action. Next, the validations will be run, and finally the DefaultWorkflow will be applied (see [XW:Interceptors#DefaultWorkflow]).

This example is very simple, and the ValidatedAction-validation.xml file is the only set of Validations which will be applied. This means that the only validation done is that you enter some text for the name field.

Visitor Validation Example

note: check out another Visitor Field Validator Example .

The ValidatedAction holds a reference to a plain Java bean, ValidatedBean:

package com.opensymphony.webwork.example;

import java.util.Date;

/**
 * ValidatedBean
 * @author Jason Carreira
 * Created Sep 12, 2003 9:24:18 PM
 */
public class ValidatedBean {
    private String text;
    private Date date = new Date(System.currentTimeMillis());
    private int number;
    private int number2;
    public static final int MAX_TOTAL = 12;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public int getNumber2() {
        return number2;
    }

    public void setNumber2(int number2) {
        this.number2 = number2;
    }
}

The base validation file for the ValidatedBean is ValidatedBean-validation.xml

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <field name="text">
        <field-validator type="requiredstring">
            <message key="invalid.text">Empty Text!</message>
        </field-validator>
    </field>
    <field name="date">
        <field-validator type="date">
            <param name="min">01/01/1970</param>
            <message key="invalid.date">Invalid Date!</message>
        </field-validator>
    </field>
    <field name="number">
        <field-validator type="int">
            <param name="min">1</param>
            <param name="max">10</param>
            <message key="invalid.number">Invalid Number!</message>
        </field-validator>
    </field>
</validators>

In the Visitor Validation Example, we add a VisitorFieldValidator to apply these validations to our ValidatedBean. The Action is defined in our xwork.xml file like this:

<action name="visitorValidation" class="com.opensymphony.webwork.example.ValidatedAction">
            <interceptor-ref name="validationWorkflowStack"/>
            <param name="validationAction">visitorValidation.action</param>
            <result name="success">valid.vm</result>
            <result name="input">validationForm.vm</result>
            <result name="error">validationForm.vm</result>
        </action>

Here we see a slight difference from the basic validation example above. I've added a static param to the Action which will be applied to the Action by the static-param interceptor. This parameter only sets the value for the action to post the form to for validation (see validationForm.vm).

The Action name above, visitorValidation is mapped to another set of validations defined in the file ValidatedAction-visitorValidation-validation.xml

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <field name="bean">
        <field-validator type="required">
            <message>The bean must not be null.</message>
        </field-validator>
        <field-validator type="visitor">
            <message>bean: </message>
        </field-validator>
    </field>
</validators>

This file is found automatically by the validation framework based on the class name (ValidatedAction), the action alias, which is used as the validation context (visitorValidation) and the standard suffix (-validation.xml) to form the filename ValidatedAction-visitorValidation-validation.xml.

This file defines two validators for the "bean" field, a required validator which makes sure the bean is not null, and a VisitorFieldValidator. The VisitorFieldValidator will apply the validators for the ValidatedBean using the same validation context as is used in validating ValidatedAction, visitorValidation. It therefore looks for the validation files ValidatedBean-validation.xml (the default validations for the ValidatedBean) and ValidatedBean-visitorValidation-validation.xml (the validations specific to this validation context) , in that order.

The ValidatedBean-validation.xml looks like this:

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <field name="text">
        <field-validator type="requiredstring">
            <message key="invalid.text">Empty Text!</message>
        </field-validator>
    </field>
    <field name="date">
        <field-validator type="date">
            <param name="min">01/01/1970</param>
            <message key="invalid.date">Invalid Date!</message>
        </field-validator>
    </field>
    <field name="number">
        <field-validator type="int">
            <param name="min">1</param>
            <param name="max">10</param>
            <message key="invalid.number">Invalid Number!</message>
        </field-validator>
    </field>
</validators>

This file applies validations for three fields (text, date, and number) and gives message keys and default messages for each of them. These message keys will be used to look up messages from a resource bundle specific to the ValidatedBean class. In the same package as the ValidatedBean is a file named ValidatedBean.properties

invalid.date=You must enter a date after ${min}.
invalid.text=You must enter some text.
invalid.number=You must enter a number between ${min} and ${max}.
invalid.total=The total of number and number2 must be less than $\{@com.opensymphony.webwork.example.ValidatedBean@MAX_TOTAL}.

These messages will be used for any errors added for the ValidatedBean using a message key. As you can see from the body of the messages, they can be parameterized with properties from the Bean, the Interceptor, and the Action (and they will be searched in that order). There is also an example of using a Static field ${@com.opensymphony.webwork.example.ValidatedBean@MAX_TOTAL}.

The ValidatedBean-visitorValidation-validation.xml file would define validations specific for the visitorValidation validation context, but it is not there, so it is ignored.

Visitor Validation with the Expression Validator

The final example shows a similar setup to the previous visitor validation example. The xwork.xml configuration for this example is very similar to the visitorValidation example:

<action name="expressionValidation" class="com.opensymphony.webwork.example.ValidatedAction">
            <interceptor-ref name="validationWorkflowStack"/>
            <param name="validationAction">expressionValidation.action</param>
            <result name="success">valid.vm</result>
            <result name="input">validationForm.vm</result>
            <result name="error">validationForm.vm</result>
        </action>

The ValidatedAction-expressionValidation-validation.xml file defines the validations specific to this validation context:

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <field name="bean">
        <field-validator type="required">
            <message>The bean must not be null.</message>
        </field-validator>
        <field-validator type="visitor">
            <param name="context">expression</param>
            <message>bean: </message>
        </field-validator>
    </field>
</validators>

This is almost identical to the ValidatedAction-visitorValidation-validation.xml file, but shows an example of passing a context param to the VisitorFieldValidator. In this case, rather than using the same validation context as is used for the ValidatedAction (expressionValidation), it passes another context (expression) to be used instead.

In this case, the validation context specific validations for the ValidatedBean is present, and it's named ValidatedBean-expression-validation.xml

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
<validators>
    <validator type="expression">
        <param name="expression">@com.opensymphony.webwork.example.ValidatedBean@MAX_TOTAL > (number + number2)</param>
        <message key="invalid.total">Invalid total!</message>
    </validator>
</validators>

This adds an object-level (as opposed to field-level) ExpressionValidator which checks the total of the number and number2 fields against a static constant, and adds an error message if the total is more than the constant.

A note about error messages with the VisitorFieldValidator

With the VisitorFieldValidator, message field names are appended with the field name of the field in the Action. In this case, the fields "text", "date", and "number" in the ValidatedBean would be added as field error messages to the Action with field names "bean.text", "bean.date", and "bean.number". The error messages added for the object-level ExpressionValidator applied in the last example will be added as field-level errors to the Action with the name "bean".