Custom Validators in Struts 2.0
Introduction
Struts 2.0 is the popular Open Source Presentation Tier framework developed by Apache Group. It is based on MVC Model 2 design pattern. Dispatcher Filter is the front controller for the struts2 based applications. Struts 2.0 has simplified web development for its users by introducing POJO based actions, interceptors, flexible validation and support for many different result types. Struts can be used to build the user interface tier of the enterprise application. Data validation is an important aspect in enterprise applications. This article explains the concept of custom validators.
also read:
Development Environment
- NetBeans IDE 6.9
- GlassFish V3
Project Structure
The sample application developed in this article is ‘Struts2CustomValidation’
where a user can register to the application by specifying few details like userId,firstname,city etc. Custom validator developed in the application will be responsible for validating the value of the city field.
Libraries/Jar Files Required
- Struts 2.0 jar files
The complete application structure is shown below:
- The User Interface (JSP pages) is created in the “Web Pages” directory. The java classes (Actions, Interceptors and struts.xml) are created in the “Source Packages” directory. The required jar files are present in the “Libraries” directory. The web application deployment descriptor “web.xml” is created by the IDE in the “WEB-INF” subdirectory of “Web Pages”.
Action Class
Struts does the request processing with the help of Action classes. Here’s the code for the RegisterAction.java which handles the request to register to our application. This class is created in the package “com.actions”.
RegisterAction.java
package com.actions; import com.opensymphony.xwork2.ActionSupport; import java.util.Date; public class RegisterAction extends ActionSupport { private String userId; private String firstName; private String lastName; private int age; private String city; private String email; private Date joiningDate; private String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public Date getJoiningDate() { return joiningDate; } public void setJoiningDate(Date joiningDate) { this.joiningDate = joiningDate; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Override public String execute() { return "success"; } }
Let us understand the request processing logic in the action class. The action class handles the register request in the execute method. There’s no validation logic hardcoded in the java class. The complete validation logic including the custom validation is added in the “RegisterAction-validation.xml” file.Upon successful validation, the user is forwarded to “Register_Success.jsp” page.
We have put the validation logic in the “RegisterAction-validation.xml” file kept in the “com.actions” package. Please note that the validation.xml file should always be kept in the same package where your action class is present.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="userId"> <field-validator type="requiredstring"> <message>Employee Id is a mandatory field</message> </field-validator> <field-validator type="regex"> <param name="expression"> <![CDATA[([E][m][p][0-9][0-9][0-9])]]> </param> <message>Valid Employee Id required e.g. Emp001</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">20</param> <param name="max">80</param> <message>Age needs to between ${min} and ${max}</message> </field-validator> </field> <field name="firstName"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>First Name is required</message> </field-validator> <field-validator type="stringlength"> <param name="maxLength">10</param> <param name="minLength">5</param> <param name="trim">true</param> <message> Enter firstName between 5 and 10 characters </message> </field-validator> </field> <field name="lastName"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>Last Name is required</message> </field-validator> <field-validator type="stringlength"> <param name="maxLength">10</param> <param name="minLength">5</param> <param name="trim">true</param> <message> Enter lastName between 5 and 10 characters </message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <message>Password is required</message> </field-validator> <field-validator type="stringlength"> <param name="maxLength">10</param> <param name="minLength">5</param> <param name="trim">true</param> <message> Enter password between 5 and 10 characters </message> </field-validator> </field> <field name="city"> <field-validator type="requiredstring" short-circuit="true"> <param name="trim">true</param> <message>You can not leave city as blank</message> </field-validator> <field-validator type="cityValidator" short-circuit="true"> <message>City value is incorrect. it must match the pattern Az*</message> </field-validator> </field> <field name="email"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>You can not leave email as blank</message> </field-validator> <field-validator type="email"> <message> The email address you entered is not valid. </message> </field-validator> </field> <field name="joiningDate"> <field-validator type="required"> <message> You cannot leave the date of joining field blank. </message> </field-validator> <field-validator type="date" > <message> Joining date must be supplied </message> </field-validator> </field> </validators>
The data validation is performed with the help of “RegisterAction-validation.xml” file. Different validation routines are configured for the the various attributes.Along with the various built in validations, we have made use of a custom validator “cityValidator” to validate the value of the “city” attribute. The id of the validator “cityValidator” is taken from the file validators.xml which is created in the default class path.
The Custom Validator
In this application, we have created a custom validator “CityValidator” which validates the value of the city attribute. The validator is created in the com.validator package inside Source Packages sub directory.The custom validator extends from the FieldValidatorSupport class and provides the implementation of validation logic in the validate() methods.
Here’s the code for the CityValidator.java which handles the logic to validate the value of the city attribute. This class is created in the package “com.validator”.
CityValidator.java
package com.validator; import com.opensymphony.xwork2.validator.ValidationException; import com.opensymphony.xwork2.validator.ValidatorContext; import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport; public class CityValidator extends FieldValidatorSupport { public void validate(Object o) throws ValidationException { String city = (String) getFieldValue("city", o); ValidatorContext ctxt = getValidatorContext(); if(city!=null){ if (!city.trim().matches("[A-Z][a-z]*")) { ctxt.addFieldError("city","City value is incorrect. It must match Pattern Az*"); } } } }
In order to use the custom validator, an xml file “validators.xml” is created in the “Source Packages” directory. This file should be available in the default class path and must be outside all packages.This file will assign the unique id to the custom validator which can be used in the “xxxAction-validation.xml” file for validating the city value of any action class.Here’s the code of validators.xml file kept in the “Source Packages” directory.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator Config 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd"> <validators> <validator name="cityValidator" class="com.validator.CityValidator"/> </validators>
Let us understand the code in the “validators.xml” file. Here we have used the tag “validator” to register “CityValidator” created in the “com.validator” package with an id “cityValidator”. The same id is used while validating the city attribute of “RegisterAction.java” class.
The User Interface Tier
The user interface part of the application is created in JSP. The home page of the application is “Register.jsp”, where a form
is displayed to the users asking for various simple details for registration purpose. PFB the code of “Register.jsp”
<%@ taglib prefix="s" uri="/struts-tags"%> <html> <s:head theme="simple"/> <title>Register Here</title> <body> <h2>Custom Validators Demo</h2> Enter the Employee Details Here <s:form action="register"> <s:textfield name="userId" key="app.userid" /> <s:textfield name="firstName" key="app.firstName" /> <s:textfield name="lastName" key="app.lastName" /> <s:password key="app.password" name="password" /> <s:textfield name="city" key="app.city" /> <s:textfield name="age" key="app.age" /> <s:textfield name="email" key="app.email" /> <s:textfield key="app.doj" name="joiningDate" /> <s:submit value="Add Employee" /> </s:form> </body> </html>
Once the user fills in some/all fields & clicks on the “Add Employee” button, the request is submitted to the action class. Upon
successful validation, the user is forwarded to “Register_Success.jsp”page. Here’s the code of “Register_Success.jsp”.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Check Employee Details Here</title> </head> <body> <h2>Employee Details saved Successfully</h2> User Id: <s:property value="userId" /> <br> <br> First Name: <s:property value="firstName" /> <br> <br> Last Name: <s:property value="lastName" /> <br> <br> Password: <s:property value="password" /> <br> <br> Age: <s:property value="age" /> <br> <br> City: <s:property value="city" /> <br> <br> Date of joining: <s:property value="joiningDate" /> <br> <br> E-Mail: <s:property value="email" /> <br> <br> </body> </html>
The Configuration Files
Here’s the configuration added in the “web.xml” file:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" 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-app_2_5.xsd"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file>Register.jsp</welcome-file> </welcome-file-list> </web-app>
In the deployment descriptor, we have added the following
configurations:
- The Welcome File of the application
- Struts2 Dispatcher Filter (Controller element of Struts2 applications)
Here’s the configuration added in the “struts.xml” file kept in the default package in the “Source Packages” directory. This file must be available in the default class path.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="com.messages.ApplicationResources"/> <package name="my-default" extends="struts-default"> <action name="register" class="com.actions.RegisterAction"> <interceptor-ref name="defaultStack"/> <result name="success">Register_Success.jsp</result> <result name="input">Register.jsp</result> <result name="error">Register.jsp</result> </action> </package> </struts>
In the struts.xml, we have added the following configurations:
- “execute” method of “RegisterAction” class is configured to handled the register attempt to the application.
- Result with the name “input” is returned whenever the validation for any of the form field fails. The user is forwarded back to the “Register.jsp” page and all the validation error messages are displayed
The resource bundle for the application is “ApplicationResources.properties” which is kept in “com.messages” directory. Here’s the configuration added in the “ApplicationResources.properties” file.
app.title=Struts2 Custom Validations Application app.welcome=Welcome to Struts2 Validators Demo Application app.userid=User Id app.city=Enter your City here app.age=Enter your Age here app.email=Enter your E-Mail here button.save=Register Here app.firstName=Enter your First Name Here app.lastName=Enter your Last Name Here app.username.blank=Please enter User Name app.email.blank=Please enter City app.age.blank=Please enter Age app.city.blank=Please enter city app.password=Password app.doj=Date of joining
Executing The Application
Finally, we are ready to execute the application. Deploy the application to the container and execute.
Open the Register.jsp page.
Enter some invalid data, The snapshot given below shows the validation error messages
Once, all the validation error messages have been rectified, the user is forwarded to Register_Success.jsp page, where the welcome message is displayed.All the details submitted by the user are available here.
Conclusion
Thus, any Struts application can take benefit of the built in validators and also take control of request processing by creating custom validators.