Sunday, January 16, 2011

Integration of Struts 2.0 Spring 3.0 and Hibernate 3.5


Integration of Struts 2.0 Spring 3.0 and Hibernate 3.5

This blog is a proof of concept of the previous blog 
http://tiwarij2eeblog.blogspot.com/2010/12/design-and-develop-reference-j2ee.html
It will use Struts 2.0 for presentation layer, Spring 3.0 to integrate presentation, service, business and integration layer pojo object. We will use P2I(program to interface) strategy to develop pojos on different layer
I will use a simple use case of user login.
Xml files will be used to configure struts, spring and hibernate along with annotations to plug the pojos through spring. We will develop this step by step.

Step 1. Download required jar files from the below sources

Install JDK 6 and MySQL databse on your machine if required.

Step 2: create a web project in Eclipse and add required jar file as shown on below figure



 

Step 3: Now create a model which is simple POJO for User information

package org.paandav.beans;

import java.io.Serializable;

public class User implements Serializable
{
private static final long serialVersionUID = 4150223136736045491L;
private Long id; //Primary key
private Long version; //To be used by hibernate for optimistic locking
private String username; //Username
private String password; //Password
}

Now create hibernate mapping file User.hbm.xml for this POJO and put it in the same package folder with User
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="org.paandav.beans">
<class name="User" table="OE_USER" optimistic-lock="version">
<id name="id" column="userId">
<generator class="native" />
</id>
<version name="version" type="long" />
<property name="username" length="50" type="string" unique="true"
not-null="true" />
<property name="password" length="20" type="string" />
</class>
</hibernate-mapping>

According to this hibernate mapping file the table name will be OE_USER in database userId will be primary key and will be generated and assigned by hibernate automatically and version column will be used by hibernate to keep track of record version. Don't modify or add values for userId and version in your java code whereas they can be used for read only purpose.

Step 4: Prepare hibernate configuration file mysql_hibernate.cfg.xml as below and put it in src folder.
<?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="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/pradeep</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.pool_size">2</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="show_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<mapping resource="org/paandav/beans/User.hbm.xml" />
</session-factory>
</hibernate-configuration>

In the above file we have also mapped our User models. All models will be mapped here. Since we are going to use MySQL so using MySQLDialect. Change connection.url, connection.username and connection.password according to your local installation and setup.

Step 5: Prepare DAO interface and implementation for Integration layer. Make sure that there will be no transaction management this layer like begin commit or rollback because transaction management will be handled by service layer.

package org.paandav.daolayer;
import java.util.List;
import org.paandav.beans.User;

public interface UserDAOIfc
{
public User getByPk(Long id) throws DAOException;
public User create(User user) throws DAOException;
public Void delete(User user) throws DAOException;
public User read(User user) throws DAOException;
public User update(User user) throws DAOException;
}

public class UserDAOImpl extends BaseDAO implements UserDAOIfc
{

protected SessionFactory sessionFactory;
@Resource(name = "sessionFactory")//Spring will inject object
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
@Override
public User create(User user) throws DAOException{
this.sessionFactory.getCurrentSession().save(user);
return user;
}
@Override
public Void delete(User user) throws DAOException{
this.sessionFactory.getCurrentSession().delete(user);
return null;
}
@Override
public User read(User user) throws DAOException {
User retuser = null;
Query query = this.sessionFactory.getCurrentSession().createQuery(
" from User where username = :name");
query.setString("name", user.getUsername());
retuser = (User) query.uniqueResult();
return retuser;
}
@Override
public User update(User user) throws DAOException{
this.sessionFactory.getCurrentSession().update(user);
return user;
}
@Override
public User getByPk(Long id) throws DAOException{
return (User) this.sessionFactory.getCurrentSession().get(User.class, id);
}
}

Step 6: Make Business layer interface and implementation POJO classes. For clearity I have used only method.
package org.paandav.domainlayer;

import java.util.List;

import org.paandav.beans.User;
import org.paandav.servicelayer.ServiceLayerException;

public interface UserDomainLogicIfc
{ public User getUser(User user) throws ServiceLayerException;
}

package org.paandav.domainlayer;

import java.util.List;

import javax.annotation.Resource;

import org.paandav.beans.User;
import org.paandav.daolayer.DAOException;
import org.paandav.daolayer.UserDAOIfc;
import org.paandav.servicelayer.ServiceLayerException;

public class UserDomainLogicImpl extends DomainLogicBase
implements
UserDomainLogicIfc
{
private UserDAOIfc userDao;
@Resource(name = "userDaoBeanProxy") // Will be injected by Spring
public void setUserDao(UserDAOIfc userDao){
this.userDao = userDao;
}
@Override
public User getUser(User user) throws ServiceLayerException{
User ret = null;
try{
ret = userDao.read(user);
//other business logic
}
catch (DAOException dex){
throw new ServiceLayerException(dex.getMessage());
}
return ret;
}
}

Step 7: Make service layer interfaces and implementation classes. Make sure that method here will not have any logic. Method in this will be entry and exit point for one single use case flow. If anything wrong happens during processing at other layers transactions will be rolledback at this layer.

package org.paandav.servicelayer;

import org.paandav.beans.User;

public interface UserServiceIfc
{
public User getUser(User user)throws ServiceLayerException;
}

package org.paandav.servicelayer;

import javax.annotation.Resource;

import org.paandav..beans.User;
import org.paandav.domainlayer.UserDomainLogicIfc;

public class UserServiceImpl implements UserServiceIfc
{

private UserDomainLogicIfc userLogic;

@Resource(name = "loginLogicBeanProxy") // Will be injected by Spring
public void setUserLogic(UserDomainLogicIfc userLogic)
{
this.userLogic = userLogic;
}
@Override
public User getUser(User user) throws ServiceLayerException
{
return userLogic.getUser(user);
}
}

Step 8: Now we are having all our POJOs now we are going to plug service, business and dao layer using spring configuration file. Below is the applicationContexxt.xml file. Put this file in WEB-INF folder

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


<context:annotation-config />
<aop:aspectj-autoproxy />
<!-- Hibernate plugin code -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
<property name="configLocation" value="classpath:mysql_hibernate.cfg.xml" />
</bean>

<!-- Servive layer beans -->
<bean id="loginBusinessInterfaceBean" class="org.paandav.servicelayer.UserServiceImpl" />
<bean id="loginBusinessInterfaceBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="org.paandav.servicelayer.UserServiceIfc" />
<property name="target" ref="loginBusinessInterfaceBean" />
</bean>

<!-- Domains Logic POJO beans-->
<bean id="loginLogicBean" class="org.paandav.domainlayer.UserDomainLogicImpl" />
<bean id="loginLogicBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"
value="org.paandav.domainlayer.UserDomainLogicIfc" />
<property name="target" ref="loginLogicBean" />
</bean>

<!-- DAO Classes -->
<bean id="userDaoBean" class="org.paandav.daolayer.UserDAOImpl" />
<bean id="userDaoBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="org.paandav.daolayer.UserDAOIfc" />
<property name="target" ref="userDaoBean" />
</bean>

<!-- Transaction Manger -->

<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW" isolation="READ_COMMITTED"
rollback-for="Exception" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="serviceLayerBeanMethods"
expression="execution(* org.paandav.servicelayer.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceLayerBeanMethods" />
</aop:config>
</beans>

According to above configuration file we are using transaction management for all methods of service layer classes.

This file plug code only for service layer, business layer and DAO layer. We will plug service layer POJO to presentation layer in struts.config file. Put the above file into WEB-INF folder.

Step 9: Write Struts 2.0 Action class POJO

package org.paandav.presentationlayer;

import org.paandav.beans.User;
import org.springframework.beans.BeanUtils;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.interceptor.SessionAware;

public class LoginAction extends ActionSupport implements SessionAware
{
protected Map session = null;
protected UserServiceIfc userServiceIfc;

private static final long serialVersionUID = 8341905716163967426L;
private String username;
private String password;

@Resource(name = "loginBusinessInterfaceBeanProxy")//Spring will inject
public void setUserServiceLayerInterface(
UserServiceIfc userServiceIfc){
this.userServiceIfc = userServiceIfc;
}
@Override
public void setSession(Map session){
this.session = session;

}

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;
}
@Override
public String execute() throws Exception{
String result = ERROR;
User user = new User();
BeanUtils.copyProperties(this, user);
user = userServiceIfc.getUser(user);

if (user != null && user.getPassword().equals(getPassword())){
user.setLogged(true);
user.setPassword(null);
session.put("user", user);
result = SUCCESS;
}
else{
session.remove("user");
addActionError(getText("loginfailed.message"));
}
return result;
}
}


Step 10: Now we are going to plug service layer spring objects to struts action PJO. Create a new struts.config file and put it in src folder

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
<constant name="struts.devMode" value="false" />
<constant name="struts.custom.i18n.resources" value="global" />
<constant name="struts.objectFactory" value="spring" />

<package name="commonPackage" namespace="/" extends="struts-default">
<action name="login">
<result>/WEB-INF/jsp/login/login.jsp
</result>
</action>
<action name="loginAction"
class="org.paandav.presentationlayer.LoginAction">
<result name="success">WEB-INF/jsp/login/loginSuccess.jsp
</result>
<result name="error">/WEB-INF/jsp/login/login.jsp
</result>
<result name="input">/WEB-INF/jsp/login/login.jsp
</result>
</action>
</package>
</struts>

Step 11: Create index.jsp, login.jsp, loginSuccess.jsp files.
Index.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<META HTTP-EQUIV="Refresh" CONTENT="0;URL=login.action">
</head>

<body>
<p>Loading ...</p>
</body>
</html>
WEB-INF\login\login.jsp

<%@ taglib prefix="s" uri="/struts-tags"%>
<s:head />
<html>
<body style="font-family: verdana; font-size: 5px;" onload="">
<table align="center" height="95%" border="0" width="90%" bgcolor="blue"
cellspacing="1" cellpadding="3">
<tr>
<td align="center" bgcolor="#F9FAD8"><s:form action="loginAction">
<s:actionerror />
<s:textfield name= "username" key="username.label" required="true" maxlength="10"></s:textfield>
<s:password name="password" key="password.label" required="true" maxlength="10"></s:password>
<s:submit key="login.label"></s:submit>
</s:form></td>
</tr>
</table>
</body>
</html>


WEB-INF\login\loginSuccess.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>

<body>
<p>Login successfully....</p>
</body>
</html>

Step 12: Now configure web.xml file. Configure

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" 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>Integration</display-name>
<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>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

Step 13: Deploy it on Tomcat and start the server and call the index.jsp file from browser.