Friday, September 30, 2011

Weavers in Spring

When we use AOP in spring then it apply advises on join point cuts. Spring uses proxy objects to apply the advices on point cuts. Weavers in Spring decide when the advices will be applied to point cuts. There are three types of weaving 
1. Run Time weaving - Spring uses Run time weaving for its interceptors.
2. Load Time weaving - Spring supports load time weaving using aop namespace.
3. Compile Time Weaving -- which Spring does not support. AspectJ supports compile time weaving.


1. Run time weaving - Spring uses run time weaving and apply the advices/interceptors on proxy objects of target bean. The problem with this approach is that if one method is being called from other method inside the target bean then the advice will only be applied to main method which is being called by client.


Example --
Below is out target bean interface and implementation



package org.paandav.springblog;
public interface TargetBeanIfc {
public void methodOne();
public void methodTwo();
}



package org.paandav.springblog;
public class TargetBean implements TargetBeanIfc {
public void methodOne() {
System.out.println("Method One Called");
}
public void methodTwo() {
methodOne();
System.out.println("Method Two Called");
}
}



Below is the before advice



package org.paandav.springblog;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class BeforeMethodAdvice implements MethodBeforeAdvice {


@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("Before Method Advice is called for method :"
+ arg0.getName());
}
}


Spring Config XML file:



<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*Bean*" />
<property name="interceptorNames">
<list>
<value>beforeMethodAdvice</value>
</list>
</property>
</bean>
<bean id="targetBean" class="org.paandav.springblog.TargetBean"></bean>
<bean id="beforeMethodAdvice" class="org.paandav.springblog.BeforeMethodAdvice"></bean>


Test File :



package org.paandav.springblog;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Test {


public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"springBlogApplicationContext.xml");
TargetBeanIfc tb = (TargetBeanIfc) ctx.getBean("targetBean");
tb.methodTwo();


}
}


Result: When we run the Test.java file we get the following output



Before Method Advice is called for method :methodTwo
Method One Called
Method Two Called


So at looking at the above result we can see that Before advice has been called only one time i.e. on method which is being called from Test.java.
Since the method one is being called from inside the target bean so Spring did not apply the before advice because it apply advices on proxy which in turn call the target bean.


2. Load Time weaving: If we want to apply the advice on both method calls on TargetBean.java file then we will have to use the Load Time Weaver. It weaves the code at time of class loading. To use the Spring load time weaving we will have to configure the below XML and will have to write the pointcut and advices in java.


Below is the new LoadtimeAspect.java file



package org.paandav.springblog.loadtime;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;


@Aspect
public class LoadtimeAspect {


@Before(value="execution( * org.paandav.springblog.*.*(..))", argNames="jp")
public void beforeAdvice(JoinPoint jp)
{
System.out.println("Before Advice is called " + jp.toString());
}
}


create a new folder META-INF in classpath and create a new xml file named aop.xml with following contents



<!DOCTYPE aspectj PUBLIC
"-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="org.paandav.springblog.*" />
</weaver>
<aspects>
<aspect name="org.paandav.springblog.loadtime.LoadtimeAspect" />
</aspects>
</aspectj>


Below is the Spring configuration file



<context:load-time-weaver />
<bean id="targetBean" class="org.paandav.springblog.TargetBean">      
        </bean>


Below is the LoadTimeWaverTest.java file



package org.paandav.springblog.loadtime;


import org.paandav.springblog.TargetBeanIfc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class LoadTimeWaverTest {


public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"loadTimeSpringBlogApplicationContext.xml");
TargetBeanIfc tb = (TargetBeanIfc) ctx.getBean("targetBean");
tb.methodTwo();
}


}

Result: To run the LoadTimeWaverTest.java file please pass jvm argument as 


-javaagent:C:\WorkSp\OpenOnlineExam\WebContent\WEB-INF\lib\org.springframework.instrument-3.0.5.RELEASE.jar

When we run the LoadTimeWaverTest.java file we get the following output.

Before Advice is called execution(void org.paandav.springblog.TargetBean.methodTwo())
Before Advice is called execution(void org.paandav.springblog.TargetBean.methodOne())
Method One Called
Method Two Called

So we can see that this time advice has been applied on both of the methods because the advice code has been weaved at class load time.

3. Compile Time Weaving : Spring does not support compile time weaving but we can use AsectJ for compile time weaving. 










Saturday, September 24, 2011

Storing and Retrieving Images In DB Using Struts 2, Spring 3 and Hibernate 3

This tutorial will show how you can store images in Database and retrieve them from DB and display in browser using Struts 2, Spring and Hibernate step by step. I have used Oracle Database to store the data.


It uses Struts 2 for presentation layer and Spring to manage the business and DAO objects and Hibernate at DAO layer to access dabase.


To get all the required jar files and classpath setup please look at 
http://tiwarij2eeblog.blogspot.com/2011/01/integration-of-struts-20-spring-30-and.html


Step 1: Setup project in eclipse as given below.





Step 2: Copy all required jar files to WEB-INF/lib folder.
Step 3: Create VO object and hibernate mapping file.



package org.paandav.blog.vo;
import java.io.Serializable;
import java.sql.Blob;


public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private Long version;
private String firstName;
private String lastName;
private Blob image;
private String conTentType;
private String fileName;
private byte[] imageInBytes; // For external use
private int contentLength;// For external use


public int getContentLength() {
return contentLength;
}


public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}


public byte[] getImageInBytes() {
return imageInBytes;
}


public void setImageInBytes(byte[] imageInBytes) {
this.imageInBytes = imageInBytes;
}


public String getFileName() {
return fileName;
}


public void setFileName(String fileName) {
this.fileName = fileName;
}


public String getConTentType() {
return conTentType;
}


public void setConTentType(String conTentType) {
this.conTentType = conTentType;
}


public String getFirstName() {
return firstName;
}


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


public Blob getImage() {
return image;
}


public void setImage(Blob image) {
this.image = image;
}


public Long getId() {
return id;
}


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


public Long getVersion() {
return version;
}


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


public String getLastName() {
return lastName;
}


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


Hibernate Mapping file - User.hbm.xml



<?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.blog.vo">
<class name="User" table="BT_USER" optimistic-lock="version" lazy="false">
<id name="id" column="userId">
<generator class="native" />
</id>
<version name="version" type="long" />
<property name="firstName" length="50" type="string" />
<property name="lastName" length="50" type="string" />
<property name="image" type="blob" />
<property name="conTentType" length="300" type="string" />
<property name="fileName" length="300" type="string" />
</class>
</hibernate-mapping>


Step 4: Hibernate configuration file oracle_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>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:ORACLE</property>
<property name="connection.username">pradeep</property>
<property name="connection.password">pradeep</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">2</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">update</property>
<mapping resource="org/paandav/blog/vo/User.hbm.xml" />
</session-factory>
</hibernate-configuration>


Step 5: Create applicationContext.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Application context definition for Online Exam -->
<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">
<!-- END FOR LOGIC LAYER -->
<context:annotation-config />
<!-- Instruct Spring to retrieve and apply @AspectJ aspects which are defined 
as beans in this context (such as the CallMonitoringAspect below). -->
<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:oracle_hibernate.cfg.xml" />
</bean>
<bean id="userService" class=" org.paandav.bolg.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="org.paandav.blog.dao.UserDAOImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</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="SUPPORTS" isolation="READ_COMMITTED"
rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceLayerBeanMethods"
expression="execution(* org.paandav.bolg.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceLayerBeanMethods" />
</aop:config>
</beans>

Step 6: Create struts.xml file
<?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">

<result-types>
<result-type name="imageBytesResult"
class="org.paandav.blog.action.ImageBytesResult" />
</result-types>

<action name="addUserData" class="org.paandav.blog.action.UserAction">
<result name="input">
WEB-INF/jsp/imageUploader.jsp
</result>
</action>
<action name="displayFullImage" class="org.paandav.blog.action.FullImageAction">
<result name="input">
WEB-INF/jsp/dispalyFullImage.jsp
</result>
</action>

<action name="getImage" class="org.paandav.blog.action.GetImageAction">
<result name="imageBytesResult" type="imageBytesResult">
</result>
</action>
</package>

</struts>

Step 7: Create web.xml file
<?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/j2ee" xmlns:javaee="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/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
id="WebApp_9" version="2.4">
<display-name>Open Online Exam</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>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>

<error-page>
<error-code>404</error-code>
<location>/index.jsp</location>
</error-page>

</web-app>

Step 8: Struts 2 action classes and validation files.
UserAction-validation.xml
<!DOCTYPE validators PUBLIC
        "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
        "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
<field name="firstName">
<field-validator type="requiredstring">
<message key="First name is blank" />
</field-validator>
</field>
<field name="lastName">
<field-validator type="requiredstring">
<message key="Last name is blank" />
</field-validator>
</field>
</validators>

UserAction.java

package org.paandav.blog.action;

import java.io.File;
import java.io.FileInputStream;
import java.sql.Blob;
import java.util.List;

import org.hibernate.Hibernate;
import org.paandav.blog.vo.User;
import org.paandav.bolg.service.UserService;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class UserAction extends ActionSupport implements ModelDriven<User>,
Preparable {
private static final long serialVersionUID = 1L;
User user = new User();
private File file;
private String contentType;
private String filename;
private UserService userService;
private List<User> allUserList;
private String from;

public String getFrom() {
return from;
}

public void setFrom(String from) {
this.from = from;
}

public List<User> getAllUserList() {
return allUserList;
}

public void setAllUserList(List<User> allUserList) {
this.allUserList = allUserList;
}

public UserService getUserService() {
return userService;
}

public void setUserService(UserService userService) {
this.userService = userService;
}

public void setUpload(File file) {
this.file = file;
}

public void setUploadContentType(String contentType) {
this.contentType = contentType;
}

public void setUploadFileName(String filename) {
this.filename = filename;
}

@Override
public User getModel() {
return user;
}

@Override
public void validate() {
if ("INIT".equalsIgnoreCase(from)) {
clearErrors();
return;
}
if (file == null) {
addFieldError("upload", "Upload an image");
}
}

@Override
public String execute() throws Exception {

if ("INIT".equalsIgnoreCase(from)) {
return INPUT;
}
try {
if (file != null) {
Blob image = Hibernate.createBlob(new FileInputStream(file));
user.setImage(image);
user.setConTentType(contentType);
user.setFileName(filename);
}
userService.save(user);
allUserList = userService.getAllUsers();
addActionMessage("Data Saved successfully");
} catch (Exception ex) {
addActionError("Upload failed");
}
return INPUT;
}

@Override
public void prepare() throws Exception {
allUserList = userService.getAllUsers();

}

}

ImageBytesResult.java
package org.paandav.blog.action;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;

public class ImageBytesResult implements Result {

private static final long serialVersionUID = 1L;

@Override
public void execute(ActionInvocation invocation) throws Exception {
GetImageAction action = (GetImageAction) invocation.getAction();
HttpServletResponse response = ServletActionContext.getResponse();

response.setContentType(action.getContentType());
response.setContentLength(action.getContentLength());
response.getOutputStream().write(action.getImageInBytes());
response.getOutputStream().flush();
}

}

GetImageAction.java
package org.paandav.blog.action;

import org.paandav.blog.vo.User;
import org.paandav.bolg.service.UserService;

import com.opensymphony.xwork2.ActionSupport;

public class GetImageAction extends ActionSupport {

private static final long serialVersionUID = 1L;
private UserService userService;
private Long id;
private User user;

public Long getId() {
return id;
}

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

public UserService getUserService() {
return userService;
}

public void setUserService(UserService userService) {
this.userService = userService;
}

@Override
public String execute() throws Exception {

user = userService.getUserById(id);
return "imageBytesResult";
}

public byte[] getImageInBytes() {
return user.getImageInBytes();
}

public String getContentType() {
return user.getConTentType();
}

public String getContentDisposition() {
return "";
}

public int getContentLength() {
return user.getContentLength();
}

public int getBufferSize() {
return 1024;
}
}

FullImageAction.java
package org.paandav.blog.action;

import com.opensymphony.xwork2.ActionSupport;

public class FullImageAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private Long imageId;

public Long getImageId() {
return imageId;
}

public void setImageId(Long imageId) {
this.imageId = imageId;
}

@Override
public String execute() throws Exception {
return INPUT;
}
}

Step 9: Service layer java classes.
UserService.java
package org.paandav.bolg.service;

import java.util.List;

import org.paandav.blog.vo.User;

public interface UserService {
User save(User user) throws Exception;

List<User> getAllUsers() throws Exception;

User getUserById(long id) throws Exception;

}

UserServiceImpl.java
package org.paandav.bolg.service;

import java.util.List;

import org.paandav.blog.dao.UserDAO;
import org.paandav.blog.vo.User;

public class UserServiceImpl implements UserService {
private UserDAO userDao;

public UserDAO getUserDao() {
return userDao;
}

public void setUserDao(UserDAO userDao) {
this.userDao = userDao;
}

@Override
public User save(User user) throws Exception {

return userDao.save(user);
}

@Override
public List<User> getAllUsers() throws Exception {

return userDao.getAllUsers();
}

@Override
public User getUserById(long id) throws Exception {
return userDao.getUserById(id);
}
}

Step 10: DAO layer java classes

BaseDAO.java
package org.paandav.blog.dao;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class BaseDAO {
private SessionFactory sessionFactory;

public SessionFactory getSessionFactory() {
return sessionFactory;
}

public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

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

}

UserDAO.java
package org.paandav.blog.dao;

import java.util.List;

import org.paandav.blog.vo.User;

public interface UserDAO {

User save(User user) throws Exception;

List<User> getAllUsers() throws Exception;

User getUserById(long id) throws Exception;

}

UserDAOImpl.java
package org.paandav.blog.dao;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;

import org.paandav.blog.vo.User;

public class UserDAOImpl extends BaseDAO implements UserDAO {

@Override
public User save(User user) throws Exception {
getSession().save(user);
return user;
}

@SuppressWarnings("unchecked")
@Override
public List<User> getAllUsers() throws Exception {
List<User> allUsers = getSession().createQuery("from User").list();
return allUsers;
}

@Override
public User getUserById(long id) throws Exception {
User user = (User) getSession().get(User.class, new Long(id));
// Convert BLOB to byte array
user.setImageInBytes(getImageInBytes(user));
user.setContentLength((int) user.getImage().length());
return user;
}

public byte[] getImageInBytes(User user) {

if (user.getImage() == null) {
return new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
InputStream is = user.getImage().getBinaryStream();
byte[] buf = new byte[1024];
int i = 0;
while ((i = is.read(buf)) >= 0) {
baos.write(buf, 0, i);
}
is.close();
} catch (Exception ex) {
ex.printStackTrace();
}
return baos.toByteArray();
}
}



Step 11: jsp files in WEB-INF/jsp folder
imageUploader.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body style="font-family: verdana; margin-top: 3%;">
<div>
<table border=1 align="center">
<tr>
<td>First Name</td>
<td>Last Name</td>
<td>Photo</td>
<td>View full Image</td>
</tr>
<s:iterator value="allUserList">
<tr>
<td><s:property value="firstName" /></td>
<td><s:property value="lastName" /></td>
<td align="center"><img alt="" width="150" height="100"
src="getImage.action?id=<s:property value="id" />"></td>
<td><a href="displayFullImage.action?imageId=<s:property value="id" />" target="_new">View full Image</a></td>
</tr>
</s:iterator>
<s:form action="addUserData" theme="simple"
enctype="multipart/form-data">
<tr>
<td><font color="red"><s:fielderror fieldName="firstName" /></font>
<s:textfield name="firstName"></s:textfield></td>
<td><font color="red"><s:fielderror fieldName="lastName" /></font><s:textfield
name="lastName"></s:textfield></td>
<td colspan="3"><font color="red"><s:fielderror
fieldName="upload" /></font><s:file name="upload" label="file" /> <s:submit></s:submit></td>
</tr>
</s:form>
</table>
</div>
</body>
</html>


dispalyFullImage.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">


</head>
<body>
<table align="center">
<tr>
<td><img alt="" width="100%" height="100%"
src="getImage.action?id=<s:property value="imageId" />" /></td>
</tr>
</table>
</body>
</html>


Step 12: Now either deploy the application on Tomcat or Other Server or start server from eclipse and access the URL


http://localhost:8080/blogs_tutorials/addUserData.action?from=INIT


It will display a screen with upload options. Fill the data and upload the photos. All uploaded photos will be displayed on the same screen and you will see small uploaded images like below 


If you want to see the original size image click on View full Image link and it will display full image in new browser window like below.