Saturday, September 14, 2013

Don't Throw your logs -- Generate Electricity from Garbage

Per day all around the world terabytes of log files are being generated by software applications/products. If everything is going fine people generally keep them for some specific time and then they throw or shred the log files. If something goes wrong people look at log files and try to know the reason of failure or error.

Most of the time we see log files only in case of failure or errors in softwares. Let's explore these log files from other point of view where we can use these log files to extract some information which we can use to reduce the errors, improve stability, improve performance of softwares etc.

Production issues have always been a nightmare for most of the organizations. People do firefighting in production to fix and resolve the issues to make the application stable and working. Whenever an error or issue occurred in software application writes this error and description in log files. People see the log file fix them and wait for other issue about to happen. By exploring these log files we can extract the information about occurrences of errors in software and try to come up with generic solution or alerting mechanism in the application so either we can reduce the occurrences of errors or take some preventive actions if we get alert when they are going to happen.

There are some situations where we get some repetitive errors which are related to programing errors like null pointer errors. In this case if we get the information about the error points in software then we can rewrite these modules or program so we can reduce these such types of programming errors in production.

There are some situations where we need some alarm points in softwares so if we can develop a utility for our softwares which can alert us when this error is about to happen like Out Of Memory errors.

There are some situations where we can collect information abut time taken by single use cases like create customer or deposit transaction and if time taken by actions are not up to mark we can improve the performance.

There are a lot of situations or errors can be avoided/reduced by taking some preventive actions and log files can up us to do that. But How?

We can use these log files to explore the occurrences of errors issues using some home grown programs or available open source softwares. To explore and extract the log files we can use the below steps.

  1. Collect one years (up to what ever time you can) all log files of application at single place or directory.
  2. Write a program in any language which is suitable for you which scans these log files one by one and generate a report where it will display number of counts for all errors.
  3. Now we are having data about the errors and their occurrences throughout the year.
  4. Identify the most occurred errors and try to analyze if we can avoid or reduce the error by improving our code, increasing vigilance or alerting.
  5. Some issues like connection leaks, memory leaks, null pointer exception can be avoided by doing some micro level investigation on application so we can improve our application to avoid these errors.
  6. There are some errors which can be avoided by alerting the user before they are about to happen like Out Of memory Errors. We can create an small application or utility which can run along with application and if memory utilization to reaching up to a specified level then it will alert to users that Out Of Memory Error can happen in next minutes. There may be another situation where application is using database connection more than a specified error and application can crash with connection not available error.


So using the above steps where we can identify the error points and their occurrences and improve our application to handle or avoid such situations and reduce the chances of application unavailability.

This is just like generating electricity from garbage.


Next post we will explore how Apache Hadoop can help us to know the error patterns in our log files. 

Tuesday, October 25, 2011

A website architecture using Tiles, DWR and Hibernate

Goal: Create a simple architecture using Java/JEE technology/frameworks which can be used to develop websites quickly.


Technology Used: 
   1. JSP, Apache Tiles, HTML, CSS, JavaScript : Render user interface, 
       Use Tiles template. 
       (http://tiwarij2eeblog.blogspot.com/2011/10/using-tiles-2-without-struts.html).
   2. DWR (Ajax Framework): Fetch data from server and render on UI using 
       javaScript.
   3. Hibernate: Add/Update/Search Data from Database.





Tuesday, October 11, 2011

Pagination Using Struts 2 and Hibernate 3.0

This blogs tell how to do pagination using Struts 2.0, Spring 3.0 and Hibernate 3.0 step by step. This blog is extension of http://tiwarij2eeblog.blogspot.com/2011/09/storing-and-retrieving-images-in-db.html. Here we will create search page which will display results with pagination.


Step 1: Setup eclipse as per above link and add some data to DB.
Step 2: Create anew Java bean as below



public class UserSearchBean {


private int selectedPageNumber;
private int totalPages;
private int serialNumberAddfactor;
private int noOfRecordsPerPage = 3; // Default


private String from;


// Search creteria
private String firstName;
private String lastName;


private List<User> selectedUserList;


       // Write Setters and getters


Step 3: Create a new java Action class as below



package org.paandav.blog.action;


import org.paandav.bolg.service.UserService;


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


public class SearchUserAction extends ActionSupport implements
ModelDriven<UserSearchBean> {


private static final long serialVersionUID = 1L;
private UserSearchBean model = new UserSearchBean();
private UserService userService;


public UserService getUserService() {
return userService;
}


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


public void setModel(UserSearchBean model) {
this.model = model;
}


@Override
public String execute() throws Exception {
if (!"INIT".equals(getModel().getFrom())) {
userService.getPaginatedResultForUserSearch(getModel());
}
return INPUT;
}


@Override
public UserSearchBean getModel() {
return model;
}


}


Step 4: Create new method getPaginatedResultForUserSearch in UserService.java and write implementation as below

UserService.java
UserSearchBean getPaginatedResultForUserSearch(UserSearchBean usb)
throws Exception;
UserServiceImpl.java
@Override
public UserSearchBean getPaginatedResultForUserSearch(UserSearchBean usb)
throws Exception {
return userDao.getPaginatedResultForUserSearch(usb);
}

Step 5: Create new method UserDAO and implement it as belwo

UserDAO.java
UserSearchBean getPaginatedResultForUserSearch(UserSearchBean usb)
throws Exception;

UserDAOImpl.java

@Override
public UserSearchBean getPaginatedResultForUserSearch(UserSearchBean usb)
throws Exception {

List<User> selectedUser = null;
Criteria crit = getCreteria(usb);
int totalNoOfRecords = (Integer) crit.setProjection(
Projections.rowCount()).uniqueResult();
int startIndex = 0;
int totalPages = 1;

if (totalNoOfRecords > usb.getNoOfRecordsPerPage()) {
double noOfPages = (double) totalNoOfRecords
/ (double) usb.getNoOfRecordsPerPage();
totalPages = (int) noOfPages;
if (noOfPages % totalPages > 0.0) {
totalPages++;
}
}

if (usb.getSelectedPageNumber() > 1) {
startIndex = usb.getNoOfRecordsPerPage()
* (usb.getSelectedPageNumber() - 1);
usb.setSerialNumberAddfactor(usb.getNoOfRecordsPerPage()
* (usb.getSelectedPageNumber() - 1));
}

usb.setTotalPages(totalPages);

crit = getCreteria(usb);
crit.setFirstResult(startIndex);
crit.setMaxResults(usb.getNoOfRecordsPerPage());

selectedUser = crit.list();
usb.setSelectedUserList(selectedUser);

return usb;
}

private Criteria getCreteria(UserSearchBean usb) {
Criteria criteria = getSession().createCriteria(User.class);
if (usb.getFirstName() != null && !"".equals(usb.getFirstName())) {
criteria.add(Restrictions.ilike("firstName", usb.getFirstName()));
}
if (usb.getLastName() != null && !"".equals(usb.getLastName())) {
criteria.add(Restrictions.ilike("lastName", usb.getLastName()));
}

return criteria;
}


Step 6: Now create a new actiom mapping in struts.xml as below

<action name="searchUser" class="org.paandav.blog.action.SearchUserAction">
<result name="input">
WEB-INF/jsp/userSearchPage.jsp
</result>
</action>
Step 7: Now create anew jsp file userSearchPage.jsp as below
<%@ 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 style="font-family: verdana;">
<s:form action="searchUser" theme="simple">
<table align="center" border=1>
<tr bgcolor="aqua">
<td>First Name <s:textfield name="firstName"></s:textfield></td>
<td>Last Name <s:textfield name="lastName"></s:textfield></td>
<td colspan="3"><s:submit value="Search User"></s:submit></td>
</tr>
<s:if test="totalPages > 1">
<tr bgcolor="lightgrey">
<td colspan="5">Page No :<s:iterator value="totalPages.{#this}"
status="stat">
<s:submit value="%{#stat.count}" name="selectedPageNumber"></s:submit>
</s:iterator></td>
</tr>
</s:if>
<s:if test="selectedUserList != null && selectedUserList.size > 0">
<tr bgcolor="aqua">
<th>S.N</th>
<th>First Name</th>
<th>Last Name</th>
<th>Content type</th>
<th>File Name</th>
</tr>
<s:iterator value="selectedUserList" status="stat">
<tr bgcolor="#FFF288">
<td><s:property value="#stat.count + serialNumberAddfactor" /></td>
<td><s:property value="firstName" /></td>
<td><s:property value="lastName" /></td>
<td><s:property value="conTentType" /></td>
<td><s:property value="fileName" /></td>
</tr>
</s:iterator>
</s:if>
</table>
</s:form>
</body>
</html>

Step 9: Now start the server and access the URL

You will see a screen like below


Now if you don't fill any data in boxes it will display all data in table with pagination like belwo




Now you can try with filling some criteria and search.


Saturday, October 8, 2011

Using Tiles 2 without Struts

Sometimes we need tiles functionality and don't want to use Struts. Tiles is very useful when we want to use data access by other frameworks like DWR. This blog displays step by step procedure for using Apache Tiles 2 Without Struts.


Step 1: Create a new dynamic web project in eclipse and put the below jar files to WEB-INF/lib folder (you can exclude dwr jar files).




Step 2: Now configure the tiles listener and tiles servlet in web.xml file as below.



<context-param>
<param-name>org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG
                </param-name>
<param-value>/WEB-INF/tiles.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.tiles.web.startup.TilesListener</listener-class>
</listener>
<servlet>
<servlet-name>Tiles Dispatch Servlet</servlet-name>
<servlet-class>org.apache.tiles.web.util.TilesDispatchServlet</servlet-class>
</servlet>
<servlet-mapping> 
         <servlet-name>Tiles Dispatch Servlet</servlet-name> 
         <url-pattern>*.tiles</url-pattern> 
</servlet-mapping>

Step 3: Now create a template jsp file like below template.jsp in WebContent folder

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Test</title>
</head>
<body style="font-family: verdana;">
<table align="center" border=0 height="100%" width="100%">
<tr height="5%">
<td><tiles:insertAttribute name="header" /></td>
</tr>
<tr height="5%">
<td><tiles:insertAttribute name="menu" /></td>
</tr>
<tr height="75%">
<td><tiles:insertAttribute name="body" /></td>
</tr>
<tr height="5%" >
<td><tiles:insertAttribute name="footer" /></td>
</tr>
</table>
</body>
</html>

Step 4: Create folur other jsp  files like header.jsp, menu.jsp, home.jsp and footer.jsp in WebContent folder

Step 5: Now create a tiles.xml file inside WEB-INF folder and create template and other pages like below

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

<tiles-definitions>
<definition name="outerLayout" template="teplate.jsp">
<put-attribute name="header" value="header.jsp" />
<put-attribute name="menu" value="menu.jsp" />
<put-attribute name="body" value="" />
<put-attribute name="footer" value="footer.jsp" />
</definition>
<definition name="homePage" extends="outerLayout">
<put-attribute name="body" value="home.jsp" />
</definition>
</tiles-definitions>

Step 6: Now start web-server and use the URL like 

http://localhost:8080/TGReality/homePage.tiles

all the pages will be called by using the name of tiles definition like homePage.tiles.

don't change *.tiles in web.xml.




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.