Struts 2 – Spring Security – Hibernate Custom Form Example

Hi All,

In the previous post, we have seen how to set up the Spring Security with Struts2 forms. In this example we are going to see how to integrate Hibernate framework with Spring Security. Let’s move on to the example.

BEHIND THE SCENE

  • NetBeans IDE 7.1.1
  • Spring-Core-3.2.1.RELEASE
  • Spring-Security-3.2.1.RELEASE
  • Struts-core-2.3.15.3
  • Hibernate 3
  • JDK 1.7
  • Apache Tomcat 7.0.22.0
  • Microsoft SQL Server 2012

IMPLEMENTATION STEPS

Here are the steps to be followed for implementing spring security in our application.

1. web.xml

Here, we have to enable spring support for our application by adding a listener in the deployment descriptor. Also we have added spring security filter and struts2 filter.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
       
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-database.xml
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>
       
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <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>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
        
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>/redirect.jsp</welcome-file>
    </welcome-file-list>
</web-app>

2. spring-database.xml

It depicts the custom user service. We represent our custom user service as a spring bean with an id “userDetailsService”. Spring creates an instance of this bean in the spring context when the application initializes. Please note that the file has been added to web.xml (line no. 7).


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <bean id="userDetailsService" class="com.ssh.service.UserService"/>
   
</beans>    

3. spring-security.xml

It describes the custom login form, “daoAuthenticationProvider” and “authenticationManager”. This file has also been added to web.xml (line no. 8).


<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/security
	http://www.springframework.org/schema/security/spring-security-3.2.xsd">
            
 
    <http auto-config="true">
        
        <intercept-url pattern="/admin" access="ROLE_ADMIN" />
        
        <access-denied-handler error-page="/WEB-INF/Content/403.jsp" />
        
        <form-login 
            login-page="/login" 
            default-target-url="/admin" 
            authentication-failure-url="/login?error" 
            username-parameter="username"
            password-parameter="password" />
        <logout logout-success-url="/login?logout" />
        
    </http>
    
    <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <beans:property name="userDetailsService" ref="userDetailsService"/>
    </beans:bean>
    
    
    <beans:bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <beans:property name="providers">
            <beans:list>
                <beans:ref local="daoAuthenticationProvider" />
            </beans:list>
        </beans:property>
    </beans:bean>
    
    <authentication-manager erase-credentials="false">
        <authentication-provider user-service-ref="userDetailsService">
        </authentication-provider>
    </authentication-manager>
    
</beans:beans>

3. UserService.java

This is our user service class.

package com.ssh.service;

import com.ssh.dao.UserDao;
import com.ssh.entity.Role;
import com.ssh.entity.User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.persistence.EntityNotFoundException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class UserService implements UserDetailsService {

    UserDao dao = new UserDao();

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = null;
        try {
            user = dao.findUser(username);
            if (user == null) {
                throw new UsernameNotFoundException("user not found");
            }
        } catch (Exception e) {
            throw new EntityNotFoundException(e.getCause().getMessage());
        }
        String uname = user.getUsername();
        String password = user.getPassword();
        boolean enabled = user.getEnabled();
        boolean accountNonExpired = Boolean.TRUE;
        boolean credentialsNonExpired = Boolean.TRUE;
        boolean accountNonLocked = Boolean.TRUE;
        Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for (Iterator it = user.getRoles().iterator(); it.hasNext();) {
            Role role = (Role) it.next();
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return new org.springframework.security.core.userdetails.User(
                uname, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }
}

4. UserDao.java

The DAO class.

package com.ssh.dao;

import com.ssh.entity.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class UserDao {

    SessionFactory sessionFactory = null;
    Session session = null;

    public User findUser(String username) throws Exception {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            session = sessionFactory.openSession();
            User user = (User) session.createQuery("from User where username=:username").setParameter("username", username).uniqueResult();
            return user;
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            session.flush();
            session.close();
        }
    }
}

In this example, we need two entities namely “Role” and “User” which in turn creates two relational database tables “role” and “users” using Hibernate framework. A separate join table namely “user_roles” is also created .

5. Role.java

Here’s the role entity.

package com.ssh.entity;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Role implements Serializable {

    private Long id;
    private String name;

    @Id
    @GeneratedValue
    @Column(name = "roleId")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

6. User.java

The user entity.

package com.ssh.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;

@Entity
@Table(name = "users")
public class User implements Serializable {

    private Long id;
    private String username, password;
    private Boolean enabled;
    private List<Role> roles = new ArrayList<Role>();

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "user_roles", joinColumns = {
        @JoinColumn(name = "userId"),}, inverseJoinColumns = {
        @JoinColumn(name = "roleId")})
    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue
    @Column(name = "userId")
    public Long getId() {
        return id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

Now we persist users and roles by using the following code snippet.

public void saveRole() throws Exception {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            session = sessionFactory.openSession();
            session.getTransaction().begin();
            Role role = new Role();
            role.setName("ROLE_ADMIN");
            session.save(role);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

    public void saveUser() throws Exception {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            session = sessionFactory.openSession();
            session.getTransaction().begin();
            User user = new User();
            user.setUsername("ddkr");
            user.setPassword("1986");
            Role role = (Role) session.get(Role.class, 1L);
            List<Role> roles = new ArrayList<Role>();
            roles.add(role);
            user.setRoles(roles);
            session.save(user);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

public void saveRole() throws Exception {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            session = sessionFactory.openSession();
            session.getTransaction().begin();
            Role role = new Role();
            role.setName("ROLE_MANAGER");
            session.save(role);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

    public void saveUser() throws Exception {
        try {
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            session = sessionFactory.openSession();
            session.getTransaction().begin();
            User user = new User();
            user.setUsername("arun");
            user.setPassword("1234");
            Role role = (Role) session.get(Role.class, 2L);
            List<Role> roles = new ArrayList<Role>();
            roles.add(role);
            user.setRoles(roles);
            session.save(user);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

The generated sql and table structure is given below.

USE [Spring]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Role](
	[roleId] [numeric](19, 0) IDENTITY(1,1) NOT NULL,
	[name] [varchar](255) NULL,
PRIMARY KEY CLUSTERED 
(
	[roleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

USE [Spring]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[users](
	[userId] [numeric](19, 0) IDENTITY(1,1) NOT NULL,
	[enabled] [tinyint] NULL,
	[password] [varchar](255) NULL,
	[username] [varchar](255) NULL,
PRIMARY KEY CLUSTERED 
(
	[userId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

USE [Spring]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[user_roles](
	[userId] [numeric](19, 0) NOT NULL,
	[roleId] [numeric](19, 0) NOT NULL,
UNIQUE NONCLUSTERED 
(
	[roleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[user_roles]  WITH CHECK ADD  CONSTRAINT [FK73429949537304F6] FOREIGN KEY([roleId])
REFERENCES [dbo].[Role] ([roleId])
GO

ALTER TABLE [dbo].[user_roles] CHECK CONSTRAINT [FK73429949537304F6]
GO

ALTER TABLE [dbo].[user_roles]  WITH CHECK ADD  CONSTRAINT [FK7342994958C85A60] FOREIGN KEY([userId])
REFERENCES [dbo].[users] ([userId])
GO

ALTER TABLE [dbo].[user_roles] CHECK CONSTRAINT [FK7342994958C85A60]
GO

This is how the tables look like.

spring_security_table_values

 

 

 

 

 

 

This is the table structure

spring_security_table_structure

 

 

 

 

 

 

 

 

 

 

 

 

 

 

7. struts.xml

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

<struts>
    
    <package name="default" extends="struts-default" namespace="/">
        
        <action name="admin" class="com.ssh.actions.AdminAction" method="execute">
            <result name="success">/WEB-INF/Content/admin.jsp</result>
        </action>
        <action name="login">  
            <result>/WEB-INF/Content/login.jsp</result>  
        </action>  
    </package>
</struts>

8. hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
        <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
        <property name="hibernate.connection.url">jdbc:sqlserver://localhost:1433;databaseName=Spring</property>
        <property name="hibernate.connection.username">username</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <mapping class="com.ssh.entity.Role"/>
        <mapping class="com.ssh.entity.User"/>
    </session-factory>
</hibernate-configuration>

9. AdminAction.java

package com.ssh.actions;

import com.opensymphony.xwork2.ActionSupport;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

public class AdminAction extends ActionSupport {

    private String username;
    private String password;

    @Override
    public String execute() {

        //Principal principal = ServletActionContext.getRequest().getUserPrincipal();
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        System.out.println("username: " + userDetails.getUsername());
        System.out.println("password: " + userDetails.getPassword());
        Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) userDetails.getAuthorities();
        for (Iterator it = authorities.iterator(); it.hasNext();) {
            SimpleGrantedAuthority authority = (SimpleGrantedAuthority) it.next();
            System.out.println("Role: " + authority.getAuthority());
        }

        return SUCCESS;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPassword() {
        return password;
    }
}

Next we move to the UI part.

10. redirect.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">
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=login">

This JSP page has been set as welcome page in the deployment descriptor. When this page is loaded in the browser, the URL gets redirected to “login” action.

11. login.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
    <body>
        <h1>Struts2 - Spring Security Demo</h1> 
        <s:if test="%{#parameters.error != null}">
            <div style="color: red">Invalid User</div>
        </s:if>
        <s:form name="loginForm" action="j_spring_security_check" method="post">
            <s:textfield name="username" label="Username"/>
            <s:password name="password" label="Password"/>
            <s:submit value="Login"/>
        </s:form>
    </body>
</html>

12. admin.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page session="true"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Admin Page</title>
    </head>
    <body>
        <h1>Hello ${pageContext.request.userPrincipal.name}</h1>  
        <a href="j_spring_security_logout">Logout</a>
    </body>
</html>

13. 403.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Access Denied</title>
    </head>
    <body>
        <h1>Access Denied for ${pageContext.request.userPrincipal.name}!!!</h1>
        <a href="login">Back</a>
    </body>
</html>

DIRECTORY STRUCTURE

directory_structure_ssh

 

 

 

 

 

 

 

 

 

 

 

The above said example says, the access to the URL pattern “/admin” is restricted only to the users with role name “ROLE_ADMIN”. Then we create a user with username “ddkr” and password “1986” and assign him the role “ROLE_ADMIN”. So obviously he can access the URL pattern “/admin”. We have also created another user “arun” who is having “ROLE_MANAGER” role. When “arun” tries to access “/admin” URL, he is redirected to access-deny page. When some other unauthorized users try to access the “/admin” URL, the spring security tries to authenticate them but gets failed and they are redirected to the login page again.

SCREEN SHOTS

1. Login Screen

login_ssh_new

2. Login as “ddkr”, an authorized user

login_before_ddkr_ssh_new

login_after_ddkr

3. Logout

logout_ssh_new

4. Login as “arun”, an unauthorized user

login_before_arun_ssh_new

login_after_arun_ssh_new

5. Login as a non-existing user

login_before_invalid_ssh_new

login_invalid_ssh_new

Thanks.

Learn Spring By Example – Struts+Spring+Hibernate Integration

Hi friends,

Finally after a long time, we are back. Here we’ve yet another example, as the title says, it is an Integration example in which we are going to see how Struts, Spring and Hibernate are getting integrated together.

In the following example, Struts is used for view layer and also works as a controller, spring for IOC and transaction layers, hibernate for ORM (model layer).

Platform

OS: Windows XP Professional SP3.
IDE: Eclipse Juno.
Java: 1.6.0
Application Server: Apache Tomcat 6.0.37
Frameworks: Struts 2.1.8, Spring 3.2.1, Hibernate 3.2.0.
Databse: MySQL 5.1.32-community.

Ok, let’s start. The heart of the application is applicationContext.xml, where these frameworks are integrated. The file looks like this.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	<tx:annotation-driven transaction-manager="transactionManager" />

	<context:annotation-config />

	<context:component-scan
		base-package="com.stringer.action,com.stringer.bo,com.stringer.dao" />

	<context:property-placeholder location="/WEB-INF/jdbc.properties" />

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${driver}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties" ref="hibernateProperties" />
		<property name="packagesToScan" value="com.stringer.model"></property>
	</bean>

	<bean id="hibernateProperties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="locations">
			<list>
				<value>/WEB-INF/hibernate.properties</value>
			</list>
		</property>
	</bean>

	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager"
		p:sessionFactory-ref="sessionFactory">
	</bean>

</beans>

It’s sorry to say that I’m not going to explain each and every point of the above configuration file. You can find out these definitions in every nook and corner of the web. Anyway I shall explain in brief.

<context:property-placeholder location="/WEB-INF/jdbc.properties" />

The above line tells spring to load property file from the specified location. We can also load multiple property files simultaneously like this.

<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${driver}" />
		<property name="url" value="${url}" />
		<property name="username" value="${username}" />
		<property name="password" value="${password}" />
	</bean>

The above bean definition says all about the database connection, i.e., driver, username, password, url etc.

<bean id="hibernateProperties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="locations">
			<list>
				<value>/WEB-INF/hibernate.properties</value>
			</list>
		</property>
	</bean>

The above bean definition tells spring to load hibernate properties from the specified file.

<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties" ref="hibernateProperties" />
		<property name="packagesToScan" value="com.stringer.model"></property>
	</bean>

The above definition creates a “sessionFactory” object using “dataSource” and “hibernateProperties”. The property “packagesToScan” specifies the path where the scanning for hibernate entities is to be performed. In this example, all model entities are encapsulated in “com.stringer.model” package.

Here comes the definition for creating a transaction manger object.

	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager"
		p:sessionFactory-ref="sessionFactory">
	</bean>

The given piece of line used to enable spring annotation.

<context:annotation-config />

Finally, the below tag is used to scan for spring components in an application. The location of where to scan for classes is also specified.

<context:component-scan
		base-package="com.stringer.action,com.stringer.bo,com.stringer.dao" />

As it is already discussed in one of the previous post, spring scans for classes that are annotated with one of the following annotations.

  • @Component
  • @Controller
  • @Repository
  • @Service
  • Any custom annotation that is itself annotated with @Component

Ok, now let’s look into property files.

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/Stringer
username=root
password=password

hibernate.properties

hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update

Now we’ve finished spring configuration. Next we are going to see struts configuration.

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="default" extends="struts-default" namespace="/">

		<global-results>
			<result name="StringerException" type="chain">
				<param name="actionName">StringerExceptionAction</param>
			</result>
			<result name="error">/WEB-INF/Content/error.jsp</result>
		</global-results>

		<global-exception-mappings>
			<exception-mapping exception="java.lang.Exception"
				result="StringerException" />
		</global-exception-mappings>

		<action name="StringerExceptionAction" class="com.stringer.util.StringerException">
			<result name="StringerExceptionResult">/WEB-INF/Content/error.jsp</result>
		</action>

	</package>

	<include file="struts-user.xml" />
	<include file="struts-account.xml" />
	<include file="struts-index.xml" />

</struts>

In the above file, we can see global-results and global-exceptions are mapped. Also, it includes some other action mapping files. Struts supports split the overall mappings into several mapping files. One of the mapping file is given below. The given file is used for action mappings of user module.

struts-user.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="User" extends="default" namespace="/User">

		<action name="*" method="{1}" class="UserAction">
			<result name="success">/WEB-INF/Content/User/home.jsp</result>
			<result name="input">/WEB-INF/Content/User/login.jsp</result>
			<result name="list">/WEB-INF/Content/User/list.jsp</result>
			<result name="profile">/WEB-INF/Content/User/profile.jsp</result>
			<result name="home">/WEB-INF/Content/User/home.jsp</result>
			<result name="create">/WEB-INF/Content/User/register.jsp</result>
			<result name="redirect" type="redirect">list.htm</result>
		</action>

	</package>

</struts>

Just look into the highlighted line given above, where we can see an attribute namely “class” and its value “UserAction”. Here, it’s not the class name actually, but the name of a spring bean. Just scroll up to the UserAction.java and we can see that the class has been annotated(@Controller) as a Controller and the controller’s name is given as “UserAction”. When we annotate a class as a controller, it becomes a spring component and we can reuse it anywhere in the application. How we could use it in this way because we add struts-spring integration plugin to the application and it helps us out.

struts.properties

struts.action.extension=htm
struts.ognl.allowStaticMethodAccess=true
struts.objectFactory= org.apache.struts2.spring.StrutsSpringObjectFactory
struts.objectFactory.spring.autoWire=type
struts.convention.result.path=/WEB-INF/content/
struts.devMode=true
struts.custom.i18n.resources=global

Here some of the struts properties defined, in which struts objectFactory and spring autowire default mode are notable. Here, we set default autowire mode as “type”. It allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown. You can get to know more about spring autowiring from here.

This is for logging, which is optional.

log4j.properties

log4j.rootLogger = INFO, FILE
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=C://log.out
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=#[%d{dd/MMM/yyyy HH:mm:ss}] - File[%F] - Method[%M] - Line[%L] - Priority[%p] - Message[%m]%n

And the following two files stand for struts internationalization(i18n) or localization(i10n) purpose.

package.properties

lbl.user.fname=First Name
lbl.user.lname=Last Name
lbl.user.age=Age
lbl.user.place=Place
lbl.user.uname=Username
lbl.user.pwd=Password
lbl.acc.no=Account Number
lbl.acc.name=Account Name
lbl.acc.desc=Account Description
lbl.acc.type=Account Type
lbl.acc.opbal=Opening Balance
btn.login=Login
btn.reg=Register
btn.sou=Save / Update
btn.home=Home

global.properties

msg.user.invalid=Invalid Username or Password !
msg.user.notfound=A User with username "$1" not found !

Finally, the most indispensable configuration.

web.xml

<?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>Stringer</display-name>
	<welcome-file-list>
		<welcome-file>/redirect.jsp</welcome-file>
	</welcome-file-list>

	<filter>
		<filter-name>struts2</filter-name>
		<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

<listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>
<listener>
    <listener-class>
      com.stringer.util.StringerListener
    </listener-class>
  </listener>
  
  <session-config>
  <session-timeout>600</session-timeout>
  </session-config>
  
</web-app>

Please note how struts filter and spring listener are configured up here. We could see a user defined listener, namely StringerListener, which has also been set up there.

Ok, finally all the configurations come to an end. Next we shall go to the source. First off, let’s see the welcome page.

redirect.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">
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=index.htm">

Here, we are going to discuss the complete flow of user registration.

register.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">
<%@taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Register</title>
</head>
<body>
	<fieldset style="width: 20%; background-color: lightyellow">
		<s:form action="saveOrUpdate" namespace="/User">
			<s:hidden name="user.id" />
			<s:hidden name="user.version" />
			<s:textfield name="user.firstName" key="lbl.user.fname" />
			<s:textfield name="user.lastName" key="lbl.user.lname" />
			<s:textfield name="user.userName" key="lbl.user.uname" />
			<s:password name="user.password" key="lbl.user.pwd" />
			<s:textfield name="user.age" key="lbl.user.age" />
			<s:textfield name="user.place" key="lbl.user.place" />
			<s:submit key="btn.sou" />
		</s:form>
	</fieldset>
</body>
</html>

UserPOJO.java

package com.stringer.pojo;

import com.opensymphony.xwork2.conversion.annotations.TypeConversion;

public class UserPOJO {
	private String firstName, lastName, userName, password, place;
	private int age;
	private Long id, salt, version;
	private Boolean isObsolete;

	public Boolean getIsObsolete() {
		return isObsolete;
	}

	public void setIsObsolete(Boolean isObsolete) {
		this.isObsolete = isObsolete;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	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;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getPlace() {
		return place;
	}

	public void setPlace(String place) {
		this.place = place;
	}

	public int getAge() {
		return age;
	}

	@TypeConversion(converter = "com.stringer.util.IntegerConverter")
	public void setAge(int age) {
		this.age = age;
	}

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}

	public Long getSalt() {
		return salt;
	}

	public void setSalt(Long salt) {
		this.salt = salt;
	}
}

There’s another class, namely UserDTO.java, which is used to transfer values between action and service.

UserDTO.java

package com.stringer.dto;

public class UserDTO {
	private String firstName, lastName, userName, password, place;
	private int age;
	private Long id, salt, version;
	private Boolean isObsolete;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	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;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getPlace() {
		return place;
	}

	public void setPlace(String place) {
		this.place = place;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}

	public Long getSalt() {
		return salt;
	}

	public void setSalt(Long salt) {
		this.salt = salt;
	}

	public Boolean getIsObsolete() {
		return isObsolete;
	}

	public void setIsObsolete(Boolean isObsolete) {
		this.isObsolete = isObsolete;
	}

}

UserAction.java

package com.stringer.action;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import com.stringer.bo.UserBO;
import com.stringer.dto.UserDTO;
import com.stringer.pojo.FetchType;
import com.stringer.pojo.SortBy;
import com.stringer.pojo.SortOrder;
import com.stringer.pojo.UserPOJO;
import com.stringer.util.ApplicationUtils;

@Controller("UserAction")
@Scope("prototype")
public class UserAction extends BaseAction {

	private UserPOJO user = null;
	private UserDTO userDto = null;
	private @Autowired
	UserBO userBO;

	public UserPOJO getUser() {
		return user;
	}

	public void setUser(UserPOJO user) {
		this.user = user;
	}

	public String Register() {
		return CREATE;
	}

	public String Login() {
		return INPUT;
	}

	public String saveOrUpdate() throws StrutsActionException {
		try {
			userDto = ApplicationUtils.copyProperties(user, UserDTO.class);
			userBO.saveOrUpdate(userDto);
			if (isNull(session.get("loggedUser"))) {
				session.put("loggedUser", userDto);
			}
			return HOME;
		} catch (Exception e) {
			throw new StrutsActionException(e);
		}
	}

}

Here, UserAction.java works as a controller; see the highlighted line.

UserBO.java

package com.stringer.bo;

import com.stringer.dto.UserDTO;

public interface UserBO extends BaseBO {

	public void saveOrUpdate(final UserDTO userDTO) throws BOException;

}

It provides an interface to the Service layer.

UserBOImpl.java

package com.stringer.bo;

import java.io.Serializable;
import java.util.Collection;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.stringer.dao.UserDAO;
import com.stringer.dto.UserDTO;
import com.stringer.model.Account;
import com.stringer.model.User;
import com.stringer.pojo.FetchType;
import com.stringer.pojo.GenericPojo;
import com.stringer.pojo.SortBy;
import com.stringer.pojo.SortOrder;
import com.stringer.util.ApplicationUtils;

@Service("userBO")
@Transactional(value = "transactionManager", timeout = 30, readOnly = true, propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE, rollbackFor = java.lang.Exception.class)
public class UserBOImpl extends BaseBOImpl implements UserBO {

	private User user = null;
	private @Autowired
	BaseDAO baseDAO;

	@Override
	@Transactional(readOnly = false)
	public void saveOrUpdate(final UserDTO userDTO) throws BOException {
		try {
			user = isNull(userDTO.getId()) ? ApplicationUtils
					.createInstance(User.class) : (User) userDAO
					.findEntityById(User.class, userDTO.getId());
			ApplicationUtils.copyProperties(userDTO, user);
			Long salt = ApplicationUtils.salt();
			user.setPassword(ApplicationUtils.uniquePassword(
					userDTO.getPassword(), salt));
			user.setSalt(salt);
			user.setIsObsolete(Boolean.FALSE);
			baseDAO.saveOrUpdate(user);
		} catch (Exception e) {
			throw new BOException(e);
		}
	}

}

This is the implementation class of UserBO.java.

BaseDAO.java

package com.stringer.dao;

package com.stringer.dao;

public interface BaseDAO {

	public <T> void saveOrUpdate(final T entity) throws DAOException;

}

}

This is an interface to the DAO layer.

BaseDAOImpl.java

package com.stringer.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;


@Repository("baseDAO")
public class BaseDAOImpl implements BaseDAO {

	private @Autowired
	SessionFactory sessionFactory;

	protected final Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public <T> void saveOrUpdate(final T obj) throws DAOException {
		try {
			getSession().saveOrUpdate(obj);
		} catch (Exception e) {
			throw new DAOException(e);
		}
	}

}

This is the implementation class of BaseDAO.java.

Finally the hibernate entity class,

User.java

package com.stringer.model;

import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Version;
import com.stringer.util.ApplicationConstants;

@Entity
@Table(name = ApplicationConstants.USER_TABLE, catalog = ApplicationConstants.CATALOG)
public class User implements Serializable {

	private String firstName, lastName, place, userName, password;
	private Integer age;
	private Long salt, id, version;
	private Boolean isObsolete;

	public void setIsObsolete(Boolean isObsolete) {
		this.isObsolete = isObsolete;
	}

	public Boolean getIsObsolete() {
		return isObsolete;
	}

	@Column(unique = true, nullable = false)
	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	@Column(unique = true, nullable = false)
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Column(unique = true, nullable = false)
	public Long getSalt() {
		return salt;
	}

	public void setSalt(Long salt) {
		this.salt = salt;
	}

	@TableGenerator(name = ApplicationConstants.GENERATOR_NAME, table = ApplicationConstants.GENERATOR_TABLE, pkColumnName = ApplicationConstants.GENERATOR_PK_COLUMN_NAME, valueColumnName = ApplicationConstants.GENERATOR_VALUE_COLUMN_NAME, pkColumnValue = ApplicationConstants.USER_TABLE, allocationSize = ApplicationConstants.USER_ALOC_SIZE, initialValue = ApplicationConstants.USER_INIT_VALUE)
	@GeneratedValue(strategy = GenerationType.TABLE, generator = ApplicationConstants.GENERATOR_NAME)
	@Id
	@Column(unique = true, nullable = false, updatable = false)
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	@Column(nullable = false)
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	@Column
	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Column
	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Column
	public String getPlace() {
		return place;
	}

	public void setPlace(String place) {
		this.place = place;
	}

	@Version
	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}
}

The folder structure and the list of libraries used are given below.

Stringer_1
Stringer_2

  • antlr-2.7.6
  • aopalliance
  • asm-3.2
  • asm-attrs
  • cglib-2.2.3
  • commons-beanutils-1.8.3
  • commons-collections-2.1.1
  • commons-fileupload-1.2.1
  • commons-io-1.3.2
  • commons-lang3-3.1
  • commons-logging-1.0.4
  • commons-logging-1.1.1
  • dom4j-1.6.1
  • ehcache-1.2.3
  • ejb3-persistence
  • freemarker-2.3.15
  • hibernate3
  • hibernate-annotations
  • hibernate-commons-annotations
  • hibernate-entitymanager
  • hibernate-tools
  • javassist
  • jdbc2_0-stdext
  • jta
  • log4j-1.2.14
  • mysql-connector-java-5.1.6-bin
  • ognl-2.7.3
  • spring-aop-3.2.1.RELEASE
  • spring-aspects-3.2.1.RELEASE
  • spring-beans-3.2.1.RELEASE
  • spring-build-src-3.2.1.RELEASE
  • spring-context-3.2.1.RELEASE
  • spring-context-support-3.2.1.RELEASE
  • spring-core-3.2.1.RELEASE
  • spring-expression-3.2.1.RELEASE
  • spring-instrument-3.2.1.RELEASE
  • spring-instrument-tomcat-3.2.1.RELEASE
  • spring-jdbc-3.2.1.RELEASE
  • spring-jms-3.2.1.RELEASE
  • spring-orm-3.2.1.RELEASE
  • spring-oxm-3.2.1.RELEASE
  • spring-struts-3.2.1.RELEASE
  • spring-test-3.2.1.RELEASE
  • spring-tx-3.2.1.RELEASE
  • spring-web-3.2.1.RELEASE
  • spring-webmvc-3.2.1.RELEASE
  • spring-webmvc-portlet-3.2.1.RELEASE
  • struts2-core-2.1.8.1
  • struts2-spring-plugin-2.1.8
  • xwork-core-2.1.6

Conclusion

So far we were going through an example which integrates struts, spring and hibernate frameworks. I hope all of you get to know the example well and expected to try out this.

Thanks.

Custom Converter in Struts2

Hi friends,

Converters are much needed and useful tools while coding. Fortunately Struts2 supports several type converters. Listing few of them.

1) String
2) boolean / Boolean
3) char / Character
4) int / Integer, float / Float, long / Long, double / Double

5) dates – uses the SHORT format for the Locale associated with the current request
6) arrays – assuming the individual strings can be converted to the individual items
7) collections – if not object type can be determined, it is assumed to be a String and a new ArrayList is created.

We can get more tips from here. Also see the java documentation about TypeConverter interface here.

Thanks to Strtus2. But, lots of things equivalent to nothing. What will we do, if we are not satisfied with these built in converters, or what will we do if we want to work on our own data types? Yes, we need “indigenous converters”. Here I’m telling the work around for creating and implementing custom converters.

Converters can be created by

1) Extending org.apache.struts2.util.StrutsTypeConverter class and overriding its two abstract methods.
2) Implementing ognl.TypeConverter interface and overriding its one and only one method.

Though I am new to the conversion, I believe both StrutsTypeConverter class and TypeConverter interface do the same job.

Anyway let’s come to the point. It’s time to create our own converter.

Step 1: Create a JSP file, input.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        <s:form action="convert">
            <s:textfield name="pojo.time" label="Enter time in 24 hour format (hh:mm)"/>
            <s:textfield name="pojo.amount" label="Enter amount in Dollars"/>
            <s:submit value="Convert"/>
        </s:form>
    </body>
</html>

Here I’m going to convert the time from 24 hour format to 12 hour format and amount from Dollars to Rupees.

Step 2: Create a POJO class, ConverterPojo.java

package com.eleventhHour.struts2;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
public class ConverterPojo {
    private String time, amount;
    public String getAmount() {
        return amount;
    }
    @TypeConversion(converter = "com.eleventhHour.struts2.CurrencyConverter")
    public void setAmount(String amount) {
        this.amount = amount;
    }
    public String getTime() {
        return time;
    }
    public void setTime(String time) {
        this.time = time;
    }
}

Here, we have to notice the annotation @TypeConversion(converter = “com.eleventhHour.struts2.CurrencyConverter”). This annotation specifies the location of one of my converters. literally, by using this annotation, I register my class com.eleventhHour.struts2.CurrencyConverter as a converter, and gets executed every time when setAmount(String amount) method is invoked.

Step 3: Create an action class, ConverterAction.java


package com.eleventhHour.struts2;

import com.opensymphony.xwork2.ActionSupport;


public class ConverterAction extends ActionSupport {

    private ConverterPojo pojo;

    public ConverterPojo getPojo() {
        return pojo;
    }
    public void setPojo(ConverterPojo pojo) {
        this.pojo = pojo;
    }
    @Override
    public String execute() {
        return SUCCESS;
    }
}

The action class contains nothing rather than the setter and getter of our ConverterPojo. An execute() method also there, which returns a SUCCESS of String type.

Before scrolling down, we have to know that “Everything moves on a HTTP request is treated as String by the protocol, i.e., numbers, booleans, dates etc. are treated as String by the protocol.”

Step 4: Create a Converter class, CurrecyConverter.java


package com.eleventhHour.struts2;

import com.opensymphony.xwork2.conversion.TypeConversionException;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;


public class CurrencyConverter extends StrutsTypeConverter {

    private static final double EXCHANGE_RATE = 55.11;
    private static final Locale LOCALE_INR = new Locale("en", "IN");
    private NumberFormat INR_FORMATTER = NumberFormat.getCurrencyInstance(LOCALE_INR);

    @Override
    public Object convertFromString(Map map, String[] strings, Class type) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String convertToString(Map map, Object obj) {
        try {
            String[] str = (String[]) obj;
            return INR_FORMATTER.format(EXCHANGE_RATE * Double.parseDouble(str[0]));
        } catch (Exception e) {
            throw new TypeConversionException(e);
        }
    }
}

Here, it can be seen that I extended my CurrencyConverter class from StrutsTypeConverter class and I overrode both of its abstract methods.

The method “public Object convertFromString(Map map, String[] strings, Class type)” executed when I convert a String type to any Object type and returns an object value. Here I’m not going to use this method.

The next method “public String convertToString(Map map, Object obj)” executed when I convert an Object to a String and which in turn returns that converted string value. Here, I use this method to convert my amount from Dollars to Rupees and format the amount to Indian locale.

Now, we have seen one way of registering converters. Another method of registration is the use of “properties” file, which will be discussed below.

Step 5: Create another Converter class, TimeConverter.java


package com.eleventhHour.struts2;

import java.lang.reflect.Member;
import java.text.SimpleDateFormat;
import java.util.Map;
import ognl.TypeConverter;

public class TimeConverter implements TypeConverter {

    private static final SimpleDateFormat INFORMAT = new SimpleDateFormat("HH:mm");
    private static final SimpleDateFormat OUTFORMAT = new SimpleDateFormat("hh:mm a z");

    @Override
    public Object convertValue(Map map, Object o, Member member, String string, Object obj, Class type) {
        try {
            String[] str = (String[]) obj;
            return OUTFORMAT.format(INFORMAT.parse(str[0]));
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        }

    }
}

Instead of extending StrutsTypeConverter, this time I implement ognl.TypeConverter interface. The interface is having one and only one method, which have been overridden above. This time, registration process of my converter goes on another way.

Step 6: Create a property file, ConverterAction-conversion.properties

pojo.time = com.eleventhHour.struts2.TimeConverter

There’s a naming convention that, the property file name should be like [action-class]-conversion.properties and should be put in the same package where our action-class resides. This is the another way how I register my class as a converter.

Step 7: Create a JSP file, output.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        Converted Time: <b><s:property value="pojo.time"/></b>
        <br/>
        Converted Amount <b><s:property value="pojo.amount"/></b>
    </body>
</html>
 

Step 8: Finally the mapping, struts.xml


<struts>
    <constant name="struts.devMode" value="true" />
    <package name="default" extends="struts-default">
        <action name="convert"
            class="com.eleventhHour.struts2.ConverterAction" method="execute">
            <result name="success">/output.jsp</result>
            <result name="input">/output.jsp</result>
        </action>
    </package>
</struts>

So, all the requisites for running our example are over. We just need to run the application. Go ahead. 🙂

input.jsp
input.jsp

output.jsp
output.jsp

Now, another question arises. What will we do if we want to declare a converter globally? Suppose, we have several packages and in each and every package, we have to use a Date object(for example) and need to convert a String value, entered by the user, to that Date object. As we have learned so far, we have to use either an annotation in every file, wherever the Date object is referenced, or use a property file in every package, both practices become a headache. So what we do is declaring a global converter for our entire application, which will be discussed below. Thanks again to Struts2 and xwork. We can easily do that. I am briefing it also here.

Step1: Create a JSP page, index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Converter Example</title>
    </head>
    <body>
        <s:form action="convert">
            <s:textfield name="pojo.uiDate" label="Date"/>
            <s:submit value="Press"/>
        </s:form>
    </body>
</html>

Step 2: Create a POJO class, ConverterPojo.java

package com.eleventhHour.struts2;


import java.util.Date;

public class ConverterPojo {

    private Date uiDate;

    public void setUiDate(Date uiDate) {
        this.uiDate = uiDate;
    }

    public Date getUiDate() {
        return uiDate;
    }
}

Here comes a difference from our previous POJO. The difference is that we created a Date object instead of a String object. Because our objective is to convert the date, entered by the user, from String type to java.util.Date type.

Step 3: Create an action class, ConverterAction.java

package com.eleventhHour.struts2;


import com.opensymphony.xwork2.ActionSupport;

public class ConverterAction extends ActionSupport {

    private ConverterPojo pojo;

    public ConverterPojo getPojo() {
        return pojo;
    }

    public void setPojo(ConverterPojo pojo) {
        this.pojo = pojo;
    }

    @Override
    public String execute() {
        return SUCCESS;
    }
}

No worry about it. It’s the same as our above action class.

Step 4: Create a converter class, DateConverter.java

package com.eleventhHour.struts2;

import com.opensymphony.xwork2.conversion.TypeConversionException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;

public class DateConverter extends StrutsTypeConverter {

    private static final DateFormat INPUT_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
    private static final DateFormat OUTPUT_FORMAT = new SimpleDateFormat("E, MMM dd, yyyy");

    @Override
    public Object convertFromString(Map context, String[] values, Class clazz) {
        try {
            return INPUT_FORMAT.parse(values[0]);
        } catch (Exception e) {
            throw new TypeConversionException(e.getMessage());
        }
    }

    @Override
    public String convertToString(Map context, Object value) {
        try {
            return OUTPUT_FORMAT.format(value);
        } catch (Exception e) {
            throw new TypeConversionException(e.getMessage());
        }
    }
}

Here, the first method “public Object convertFromString(Map context, String[] values, Class clazz)” is called when the String value is converted to Date object. It happens when an HTML form is submitted. The second method “public String convertToString(Map context, Object value)” is called when a Date object is again converted to a String. It happens when the object gets printed on an HTML page.

Next we have to register this converter globally, for that we have to create a global property file.

Step 5: Create a property file, xwork-conversion.properties

java.util.Date=com.eleventhHour.struts2.DateConverter

From now on, all the Date objects of our application will be converted using our custom converter. There’s also a naming convention. The file name must be “xwork-conversion.properties” and should be put in our root package for the global view.

Step 6: Create a JSP file, result.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        Converted Date : <b><s:property value="pojo.uiDate"/></b>
    </body>
</html>

Step 7: Finally the mapping, struts.xml

<struts>
    <package name="default" extends="struts-default">
        <action name="convert" class="com.eleventhHour.struts2.ConverterAction" method="execute">
            <result name="success">/result.jsp</result>
            <result name="input">/result.jsp</result>
        </action>
    </package>
</struts>

index.jsp
index.jsp

result.jsp
result.jsp

Thanks.

Struts 2 Annotation

Hi friends,

Here I would like to share an information with all of you regarding Struts2. Somehow you have already come across this tip, still I just want to share it now. 🙂

This post is something about Struts2 annotation. Do you know or do you even think how to run a Struts2 application without its heart, ie, struts.xml? Do you know it’s possible of doing so? Yes we can, Alas!

Here it goes…

Step 1: Download struts2-convention-plugin-2.1.x and add it to your project library.
Step2: There are some naming conventions when we use this plugin.
Convention 1: The package name of our action class must contain struts, strtuts2, action or actions keyword.
Convention 2: Obey anyone of the following rule.
1. Our action class must implement com.opensymphony.xwork2.Action interface.
2. Our action class must extend com.opensymphony.xwork2.ActionSupport class.
3. Name of our action class must end with Action (eg: LoginAction, SearchAction.)

That’s all.

You can visit this page to know what happens behind this plugin and how it scans our action classes.

Finally I add a small stuff of code using annotation.

package com.testPkg.actions;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
@Namespace(value = "/")
public class testAction extends ActionSupport {

    @Action(value = "/test", results = {
        @Result(name = "success", location = "/home.jsp")
    })
    public String testMethod() {
        return SUCCESS;
    }
}

Now your application is safe to remove “struts.xml”. But just do remind in mind that we still need our filter configuration in the deployment descriptor (web.xml).

<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>

Thanks.