Skip to end of metadata
Go to start of metadata

This document describes how valiation works in Stripes, how you specify validations, and how ActionBeans interact with validation. It includes the following sections:


Validation is conceptually broken into three areas in Stripes:

  • Annotation Driven Validation
  • Type Converstion
  • Custom Validation

Annotation Driven Validation is named thusly because the way the validation rules are specified is through annotations in the ActionBean. These Validations apply to single fields in the form/ActionBean, and encapsulate common and often used validations.

Type Conversion is the process of converting a single form field input from the String value in the HttpServletRequest into a Java object (of any Class, including String). Type conversion is a core part of the validation process, and is easy to extend to new or custom types.

Custom Validation is the name given to the validation performed by arbitrary Java code in one or more validation methods in an ActionBean. An ActionBean can define zero, one or many validation methods and apply them in different scenarios.

Validation Processing Flow

Given the different kinds of validation on offer, one might ask how they are applied, and what happens when errors are produced at any stage. The following is the general flow:

  1. Required field validation is run first
  2. For fields that have no errors after required field validation
    1. Perform minimum/maximum length and mask validations
    2. If the field still has no errors, convert to the target type and bind the property to the ActionBean
    3. If the field converted without errors, run any min/max value checks (for numbers)
  3. For fields that have no errors, evaluate expression based validations
  4. If there are still no errors, run custom validation (unless overridden to run with errors)

This strategy is designed to catch the largest possible number of validation errors possible in a single pass. Thus if the user forgets to enter one value, and mis-types another, they will be presented with both errors the first time they submit the form - but they will not be presented with any additional (e.g. minimum length violation) errors for the field that was required but not supplied. Lastly it should be noted that the reason custom validation is run only when other validations succeed is so that it can rely on the ActionBean being in a well defined. This is, however, configurable (see the Configuration Reference).

As we will see in the sections Selectively Disabling Validation and Changing Validation Outcomes this is not the whole story, but it's good enough for now.

Validation Error Classes

Before we go much further, a quick tour of the different validation error classes is in order. We'll be seeing a lot about validation errors in the next few sections, so it is worthwhile to understand the different types available, and how they are used.

ValidationError is an interface that specifies the methods that all validation error types must support. This makes it easy to have multiple different error classes that offer different functionality. It also means that you can write your own error classes easily if you are not satisfied with the ones that Stripes supplies (hopefully that won't be the case).

SimpleError is, as it says, a simple implementation or ValidationError. It allows a hard-coded message (or one supplied at construction time) to be used, and provides the ability to insert the name and value of the field (and other arbitrary bits of information) into the message.

LocalizableError extends SimpleError with the capability to look up the error message in an external resource bundle. The name of the bundle can be configured - to do this take a look at the Configuration Reference. Looks twice, once with the key prefixed with the action path of the ActionBean being used, and once with the exact key provided.

ScopedLocalizableError extends LocalizableError and provides the ability to perform "scoped searches" for an error message. In reality this just means looking for error messages in the resource bundle with names corresponding to:

  • <actionPath>.<fieldName>.<errorName>
  • <actionPath>.<fieldName>.errorMessage
  • <fieldName>.<errorName>
  • <fieldName>.errorMessage
  • <actionPath>.<errorName>
  • <defaultScope>.<errorName>

In the descriptions below you will see reference to "default scope" and "error name", which refer to how a ScopedLocalizableError is found. All the built in validations in Stripes use ScopedLocalizableError, so that you can specify fairly generic messages at a high level and override them as you see fit.

All of the error messages in Stripes use java.text.MessageFormat which allows you to provide parameters to be merged in to the message. The first two parameters (0 and 1 respectively) are always the field name and field value respectively. An example follows, but check the individual error class javadocs, and the javadoc for MessageFormat for more details:

Example error message specification
myCustomError={0} must be formatted mm/dd/yy. {1} is not a valid date. 

Annotation Driven Validation

There are two annotations for adding validations to fields. The @Validate annotation is used to specify validations for scalar or simple properties within an ActionBean. The @ValidateNestedProperties annotation is used to aggregate one or more @Validate annotations in order to specify validations for nested properties of complex types. The annotations can be attached to either the getter or setter methods on the ActionBean. They can also be attached directly to the properties (even private ones).

Each @Validate can specify multiple validations for a single property. The following example demonstrates how to use the annotations:

Using Validation Annotations
@Validate(required=true, minvalue=13, maxvalue=120) 
private int age; // annotated right on the field 

	@Validate(field="line1", required=true, minlength=5, maxlength=50), 
	@Validate(field="line2", minlength=5, maxlength=50), 
	@Validate(field="zip", required=true, mask="\\d{5}(-\\d{4})?")
public Address getAddress() { ... } 

The following sections walk through the syntax of each validation that can be specified with the @Validate annotation.


The field property is used only when the @Validate annotation is nested within a @ValidateNestedProperties annotation. It is used to specify the property of the complex type to which the validation applies.


Specifies whether or not the annotated field is required when the form is submitted. Defaults to false (so it's never necessary to write required=false even though you can). If a required field is not supplied a single error is generated. The "default scope" is specified as validation.required and the error name is valueNotPresent. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueNotPresent
  • actionPath.fieldName.errorMessage
  • fieldName.valueNotPresent
  • fieldName.errorMessage
  • actionPath.valueNotPresent
  • validation.required.valueNotPresent


The 'on' attribute of the @Validate annotation does not perfom any validation itself, but restricts the application of the required check. To reiterate: it modifies when the required check is applied - not other validations specified at the same time.

By default required field validation is applied to all events not marked with @DontValidate. However, by specifying one or more event names in the 'on' attribute it can be restricted to just those events. For example:

@Validate(required=true, on={"save", "update"}) private String description; 
@Validate(required=true, on="delete") private boolean confirm;

The list can also be inverted by supplying event names prefixed with '!'s, for example:

@Validate(required=true, on="!delete") private String description; 
@Validate(required=true, on="delete") private boolean confirm;

Note: you cannot mix event names with and without '!'s for the same property, e.g. {"!delete", "save"}.


Specifies the minimum length that the pre-conversion String must be. For example you might specify that a password field must have a minimum length of 6. Doing so would require the password to be six characters in length or greater. If a field fails the minimum length test a single validation error is generated. The default scope is validation.minlength and the error name is valueTooShort. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueTooShort
  • actionPath.fieldName.errorMessage
  • fieldName.valueTooShort
  • fieldName.errorMessage
  • actionPath.valueTooShort
  • validation.minlength.valueTooShort

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the minimum length constraint that was violated - e.g. 6 in the example above.


Specifies the maximum length that the pre-conversion String may be. For example you might specify that a username field may have a maximum length of 25. Doing so would require the username to be twenty-five characters in length or less. If a field fails the maximum length test a single validation error is generated. The default scope is validation.maxlength and the error name is valueTooLong. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueTooLong
  • actionPath.fieldName.errorMessage
  • fieldName.valueTooLong
  • fieldName.errorMessage
  • actionPath.valueTooLong
  • validation.maxlength.valueTooLong

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the maximum length constraint that was violated - e.g. 25 in the example above.


Specifies the minimum value for a numeric type (after it has been converted to a number). For example you might specify an age field has a minvalue of 0 to enforce non-negative ages! If a field fails the minimum value test a single validation error is generated. The default scope is validation.minvalue and the error name is valueBelowMinimum. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueBelowMinimum
  • actionPath.fieldName.errorMessage
  • fieldName.valueBelowMinimum
  • fieldName.errorMessage
  • actionPath.valueBelowMinimum
  • validation.minvalue.valueBelowMinimum

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the minimum value constraint that was violated - e.g. 0 in the example above.


Specifies the maximum value for a numeric type (after it has been converted to a number). If a field fails the maximum value test a single validation error is generated. The default scope is validation.maxvalue and the error name is valueAboveMaximum. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueAboveMaximum
  • actionPath.fieldName.errorMessage
  • fieldName.valueAboveMaximum
  • fieldName.errorMessage
  • actionPath.valueAboveMaximum
  • validation.maxvalue.valueAboveMaximum

The error message is supplied with an additional parameter (which can be used as {2} in the message text) which is the maximum value constraint that was violated.


Specifies a regular expression that the user's input must match. The mask is compiled using java.util.regex.Pattern - if you are not familiar with regular expressions, the documentation for the Pattern class provides a good introduction to the syntax. The entire String submitted must match the mask/pattern supplied, as specified by Pattern.matcher(input).matches().

If the input does not match the mask, a single validation error is output with the default scope validation.mask and the error name valueDoesNotMatch. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueDoesNotMatch
  • actionPath.fieldName.errorMessage
  • fieldName.valueDoesNotMatch
  • fieldName.errorMessage
  • actionPath.valueDoesNotMatch
  • validation.mask.valueDoesNotMatch


Specifies an expression using the JSP Expression Language (EL) to be evaluated. Expressions must return a boolean value. Expression validation is run after all form properties (that have passed validation so far) have been bound into the ActionBean. As a result expressions have access to all the values in the ActionBean!

The value being validated is also available (of course) under the name 'this'. Other named values in the expression are resolved first by looking for an ActionBean property of the same name, and if one is not found, then by then a value is looked for in the request scope, and then the session scope. All the usual EL builtins are available, so it is also possible to access the request parameters etc.

Care should be taken because, while expression validation will not be executed on null or empty values, other values referenced by the expression could still be null. Given the EL's penchant for safely handling nulls, and converting types as needed, this may lead to unexpected outcomes!

Some examples are in order:

// Validate that a number is odd 
@Validate(expression="self % 2 == 1") private int oddNumber; 

// Validate that this number is bigger than the odd number 
@Validate(expression="oddNumber != null && self > oddNumber") private long biggerNumber; 

// Validate that a username is the current user's, or the current user is 'root' 
@Validate(expression="'root' ||") private String name; 

If the expression does not evaluate to true an error is created with the default scope validation.expression and the error name valueFailedExpression. This results in an attempt to find error messages in the resource bundle with the following keys:

  • actionPath.fieldName.valueFailedExpression
  • actionPath.fieldName.errorMessage
  • fieldName.valueFailedExpression
  • fieldName.errorMessage
  • actionPath.valueFailedExpression
  • validation.expression.valueFailedExpression

NOTE: The "self" keyword is a substitute for the keyword "this" which indicates the field itself.  The keyword "this" is also supported by Stripes, but some containers may not permit the usage of "this" within EL expressions by default (e.g. Tomcat 7).  In those cases, you would have to check your container's documentation to see if it offers a configuration option to skip identifier checking.  Because of this issue, "self" is the preferred method of referencing the field being validated rather than "this".


Specifies the TypeConverter that should be used to convert this field. This is not usually necessary as in most cases Stripes will use the right converter for the target type. Two common cases where this is useful are:

  1. When you are using formats that modify the meaning of the data. E.g. formatting numbers as percentages causes 0.95 to be written as 95%. Using the default Double/Float type converter in this case would result in the value being multipled by 100 every time the user edited the value. In this case you could tell Stripes to use the PercentageTypeConverter which does the right thing.
  2. When you are converting to custom types, and have decided not to write your own (very simple) TypeConverterFactory. More on this in the next section.

Since this is not strictly a validation, but a way of modifying the type conversion, it does not produce any error messages.

Type Conversion

Type conversion is quite simply the process of converting the String parameters that are supplied in the HttpServletRequest into the types of the corresponding ActionBean properties. The TypeConverter interface specifies how Stripes will interact with classes that perform type conversion. There are type converters in Stripes for most common types in Java, and adding support for additional types is as easy as coding up a new TypeConverter.

Stripes gets to know about TypeConverters in one of two ways. If an @Validate annotation specifes a converter then that converter will be used, no questions asked. Otherwise Stripes will ask the configured TypeConverterFactory to supply the appropriate TypeConverter for the type being converted to. Extending the DefaultTypeConverterFactory is fairly straight-forward. See the Configuration Reference for details on how to configure Stripes to use your custom TypeConverterFactory implementation.

The following sections document the individual type converters used in Stripes.


The BooleanTypeConverter determines that a value is "true" if it matches (ignoring case) any of the values "true", "t", "yes", "y" or "on" or if the value can be parsed as a number and does not equal zero.

Please note that Stripes does not convert and bind parameters submitted in the request with the value "" (empty string). So while you might think that "" would convert to false, in fact conversion is never run, so the value on your ActionBean will be whatever default value is assigned to the property.

Since any String can be converted to a boolean (either false or true), no validation errors are produced.


The DateTypeConverter employs a number of techniques to get the input to parse as a java.util.Date. If the input cannot be parsed into a Date then a single validation error is produced. The default scope is and the error name is invalidDate.


The EmailTypeConverter, strictly speaking is not a type converter. It uses JavaMail to parse the String and return a well formatted email address as a String.

A single validation error is produced if the address cannot be parsed. The default scope is and the error name is invalidEmail.


The EnumeratedTypeConverter converts Strings representing the names of enumerated type values, back into an instance of the enumerated type. If the value supplied does not match any of the values of the target enumerated type, a single validation error is produced. The default scope is converter.enum and the error name is notAnEnumeratedValue.

Number TypeConverters

Stripes supplied a set of converters for converting to the Java numeric types: byte, short, int, long, float, double and their corresponding wrapper types as well as BigDecimal and BigInteger. They all operate similarly (using one or more java.text.NumberFormats). The converters make a best effort to parse a String as a number. They can handle the following without any additional effort:

  • additional white space
  • currency and non-currency numbers
  • use of the minus sign and parentheses to indicate negation
  • use of grouping characters and decimals (obviously)

The number TypeConverters produce two types of error. The first error is common to all the number TypeConverters; it is generated when the String is not parsable as a number. The default scope is converter.number and the error name is invalidNumber.

The second error is named differently for each number TypeConverter, but means the same; it is generated when the supplied number is out of the range for the target type. The default scope is converter.[byte|short|integer|float] and the error name is outOfRange. For all outOfRange errors two additional parameters ({2} and {3}) are supplied to the error message. The parameters are the minimum and maximum allowable values for the type being converted to, e.g. Integer.MIN_VALUE and Integer.MAX_VALUE.


The PercentageTypeConverter converts numbers displayed as percentages (e.g. 95%) into decimal numbers (e.g. 0.95). It requires that the target type is either float, double (or the wrapper types) or BigDecimal. It produces the same error as other number classes, with a default scope of converter.number and the error name invalidNumber.


The OneToManyTypeConverter is a special type converter that converts a single value in the request into a List of values to be bound to the ActionBean. For example, if a user were to enter one or more numbers into a single field, '123, 456 789' that is converted using the OneToManyTypeConverter, a List would be bound to the ActionBean containing three numbers.

The type of the elements in the List is determined based on the declared type in the ActionBean, so if you declare List<Long> you'll get Longs, if you declare List<Boolean> you'll get Booleans. Under the covers the OneToManyTypeConverter will request the appropriate type converter for the individual items, and hence will generate the same validation errors as the LongTypeConverter, BooleanTypeConverter etc.

Custom Validation

Custom validation refers to the execution of arbitrary, custom, validation logic within the ActionBean. ActionBean classes may optionally include one or more validation methods. Validation methods are regular methods that are marked with the annotation @ValidationMethod. These methods are invoked after all other validations have been run and all values have been converted and bound to the ActionBean.

By default validation methods will not be invoked if preceeding validations resulted in errors. Since the ActionBean will be fully populated when validation methods are called it is safe to refer to any property of the ActionBean, or through the ActionBeanContext access values in the HttpServletRequest (though hopefully this will not be common since it makes the ActionBean less testable).

It is possible to configure Stripes to, by default, invoke validation methods regardless of whether or not validation errors exist. To do this refer to the Configuration Reference. Note that by doing this you are no longer guaranteed that all required fields have been supplied or that type conversions and binding succeeded before validation methods are called.

The @ValidationMethod annotation has three attributes that allow the application of the method to be altered, all of which are completely optional. The first is the 'when' attribute which allows the developer to specify whether or not the method should be run when errors exist, regardless of the application default policy. E.g.

@ValidationMethod(when=ValidationState.ALWAYS) public void validateSomething { ... } 
@ValidationMethod(when=ValidationState.NO_ERRORS) public void validateSomethingElse { ... } 

The 'on' attribute (similar to the 'on' attribute of @Validate) restricts the method to run only on certain events. By default validation methods are run without regard to the event being handled. The 'on' attribute can specify a single event, a list of events, or one or more events preceeded by '!'s. In the last case the meaning is inverted, and the method is executed on all events except those listed. E.g.

@ValidationMethod(on="create") public void one() { ... } 
@ValidationMethod(on="update") public void two() { ... } 
@ValidationMethod(on={"update","delete"}) public void three() { ... } 
@ValidationMethod(on="!delete") public void four() { ... } 

The 'priority' attribute is useful only when multiple validation methods exist within an ActionBean and the order in which they are executed is important. Methods with lower (nearer to zero) values for the 'priority' attribute are run before those with higher values.

Lastly, it should be noted that there are two ways to get access to the ValidationErrors object in order to save validation error messages. Firstly, as with any other method in the ActionBean, validation methods can call getContext().getValidationErrors() to retrieve the validation errors. However, they may also take the ValidationErrors objects as a parameter, in which case it will be supplied to the method. E.g.

@ValidationMethod public void validateSomething(ValidationErrors errors) { ... } 

Lastly it should be noted that validation methods must be public, must take either zero parameters or one parameter of type ValidationErrors and may return any type, though returns will be ignored.

Performing Validation in Handler Methods

Sometimes it's just not possible (or reasonable) to validate absolutely everything up front in validation methods. Maybe you don't have all the data you need, maybe you won't know if something really is valid until you try and do it! For example, you might not be able to validate that a withdrawal from a bank account is valid until you do it due to concurrency issues (what if two threads check a $100 balance then allow $75 withdrawals?).

In these situations you can create validation errors in your handler method, and send the user back to the page they came from (or elsewhere if you choose). The following is an example:

public Resolution withdrawFunds() { 
	try { 
		getAccount().withdraw( getAmount() ); 
		return new RedirectResolution("/account/summary.jsp"); 
	catch (InsufficientBalanceException ibe) { 
		ValidationErrors errors = new ValidationErrors(); 
		errors.add( "amount", new LocalizableError("/account/transaction.action.insufficientFunds") ); 
		return getContext().getSourcePageResolution(); 

Selectively Disabling Validation

Often is it desirable to have validation run for one or more events in an ActionBean, but not for others. For example the "save" method might require validation, as might the "add" method. But the "prepopulate" method might not. While usage of the 'on' attribute of @Validate and @ValidationMethod can acheive this, sometimes it's overly repetitive.

Stripes uses the @DontValidate annotation to make this easier. Any handler method that is annotated with @DontValidate will be invoked directly skipping validation. Type conversion and binding are still executed, and may produce validation errors. However, it is expected that in most (all?) cases the @DontValidate annotation will be used with events whose only input is not user-input (e.g. hidden fields, select lists etc.).

What Happens When There Are Errors?

When one or more validation errors are registered during a request, Stripes will execute the Resolution returned by calling ActionBeanContext.getSourcePageResolution(). By default this returns a ForwardResolution which will forward the request to the same JSP page from which it originated. The page is identified by a request parameter called _sourcePage which is written as a hidden field by the Stripes form tag, and included as a parameter by the Stripes link tag.

To change this behaviour at the global level it is possible to simply override the ActionBeanContext.getSourcePageResolution() method to return a different resolution.

Changing Validation Outcomes

There are times when even the most flexible system cannot give you everything, and you want to manage the validation process at a more detailed level. You can do this by implementing the ValidationErrorHandler interface in your ActionBean. The interface defines a single method handleValidationErrors(ValidationErrors errors) that is invoked when validation fails, but before determining what to do next.

The ActionBean can manipulate the ValidationErrors in any way it sees fit and the changes will be taken into account before proceeding. If all errors are cleared out, it will be as if they never existed! The ActionBean can also use this opportunity to undo any side-affects that might be caused by the binding process if necessary, or to substitute a different Resolution. Check out the ValidationErrorHandler javadoc for more information.

Using the field-metadata tag

Stripes provides field meta data which can be used for client side validation and layout. Here is an example provided by Aaron Porter, using the jQuery JavaScript framework:


(Required scripts available at

<s:field-metadata var="fieldMetadata"> 
$(function(){applyStripesValidation('${fieldMetadata.formId}', ${fieldMetadata});}); 

The field-metadata tag provides meta data for fields referenced by input tags. The tag can only provide meta data for fields referenced by tags that appear before its own position in the page code, so usually you'll put it at the end of your form, just before the closing form tag. (If you put it just below opening form tag, for example, no field meta data will be available.) Also, if you have more than one form on your page, be sure to set the form id yourself, otherwise you'll end up with a duplicated form id on your page and validation will only work for one of them.

The tag binds a block of Javascript (actually, JSON) code to a JSP variable, and generates an HTML script block around its body. The body therefore should be Javascript code that makes use of the meta data. Here is also an example of how to use the tag with a Jquery validation plugin:

Note: visit the above link for updates and improvements. The code below is a skeletal example for illustration:

 * Jquery addapter class so we can use built in Jquery validator. 
 * NOTE: this is only skeleton, implementing only small part of possible validation, 
 * Usage: 
 * &lt;s:field-metadata var="metaData"&gt; 
 * $(function(){$.fn.stripesValidation('${metaData.formId}', ${metaData});}); 
 * &lt;/s:field-metadata&gt; 
 * dependancies: 
 * jquery.1.3.1 
 * jquery.form 
 * jquery.validate 
	 * Processess Stripes meta data and converting it so jquery 
	 * validation plugin is used for client side validation 
	 * @param formId id of a form to be processed 
	 * @param metaData metadata that is produced by stripes tag 
	$.fn.stripesValidation = function(formId, metaData) { 
		var form = $('#' + formId); 
		var mask_count = 0; 
	for (var fieldName in metaData) { 
		var field = form.find('[name=' + fieldName + ']'); 
		addValidation(field, metaData[fieldName]); 
		// run validation: 

	function addValidation(field, obj) { 
		for (var prop in obj) { 
				case 'label': 
				case 'required': 
					if (obj[prop]) { // add only if true 
				case 'minlength': // should already be there 
					field.attr(prop, obj[prop]); 
				case 'maxlength': // should already be there 
					field.attr(prop, obj[prop]); 
				case 'mask': 
					setMask(field, obj[prop]); 
				case 'typeConverter': 
					setConverter(field, obj[prop]); 
					debug('missing this:' + prop + ':' + [obj[prop]]); 

	 * Adds regular expression validation 
	 * @param field field reference 
	 * @param mask regular expression mask 
	function setMask(field, mask) { 
		var methodRef = 'maskMethod' + mask_count; 
			methodRef: { 
				methodRef: true 
		$.validator.addMethod(methodRef, function (value) { 
			return mask.test(value); 
		}, 'Value is invalid'); 

	 * Add converter mappings 
	 * @param field field reference 
	 * @param converter converter used by stripes for given field 
	function setConverter(field, converter) { 
		if (converter == 'EmailTypeConverter') { 
		else { 
			debug('missing converter mapping:' + converter); 

	function debug(msg){ 
		if (window.console && window.console.log) { 

  • No labels