Friday, April 11, 2008

Struts 2 Validation : Annotations

In a previous post, I described how to use validations in Struts 2, using XML validation rules. This post will show how to use Annotation based validation in Struts 2. For this example I used the add transaction part of google's portfolio manager (noticed that they do not have validations over there). Struts 2 provides a number of validators for XML based validation rules. All of them have respective annotations defined and can be used in place of XML validation rules. In the example, we will use the @RequiredStringValidator, @RegexFieldValidator and also see how to parameterize messages when using annotations.

Follow these steps to implement the example ... There's more

  1. Create a dynamic web project in Eclipse.
  2. Copy the following jar files into the WEB-INF/lib directory, all these files are available with sturts download.
    • struts2-core-2.0.11.1.jar
    • xwork-2.0.4.jar
    • freemarker-2.3.8.jar
    • commons-logging-1.1.1.jar
    • ognl-2.6.11.jar
  3. Update your web deployment desciptor to include the sturts filter dispatcher.
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>struts2Validation</display-name>
    <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>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>
    WEB-INF/web.xml
  4. Create the input JSP
    <%@ taglib prefix="s" uri="/struts-tags"%>
    <html>
    <head>
    <title>Add Transaction</title>
    </head>
    <body>
    <s:form action="addTransaction" validate="true" method="post">
    <s:fielderror />
    <table>
    <tr>
    <td>Symbol</td>
    <td><s:textfield name="symbol"></s:textfield></td>
    </tr>
    <tr>
    <td>Type</td>
    <td><s:select name="type" list="#{'':'Select One', 'BUY':'Buy','SELL':'Sell','BUY_COVER':'Buy to Cover','SELL_SHORT':'Sell Short'}">
    </s:select></td>
    </tr>
    <tr>
    <td>Date</td>
    <td><s:textfield name="date" ></s:textfield></td>
    </tr>
    <tr>
    <td>Number of Shares</td>
    <td><s:textfield name="numberOfShares"></s:textfield></td>
    </tr>
    <tr>
    <td>Price</td>
    <td><s:textfield name="price"></s:textfield></td>
    </tr>
    <tr>
    <td>Commission</td>
    <td><s:textfield name="comission"></s:textfield></td>
    </tr>
    <tr>
    <td>Notes</td>
    <td><s:textfield name="notes"></s:textfield></td>
    </tr>
    <tr>
    <td colspan="2"> <s:submit name="submit" value="submit"></s:submit>
    <s:submit name="submitNoValidate" value="submit without validation" method="noValidation"></s:submit> </td>

    </tr>
    </table>
    </s:form>
    </body>
    </html>
    transactions.jsp

    Note: The method="noValidation" indicates to struts that on submission, the noValidation() method will be invoked on the AddTransactionAction class.
  5. Create the output JSP
    <%@ taglib prefix="s" uri="/struts-tags"%>
    <html>
    <head>
    <title>Transaction Added</title>
    </head>
    <body>
    <s:bean name="actions.AddTransactionAction" id="addTransaction" ></s:bean>
    <table>
    <tr>
    <td colspan="2">The following transaction has been added to your portfolio</td>
    </tr>
    <tr>
    <td>Symbol</td>
    <td><s:label name="symbol" value="%{symbol}" /></td>
    </tr>
    <tr>
    <td>Type</td>
    <td><s:label name="type" value="%{type}" />
    </td>
    </tr>
    <tr>
    <td>Date</td>
    <td><s:label name="date" value="%{date}"/></td>
    </tr>
    <tr>
    <td>Number of Shares</td>
    <td><s:label name="numberOfShares" value="%{numberOfShares}"></s:label></td>
    </tr>
    <tr>
    <td>Price</td>
    <td><s:label name="price" value="%{price}"></s:label></td>
    </tr>
    <tr>
    <td>Commission</td>
    <td><s:label name="comission" value="%{comission}"></s:label></td>
    </tr>
    <tr>
    <td>Notes</td>
    <td><s:label name="notes" value="%{notes}"></s:label></td>
    </tr>
    </table>
    </body>
    </html>
    done.jsp
  6. Create the Action class
    package actions;

    import org.apache.struts2.interceptor.validation.SkipValidation;

    import com.opensymphony.xwork2.ActionSupport;
    import com.opensymphony.xwork2.validator.annotations.RegexFieldValidator;
    import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
    import com.opensymphony.xwork2.validator.annotations.ValidatorType;
    import com.opensymphony.xwork2.validator.annotations.Validation;

    @Validation
    public class AddTransactionAction extends ActionSupport {

    private String symbol;
    private String type;
    private String date;
    private String numberOfShares;
    private String price;
    private String comission;
    private String notes;


    public String execute() throws Exception {
    System.out.println("In Execute");

    return SUCCESS;
    }

    @SkipValidation
    public String noValidation() throws Exception {
    System.out.println("In Novalidation");
    return SUCCESS;
    }


    public String getSymbol() {
    return symbol;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Symbol Required")
    public void setSymbol(String symbol) {
    this.symbol = symbol;
    }


    public String getType() {
    return type;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Type Required")
    public void setType(String type) {
    this.type = type;
    }


    public String getDate() {
    return date;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Date Required")
    @RegexFieldValidator(type=ValidatorType.FIELD, message="",key="date.error.message", expression = "[0-9][0-9]/[0-9][0-9]/[1-9][0-9][0-9][0-9]")
    public void setDate(String date) {
    this.date = date;
    }


    public String getNumberOfShares() {
    return numberOfShares;
    }


    @RequiredStringValidator(type = ValidatorType.FIELD, message = " Number of Shares Required")
    public void setNumberOfShares(String numberOfShares) {
    this.numberOfShares = numberOfShares;
    }


    public String getPrice() {
    return price;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Price Required")
    public void setPrice(String price) {
    this.price = price;
    }


    public String getComission() {
    return comission;
    }

    @RequiredStringValidator(type = ValidatorType.FIELD, message = "Comission Required")
    public void setComission(String comission) {
    this.comission = comission;
    }


    public String getNotes() {
    return notes;
    }


    public void setNotes(String notes) {
    this.notes = notes;
    }
    }
    AddTransactionAction.java

    Note:
    • The annotation @Validation is used to indicate that the current action might need validation. The validations on a method level can be skipped using the @SkipValidation annotation on the method.
    • The method noValidations() uses the @SkipValidation annotation, you can see this when you click on "Submit without validation" in the JSP
    • The @RequiredStringValidator annotation is used to indicate a Required Strint similar to the following xml rule
      <validators>
      <field name="numberOfShares">
      <field-validator type="requiredstring">
      <message>Number of Share is required</message>
      </field-validator>
      </field>
      </validators>
    • On the date field, I used a @RegexFieldValidator annotation, so that the date field will be mandated to have a given format.
    • Parameterized messages: You will notice that the message attribute of the @RegexFieldValidator is set to an empty string, while the key is set to a value. This is due to the fact that the message attribute is mandatory, and the key attribute is used to denote the message key from the properties files. The parameters can be retrieved in the properties files using the ${date} notation where the "date" variable is expected to available in the value stack
  7. Create a definition for the action in struts.xml
    <?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>
    <package name="struts2Validation" extends="struts-default">
    <action name="addTransaction"
    class="actions.AddTransactionAction">
    <result name="success">done.jsp</result>
    <result name="input">transactions.jsp</result>
    </action>
    </package>
    </struts>
    struts.xml

    Note: The result with name "input" is because the validator returns the result to input when validation fails.
  8. Create the properties file for messages
    date.error.message=Date ${date} is not properly formatted.
    package.properties

    Note: In the properties file, ${date} is used to retrieve the "date" value from the value stack, this is the way Struts 2 supports parameterization.

  9. Create the struts.properties file to set the theme to simple theme, so that you have more control how the UI components are laid out.
    struts.ui.theme=simple
    struts.properties

9 comments:

  1. I hava a question,
    if I do not use the Struts2 Tag such as < s:form

    can I validate the input by using annotation?

    If I want to take the validation error messages from Resource Bundle file when using annotation type validation, how can I do that? And also how can I do it when I do validation with the help of validator framework using xml files.

    ReplyDelete
    Replies
    1. Ya u can use annotation validation in place of vation.xml file . we can declare annotaion in Action Class LIke :
      @Validations(
      requiredFields =
      {@RequiredFieldValidator(type = ValidatorType.SIMPLE, fieldName = "customfield", message = "You must enter a value for field.")},
      requiredStrings =
      {@RequiredStringValidator(type = ValidatorType.SIMPLE, fieldName = "stringisrequired", message = "You must enter a value for string.")},
      emails =
      { @EmailValidator(type = ValidatorType.SIMPLE, fieldName = "emailaddress", message = "You must enter a value for email.")},
      urls =
      { @UrlValidator(type = ValidatorType.SIMPLE, fieldName = "hreflocation", message = "You must enter a value for email.")},
      stringLengthFields =
      {@StringLengthFieldValidator(type = ValidatorType.SIMPLE, trim = true, minLength="10" , maxLength = "12", fieldName = "needstringlength", message = "You must enter a stringlength.")},
      intRangeFields =
      { @IntRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "intfield", min = "6", max = "10", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
      dateRangeFields =
      {@DateRangeFieldValidator(type = ValidatorType.SIMPLE, fieldName = "datefield", min = "-1", max = "99", message = "bar must be between ${min} and ${max}, current value is ${bar}.")},
      expressions = {
      @ExpressionValidator(expression = "foo > 1", message = "Foo must be greater than Bar 1. Foo = ${foo}, Bar = ${bar}."),
      @ExpressionValidator(expression = "foo > 2", message = "Foo must be greater than Bar 2. Foo = ${foo}, Bar = ${bar}."),
      @ExpressionValidator(expression = "foo > 3", message = "Foo must be greater than Bar 3. Foo = ${foo}, Bar = ${bar}."),
      @ExpressionValidator(expression = "foo > 4", message = "Foo must be greater than Bar 4. Foo = ${foo}, Bar = ${bar}."),
      @ExpressionValidator(expression = "foo > 5", message = "Foo must be greater than Bar 5. Foo = ${foo}, Bar = ${bar}.")
      }
      )
      u want to include jar file of struts this : struts2 - convention .jar file in your lib folder as well as buid path of librery / jar .

      Delete
  2. thank you man!

    it is really madness trying to find appropriate help.

    Apache will burn in hell for lack of HELLP and bloody overengineering!

    ReplyDelete
  3. Hi,
    I'm just having a lot of troubles with the parametrized messages in Struts 2.
    I have tried almost al the possibilities I have found over the net and no one is working.

    I just want to print a message like "Account is Locked for User ID ${getUserIdOld()}" but the value from getUSerIdOld is never show, I just have the text as it is in the message without changing the value.
    Changing ${getUserIdOld()} to ${userIdOld} don't fix my problem.


    I'm reading the message form a properties file and adding it in an action error message.

    What am I doing wrong? Can you help me?

    THANKS

    ReplyDelete
  4. I have an issue while using validate method in my action. Problem that I face is I want the validate method to function only for certain methods and would like to skip for some methods. For example in my registration page I have a link "Check user availability". When I click this link a method is called from my registration action which has a validate method included in it. So whenever my other fields are empty the validate method will return "input" string so that I am unable to call the method for checking user availability.
    Any help is higly appreciated. Thanks in advance.

    ReplyDelete
  5. i have one jsp page for input. this page also shows (s:select)list populated from some other action.
    when i get input error from that page and when i call that page again by showing input error it shows empty list in (s:select).
    how to call that action to get list when i get input error.

    ReplyDelete
  6. I have used tag of dojo in my application for use of tag...
    Due to this struts 2 validation not working.When I click on submit button application doesn't proceeds and if I remove the it works.
    plz answer me as fast as possible..
    thank you ..
    I am waiting..

    ReplyDelete
  7. I have used tag sx:head/of dojo in my application for use of sx:datetimepicker tag...
    Due to this struts 2 validation not working.When I click on submit button application doesn't proceeds and if I remove the sx:head/ it works.
    plz answer me as fast as possible..
    thank you ..
    I am waiting..

    ReplyDelete