XWork2 have build-in validation. They are done through the following major classes:-

  • ActionVaidatorManagerFactory
  • ActionValidatorManager
  • Validator

ActionValidatorManagerFactory

ActionValidatorManagerFactory serves as a static factory for ActionValidatorManager.

ActionValidatorManager

This class is the main entry point to trigger XWork2 validation. Its default implementation (DefaultActionValidatorManager also takes care of loading the validators defined through xml file (default.xml) located in XWOrk2 jar file)

To trigger validation through code use:-

ActionValidatorManager actionValidatorManager = ....
  actionValidatorManager.validate(myAction, "actionAlias");

Validator

A validator needs to implement the Validator interface. XWork2 provides supporting base class that makes creating a custom validator easier. Typically one would like to extends from either :-

  • ValidatorSupport
  • FieldValidatorSupport

Extending ValidatorSupport is ideal for if its a global level validator while extending from FieldValidatorSupport is for validator that are meant to validate at a field level.

How to Write a Custom Validator

Global validator

public class ExpressionValidator extends ValidatorSupport {

    private String expression;


    public void setExpression(String expression) {
        this.expression = expression;
    }

    public String getExpression() {
        return expression;
    }

    public void validate(Object object) throws ValidationException {
        Boolean answer = Boolean.FALSE;
        Object obj = null;

        try {
            obj = getFieldValue(expression, object);
        } catch (ValidationException e) {
            throw e;
        } catch (Exception e) {
            // let this pass, but it will be logged right below
        }

        if ((obj != null) && (obj instanceof Boolean)) {
            answer = (Boolean) obj;
        } else {
            log.warn("Got result of " + obj + " when trying to get Boolean.");
        }

        if (!answer.booleanValue()) {
            if (log.isDebugEnabled()) log.debug("Validation failed on expression " + expression + " with validated object "+ object);
            addActionError(object);
        }
    }
}

Field validator

public class DoubleRangeFieldValidator extends FieldValidatorSupport {
    
    String maxInclusive = null;
    String minInclusive = null;
    String minExclusive = null;
    String maxExclusive = null;

    Double maxInclusiveValue = null;
    Double minInclusiveValue = null;
    Double minExclusiveValue = null;
    Double maxExclusiveValue = null;

    public void validate(Object object) throws ValidationException {
        String fieldName = getFieldName();
        Double value;
        try {
            Object obj = this.getFieldValue(fieldName, object);
            if (obj == null) {
                return;
            }
            value = Double.valueOf(obj.toString());
        } catch (NumberFormatException e) {
            return;
        }

        parseParameterValues();
        if ((maxInclusiveValue != null && value.compareTo(maxInclusiveValue) > 0) ||
                (minInclusiveValue != null && value.compareTo(minInclusiveValue) < 0) ||
                (maxExclusiveValue != null && value.compareTo(maxExclusiveValue) >= 0) ||
                (minExclusiveValue != null && value.compareTo(minExclusiveValue) <= 0)) {
            addFieldError(fieldName, object);
        }
    }

    private void parseParameterValues() {
        this.minInclusiveValue = parseDouble(minInclusive);
        this.maxInclusiveValue = parseDouble(maxInclusive);
        this.minExclusiveValue = parseDouble(minExclusive);
        this.maxExclusiveValue = parseDouble(maxExclusive);
    }

    private Double parseDouble (String value) {
        if (value != null) {
            try {
                return Double.valueOf(value);
            } catch (NumberFormatException e) {
                if (log.isWarnEnabled()) {
                    log.warn("DoubleRangeFieldValidator - [parseDouble]: Unable to parse given double parameter " + value);
                }
            }
        }
        return null;
    }

    public void setMaxInclusive(String maxInclusive) {
        this.maxInclusive = maxInclusive;
    }

    public String getMaxInclusive() {
        return maxInclusive;
    }

    public void setMinInclusive(String minInclusive) {
        this.minInclusive = minInclusive;
    }

    public String getMinInclusive() {
        return minInclusive;
    }

    public String getMinExclusive() {
        return minExclusive;
    }

    public void setMinExclusive(String minExclusive) {
        this.minExclusive = minExclusive;
    }

    public String getMaxExclusive() {
        return maxExclusive;
    }

    public void setMaxExclusive(String maxExclusive) {
        this.maxExclusive = maxExclusive;
    }
}

Default Validators

<validators>
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
    <validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
    <validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
    <validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
    <validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
    <validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
    <validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
    <validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
    <validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
    <validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
    <validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
    <validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
    <validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
    <validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
    <validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>
</validators>

Integrate validation into Action

Action-level validation

To create an action level validation, create a file 'ActionClass-validation.xml' at the same location where the Action class itself lies. Example, if the action class is MyAction, the xml file would be named 'MyAction-validation.xml'

<action name="myAlias" class="foo.bar.MyAction">
     ....
   </action>
   <action name="myAnotherAlias" class="foo.bar.MyAction" method="create">
    .....
   </action>

An action-level validation allows the validation to be applied to all 'MyAction' action class. In the example above, both action 'myAlias' and 'myAnotherAlias' will have the validation applied.

Action Alias-level validation

To create an action alias level validation, create a file 'ActionClass-actionAlias-validation.xml' at the same location where the Action class itself lies. Example if the action class is MyAction with an alias 'myAlias', the xml file would be named 'MyAction-myAlias-validation.xml'.

<action name="myAlias" class="foo.bar.MyAction">
    ...
  </action>
  <action name="myAnotherAlias" class="foo.bar.MyAction" method="create">
    .....
   </action>

An action-alias-level validation allows the validation to be applied to all 'MyAction' action class with alias 'myAlias' only, hence limiting the scope where the validation is applied compared to global-level validation.

In the example above, action 'myAlias' will have the validation applied whereas action 'myAnotherAlias' will not.

Validation configuration

Following is an example how a validation xml configuration file looks like

<validators>
	<validator type="expression">
		<param name="expression"><![CDATA[name != null && age != null]]></param>
		<message>Both fields are required</message>
	</validator>
	<field name="name">
		<field-validator type="requiredstring">
			<message>Name is mandatory</message>
		</field-validator>
	</field>
	<field name="age">
		<field-validator type="requiredstring">
			<message>Age is mandatory</message>
		</field-validator>
		<field-validator type="int">
			<param name="min">20</param>
			<param name="max">50</param>
			<message>Age must be between 20 and 50</message>
		</field-validator>
	</field>
</validators>