Friday 25 May 2012

CustomValidation using Annotation in Struts

While doing the validation, the developer can write his own custom validation to validate the fields. In Struts2 it can be done by using @CustomValidator. The Custom Validator takes mandatory fields as:
1. type - it is the name to the validator which will be defined in the validation.xml
2. message - it is the message which you would like to show on the validation failure.
3.fieldName - this will be necessary if you are not defining the validator above the field name. In the below example i have used the validator at the class level so its necessary to mention the name of the validator.


A custom validator can be implemented by implementing the FieldValidatorSupport or the ValidatorSupport. Below is the example to write a custom validator.


-----------------------------------------------------------------------
PhoneNumberValidator.java :
-----------------------------------------------------------------------

package com.lntinfotech.office.common;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.xwork2.validator.ValidationException;
import com.opensymphony.xwork2.validator.validators.FieldValidatorSupport;

public class PhoneNumberFieldValidator extends FieldValidatorSupport {
private Logger logger=LoggerFactory.getLogger(getClass());
public void validate(Object object) throws ValidationException {

String fieldName = getFieldName();
Object value = this.getFieldValue(fieldName, object);

if (!(value instanceof String)) {
return;
}

String str = ((String) value).trim();
try{
if (str.length() != 0) {
if (str.indexOf("-", 0) <= 5) {
if (str.indexOf("-") == str.lastIndexOf("-")) {
String stdcode = str.substring(0, str.indexOf("-"));
String phoneNo = str.substring((str.indexOf("-") + 1));
if(phoneNo.length()<6){
addFieldError(fieldName, object);
}
try {
Integer.parseInt(stdcode);
Integer.parseInt(phoneNo);
} catch (NumberFormatException nfe) {
addFieldError(fieldName, object);
logger.error("<-- validate(Object object) :"+nfe);
return;
}

return;
}
}

}
}catch(Exception e ){
addFieldError(fieldName, object);
logger.error("<-- validate(Object object) :"+e);
return;
}
}
}


note:


The fieldName and getFieldValue are implemented in superclass and are used to retrieve the name and value of the field getting validated respectively.
The addFieldError method is used to add any failed validations to the list of error to be displayed.
-----------------------------------------------------------------------


The validators.xml is used to map the validator class with a name which will be used in the class which will request validation.
-----------------------------------------------------------------------
validators.xml
-----------------------------------------------------------------------

<?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="phoneNumberFieldValidator" class="com.lntinfotech.office.common.PhoneNumberFieldValidator"/>
</validators>
-----------------------------------------------------------------------


In the below class we will be validating the phone number using the custom validator. I have removed various other fields from the class as its not necessary. For validating the field either you can define the validation at the class level as I have done below or can be done on the getter method of the field. for doing the validation at the getter method you need to define the validator just above the getter method.


-----------------------------------------------------------------------
RegistrationAction.java
-----------------------------------------------------------------------


package com.lntinfotech.office.controller;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.CustomValidator;
import com.opensymphony.xwork2.validator.annotations.Validations;

@ParentPackage(value = "office")
@InterceptorRef("jsonValidationWorkflowStack")
@Validations(
customValidators = { @CustomValidator(fieldName = "phoneNo", message = "Please enter the phone number in STD-Phone(XXX-XXXXXX) Format", type = "phoneNumberFieldValidator") })
public class RegistrationAction extends ActionSupport {
private static final long serialVersionUID = 1L;

private Logger logger = LoggerFactory.getLogger(getClass());

private String phoneNo;
public String getPhoneNo() {
return phoneNo;
}

public void setPhoneNo(String phoneNo) {
this.phoneNo = phoneNo;
}

@Action(value = "/createuser", results = {
@Result(location = "registrationSuccess.jsp", name = "success"),
@Result(location = "registration.jsp", name = "input"),
@Result(location = "registration.jsp", name = "error") }, className = "registerUser")
public String createUser() {
logger.debug("--> createUser()");
return "success";
}

}

-----------------------------------------------------------------------

Thursday 17 May 2012

Struts 2.3.3 integration with Spring 3.0.5 using Struts annotation

An example application to integrate Struts and Spring using the struts annotation. The application is very simple, just shown the way to do the confuguration and hence not implemented variuos features given by the two framework.

First we will do the configuration in the Deployment discriptor to use Struts FilterDispatcher and the spring WebApplicationContext. Below is the web.xml

------------------------------------------------------------------------------------
web.xml
------------------------------------------------------------------------------------





<?xml version="1.0" encoding="UTF-8"?>

<web-app id="starter" version="2.4" 
         xmlns="http://java.sun.com/xml/ns/j2ee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts 2 - Maven Archetype - Starter</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext*.xml</param-value>
</context-param>
<!-- Filters -->
<filter>
        <filter-name>action2-cleanup</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
    </filter>
    <filter>
        <filter-name>action2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
<filter-mapping>
        <filter-name>action2-cleanup</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>action2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!-- Listeners -->
<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    
    <!-- Servlets -->
    <servlet>
    <servlet-name>jspSupportServlet</servlet-name>
    <servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
    <load-on-startup>5</load-on-startup>
    </servlet>
    
    
    <!-- Welcome file lists -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

</web-app>

------------------------------------------------------------------------------------
now we will define the struts.xml for the basic configuration. This can be moved as per your requirment.
------------------------------------------------------------------------------------
struts.xml
------------------------------------------------------------------------------------





<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="false" />
<constant name="struts.convention.result.path" value="/pages" />
<constant name="struts.convention.action.packages" value="com.lnt.struts" />

<constant name="struts.objectFactory.spring.autoWire" value="auto" />
<package name="showcase" extends="struts-default,json-default"
namespace="/">
</package>
</struts>

------------------------------------------------------------------------------------
Now we will configure the spring webapplicationcontext so that we can integrate the spring with the struts.xml
------------------------------------------------------------------------------------
applicationContext.xml
------------------------------------------------------------------------------------






<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC 
"-//SPRING//DTD BEAN//EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-autowire="autodetect">
<bean id="helloworld" class="com.lnt.struts.Testing" singleton="false">
</bean>
</beans>

------------------------------------------------------------------------------------

Now we will configure the struts mapping using the annotation in the class file. In the below java class we have annotated the method. The annotation "Action" has been used which tells the application that  for which url mapping which method or class has to be executed . In the below class we have defined the Action value as "helloworld", so when the url "/helloworld.action" will be hit the application knows which method it has to execute. We have also defined few more attributes. I mean results, which tells the application once it has executed the class as per the definition which view it has to redirect the control. The last configuration "className" is too important in the StrutsSpring application, As this attribute tells the application to look into the applicationContext of the spring for the particular bean definition defined.

Please note as our application is started the url "/helloworld.action" will be hit, so as per the configuration we have done the control will be coming to the HelloWorldAction.java. Once the control comes here the other configuration plays their part. The configuration "className" done above the method hello() tells the application to look for the name helloworld in the applicationcontext.xml.(the attribute "className" need not be same as url. you can give the same name you will define in the applicationContext.xml). Once the bean definition is seen the control moves to the class defined as per the applicationContext.xml. In our application the control moves to the Testing.java and executes the method hello(). This is because we have defined the configuration above the hello() method in the HelloWorldAction.java.(The controller takes the name of the method above which the configuration is defined and search and execute the same name method in the class defined by the spring bean).

Now if you dont want your spring configuration to overwrite the method defined by the struts, you just hace to remove the configuration "className". In that case the application will execute the method above which the mapping configuration has been defined. In our application that would be hello() method from the HelloWorldAction.java.
------------------------------------------------------------------------------------
HelloWorldAction .java
------------------------------------------------------------------------------------
package com.lnt.struts;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;

import com.opensymphony.xwork2.ActionSupport;

@ParentPackage(value = "showcase")

public class HelloWorldAction extends ActionSupport {

@Action(value="helloworld",results={@Result(location="hello.jsp",name="success"),@Result(location="input.jsp",name="input"),@Result(location="error.jsp",name="error")},className="helloworld")
public String hello(){
System.out.println("inside Helloworld hello");
return "error";
}
  
    public String execute() throws Exception {
        System.out.println("inside execute method");
     return SUCCESS;
    }
}

------------------------------------------------------------------------------------
Testing.java
------------------------------------------------------------------------------------
package com.lnt.struts;
public class Testing {
 public String hello(){
  System.out.println("inside Testing hello");
  return "error";
 }

  public String execute() throws Exception {
         System.out.println("inside Testing execute method");
      return "success";
     }
}

 ------------------------------------------------------------------------------------

Now you may be wondering why other methods are been defined if its not used any where...... It is just to let you know how to define the same configuration for a class. For example

@ParentPackage(value = "showcase")
@Action(value="helloworld",results={@Result(location="hello.jsp",name="success"),@Result(location="input.jsp",name="input"),@Result(location="error.jsp",name="error")},className="helloworld")
public class HelloWorldAction extends ActionSupport {
// same code as above except the configuration above the method.
}

When you are defining configuration for the class the same configuration you need to put above the class declaration. Now the application will act the same way it has done for the method but instead of executing the hello() method defined in the class it will execute the execute() method of the class. This is because we have exetended our class to ActionSupport. Now as per the configuration of the "className", the application will decide which method to execute from which class the same way it does for the method configuration.
------------------------------------------------------------------------------------
error.jsp
------------------------------------------------------------------------------------




<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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>Insert title here</title>
</head>
<body>
error
</body>
</html>


------------------------------------------------------------------------------------
input.jsp
------------------------------------------------------------------------------------




<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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>Insert title here</title>
</head>
<body>
input
</body>
</html>

------------------------------------------------------------------------------------
hello.jsp
------------------------------------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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>Insert title here</title>
</head>
<body>
Hello
</body>
</html>
------------------------------------------------------------------------------------
index.jsp
------------------------------------------------------------------------------------




<% response.sendRedirect("helloworld.action"); %>


the index.jsp can be avoided by configuring the welcome tag in the web.xml to directly pointing helloworld.action url.
------------------------------------------------------------------------------------
Project Sturcture
------------------------------------------------------------------------------------

------------------------------------------------------------------------------------
library required
------------------------------------------------------------------------------------
this jar list is for various other needs. Whole jars may not be needed for this application
  1. antlr-2.7.6.jar
  2. aopalliance-1.0.jar
  3. asm-3.3.jar
  4. asm-commons-3.3.jar
  5. asm-tree-3.3.jar
  6. c3p0-0.9.1.jar
  7. commons-collections-3.1.jar
  8. commons-fileupload-1.1.1.jar
  9. commons-io-2.0.1.jar
  10. commons-lang3-3.1.jar
  11. commons-logging-1.0.4.jar
  12. dom4j-1.6.1.jar
  13. dwr-1.1-beta-3.jar
  14. freemarker-2.3.19.jar
  15. hibernate-c3p0-3.6.3.Final.jar
  16. hibernate-commons-annotations-3.2.0.Final.jar
  17. hibernate-core-3.6.3.Final.jar
  18. hibernate-jpa-2.0-api-1.0.0.Final.jar
  19. javassist-3.11.0.GA.jar
  20. jta-1.1.jar
  21. log4j-1.2.16.jar
  22. mysql-connector-java-5.1.9.jar
  23. ognl-3.0.5.jar
  24. sitemesh-2.4.2.jar
  25. slf4j-api-1.6.4.jar
  26. slf4j-jcl-1.6.4.jar
  27. slf4j-log4j12-1.6.4.jar
  28. spring-2.5.6.jar
  29. spring-aop-3.0.5.RELEASE.jar
  30. spring-asm-3.0.5.RELEASE.jar
  31. spring-beans-3.0.5.RELEASE.jar
  32. spring-context-3.0.5.RELEASE.jar
  33. spring-core-3.0.5.RELEASE.jar
  34. spring-expression-3.0.5.RELEASE.jar
  35. spring-web-3.0.5.RELEASE.jar
  36. struts2-config-browser-plugin-2.3.3.jar
  37. struts2-convention-plugin-2.3.3.jar
  38. struts2-core-2.3.3.jar
  39. struts2-sitemesh-plugin-2.3.3.jar
  40. struts2-spring-plugin-2.3.3.jar
  41. xwork-core-2.3.3.jar
------------------------------------------------------------------------------------
pom.xml configuration for the above project:
------------------------------------------------------------------------------------
this pom.xml is configured to use Struts 2.3.3 , Spring 3.0.5 , Hibernate 3.6.3 and mysql 5.1





<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lnt</groupId>
<artifactId>struts</artifactId>
<name>StrutsSpringproject</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<struts2.version>2.3.3</struts2.version>
</properties>

<dependencies>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

<!-- Struts 2 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-sitemesh-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-config-browser-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>

<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
<!-- Servlet & Jsp -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>

<!-- Jakarta Commons -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.1.1</version>
</dependency>

<!-- Dwr -->
<dependency>
<groupId>uk.ltd.getahead</groupId>
<artifactId>dwr</artifactId>
<version>1.1-beta-3</version>
</dependency>

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.3.Final</version>
</dependency>

<!-- Hibernate c3p0 connection pool -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>3.6.3.Final</version>
</dependency>


<!-- log4j dependency -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.6.4</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>

<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>

<!-- Strutsjquery plugin dependency start -->
<!-- <dependency> <groupId>com.jgeppert.struts2.jquery</groupId> <artifactId>struts2-jquery-plugin</artifactId> 
<version>3.3.1</version> </dependency> <dependency> <groupId>com.jgeppert.struts2.jquery</groupId> 
<artifactId>struts2-jquery-grid-plugin</artifactId> <version>3.3.1</version> 
</dependency> <dependency> <groupId>com.jgeppert.struts2.jquery</groupId> 
<artifactId>struts2-jquery-richtext-plugin</artifactId> <version>3.3.1</version> 
</dependency> <dependency> <groupId>com.jgeppert.struts2.jquery</groupId> 
<artifactId>struts2-jquery-tree-plugin</artifactId> <version>3.3.1</version> 
</dependency> <dependency> <groupId>com.jgeppert.struts2.jquery</groupId> 
<artifactId>struts2-jquery-mobile-plugin</artifactId> <version>3.3.1</version> 
</dependency> -->

</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
------------------------------------------------------------------------------------
project structure as a maven project
------------------------------------------------------------------------------------