Web Design Patterns at Work with Pagination

This example demonstrates the use of common web design patterns and gives a solution for pagination. I am using JSF as the MVC framework for this example.
I am using the authors database on mySQL which I have used in the previous examples. Refer the simple entity bean example on details of creation of the authors database and setting up of the mySQL datasource.
You need to set JBOSS_HOME and ANT_HOME before you can get on with this example.
You would also need to download the jsf jars listed under lib/jsfJars from the relevant website.

My work folder has the following structure:
build.xml

<build>
     <classes>
     <dist>
<webRoot>
     <web-INF>
         faces-config.xml
         web.xml
         html_basic.tld
         jsf_core.tld
<lib>
     <jsfJars>
         commons-beanutils.jar
         commons-collections.jar
         commons-digester.jar
         commons-logging.jar
         jsf-api.jar
         jsf-impl.jar
<src>
     <web>
         <mattiz>
              <pages>
                       author.jsp
                       authorAdded.jsp
                       authorPage.jsp
     <java>
         <com>
              <mattiz>
                       <beans>
                           AuthorDTO.java
                       <businessDelegates>
                           MattizDelegate.java
                       <dao>
                           MattizDAO.java
                       <exception>
                           MattizException.java
                       <web>
                           <managedBeans>
                                 AuthorBean.java

Let me list the web pages first:
author.jsp :


<%@page contentType="text/html"%>
<%@ taglib uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<html>
<head>
<title>author.jsp</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
	<f:view>
		<h:form>
			<h:panelGrid id="grid1" columns="2" border="0" width="100%">
				<h:outputText id="text1"
					style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;"
					value="Enter ISBN Code    :" />
				<h:inputText style="color: automatic; background: iceblue;"
					value="#{authorBean.isbnCode}" />
				<h:outputText id="text2"
					style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;"
					value="Enter Author Name:" />
				<h:inputText style="color: automatic; background: iceblue;"
					value="#{authorBean.author}" />
			</h:panelGrid>
			<h:panelGrid id="grid3" columns="2" border="0" width="100%">
				<h:commandButton value="Add" type="submit"
					action="#{authorBean.addAuthor}" />
			</h:panelGrid>
			<hr />
			<h:panelGrid id="grid5" columns="2" border="0" width="100%">
				<h:outputText id="text3"
					style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;"
					value="Paginate Author Details " />
			</h:panelGrid>
			<h:panelGrid id="grid6" columns="2" border="0" width="100%">
				<h:commandButton value="Paginate" type="submit"
					action="#{authorBean.displayAuthors}" />
			</h:panelGrid>
		</h:form>
	</f:view>
</body>
</html>

authorPage.jsp:

<%@page contentType="text/html"%>
<%@ taglib uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<html>
<head>
<title>authorPage.jsp</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
	<f:view>
		<h:form>
			<%
				if (request.getAttribute("count") != null) {
							int count = Integer.parseInt(request.getAttribute(
									"count").toString());
							int start = Integer.parseInt(request.getAttribute(
									"start").toString());
							int end = Integer.parseInt(request.getAttribute("end")
									.toString());
							int totRows = Integer.parseInt(request.getAttribute(
									"totRows").toString());
			%>
			<h:dataTable width="100%" border="0" id="values"
				value="#{authorBean.authorsList}" var="authorDTO"
				style="font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px;font-weight: bold; color: #000000">
				<h:column>
					<h:outputText value="#{authorDTO.isbnCode}"
						style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;" />
				</h:column>
				<h:column>
					<h:outputText value="#{authorDTO.author}"
						style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;" />
				</h:column>
			</h:dataTable>
			<%
				if (!(start == 1)) {
			%>
			<h:commandLink action="#{authorBean.displayAuthors}">
				<h:outputText value="PREV"
					style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;" />
				<f:param name="begin" value="#{start-10}" />
				<f:param name="finish" value="#{end-10}" />
				<f:param name="no" value="#{count}" />
				<f:param name="change" value="decr" />
			</h:commandLink>
			<%
				}
							if (!(count == totRows)) {
			%>
			<h:commandLink action="#{authorBean.displayAuthors}">
				<h:outputText value="NEXT"
					style="color: #000099;font-family: Verdana, Arial, Helvetica, sans-serif;font-size: 10px;font-weight: bold;" />
				<f:param name="begin" value="#{start+10}" />
				<f:param name="finish" value="#{end+10}" />
				<f:param name="no" value="#{count}" />
				<f:param name="change" value="incr" />
			</h:commandLink>
			<%
				}
						}
			%>
		</h:form>
	</f:view>
</body>
</html>

authorAdded.jsp :

<%@page contentType="text/html"%>
<%@ taglib uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>Author Added Page</title>

<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
	<f:view>
Author added successfully to database
<h:form>
			<h:commandButton value="Back" type="submit" action="back" />
		</h:form>
	</f:view>
</body>
</html>

Next I list the java classes:

AuthorBean.java is the backing bean for all the jsp pages:

package com.mattiz.web.managedBeans;

import java.util.ArrayList;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import com.mattiz.Exception.MattizException;
import com.mattiz.beans.AuthorDTO;
import com.mattiz.businessDelegates.MattizDelegate;

public class AuthorBean {
	private String isbnCode;
	private String author;
	private HttpServletRequest request;
	private HttpSession session;
	ArrayList authorsList;

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getIsbnCode() {
		return isbnCode;
	}

	public void setIsbnCode(String isbnCode) {
		this.isbnCode = isbnCode;
	}

	public String addAuthor() {
		AuthorDTO authorDTO = new AuthorDTO(getIsbnCode());
		authorDTO.setAuthor(getAuthor());
		MattizDelegate mattizDelegate = new MattizDelegate();
		try {
			mattizDelegate.addAuthor(authorDTO);
		} catch (MattizException me) {

		}
		return "authorAdded";
	}

	public String displayAuthors() {
		FacesContext context = FacesContext.getCurrentInstance();
		request = (HttpServletRequest) context.getExternalContext()
				.getRequest();
		session = request.getSession();
		MattizDelegate mattizDelegate = new MattizDelegate();
		try {
			int start = 1;
			int end = 10;
			if (request.getParameter("begin") != null) {
				start = Integer.parseInt(request.getParameter("begin"));
			}
			if (request.getParameter("finish") != null) {
				end = Integer.parseInt(request.getParameter("finish"));
			}
			authorsList = mattizDelegate.getAuthors(start, end);
			int count = 0;
			String change = "incr";
			if (request.getParameter("no") != null) {
				count = Integer.parseInt(request.getParameter("no"));
			}
			int totalRows = mattizDelegate.getNumberOfRows();
			if (request.getParameter("change") != null) {
				change = request.getParameter("change").toString();
			}
			if (change.equals("incr")) {
				request.setAttribute("count",
						new Integer(count + authorsList.size()));
			} else if (change.equals("decr")) {
				request.setAttribute("count", new Integer(end));
			}
			request.setAttribute("start", new Integer(start));
			request.setAttribute("end", new Integer(end));
			request.setAttribute("totRows", new Integer(totalRows));
		} catch (MattizException me) {

		}
		return "paginateAuthors";
	}

	public ArrayList getAuthorsList() {
		return authorsList;
	}

	public void setAuthorsList(ArrayList authorsList) {
		this.authorsList = authorsList;
	}
}

MattizDelegate.java is the business delegate class:

package com.mattiz.businessDelegates;

import java.util.ArrayList;
import com.mattiz.Exception.MattizException;
import com.mattiz.beans.AuthorDTO;
import com.mattiz.dao.MattizDAO;

public class MattizDelegate {
	MattizDAO mattizDAO;

	public MattizDelegate() {
		mattizDAO = new MattizDAO();
	}

	public void addAuthor(AuthorDTO author) throws MattizException {
		mattizDAO.addAuthor(author);
	}

	public ArrayList getAuthors(int start, int end) throws MattizException {
		ArrayList authorArray = mattizDAO.getAuthors(start, end);
		return authorArray;
	}

	public int getNumberOfRows() throws MattizException {
		int rowCount = mattizDAO.getNumberOfRows();
		return rowCount;
	}
}

MattizDAO.java is the Data Access Object class. Note helper method closeStatements(ResultSet rs, PreparedStatement ps, Connection conn) which adds elegance to how connections are created, statements initialized and later disposed off.

package com.mattiz.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.mattiz.Exception.MattizException;
import com.mattiz.beans.AuthorDTO;

public class MattizDAO {
	private static DataSource ds;
	public static final String DATASOURCE_LOOKUP_NAME = "java:dbpool";

	public MattizDAO() {
		try {
			InitialContext ctx = null;
			ctx = new InitialContext();
			ds = (DataSource) ctx.lookup(DATASOURCE_LOOKUP_NAME);
		} catch (javax.naming.NamingException jnx) {
			// log an error declaring connection could not be created
			// object MattizDAO could not be initialized
		}
	}

	public void addAuthor(AuthorDTO author) throws MattizException {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = ds.getConnection();
			String qry = "insert into authors values(?,?);";
			ps = conn.prepareStatement(qry);
			ps.setString(1, author.getIsbnCode());
			ps.setString(2, author.getAuthor());
			// other examples of setters for prepared statement below:
			// ps.setInt(3, 0);
			// ps.setDate(4, new java.sql.Date(1 - 1 - 2007));
			// ps.setTime(5, new java.sql.Time(1 - 1 - 2007));
			// ps.setDouble(6, 1);
			ps.execute();// in case this is an insert/ update
			// PreparedStatement ps, ResultSet rs, Connection conn can be reused
			// with new values for another query
		} catch (SQLException e) {
			throw new MattizException();
		} finally {
			closeStatements(rs, ps, conn);
		}
	}

	public ArrayList getAuthors(int start, int end) throws MattizException {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		ArrayList authorsList;
		try {
			conn = ds.getConnection();
			String qry = "select * from authors;";
			ps = conn.prepareStatement(qry);
			rs = ps.executeQuery();// in case this is a select
			authorsList = new ArrayList();
			int counter = 0;
			while (rs.next()) {
				counter++;// loop till counter>=start
				if (counter >= start && counter <= end) {
					AuthorDTO authorDTO = new AuthorDTO(
							rs.getString("isbn_code"));
					authorDTO.setAuthor(rs.getString("author"));
					authorsList.add(authorDTO);
				} else if (counter > end) {// break out of loop when
				// counter>end
					break;
				}
			}
			// PreparedStatement ps, ResultSet rs, Connection conn can be reused
			// for another query
		} catch (SQLException e) {
			throw new MattizException();
		} finally {
			closeStatements(rs, ps, conn);
		}
		return authorsList;
	}

	public int getNumberOfRows() throws MattizException {
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		int rowCount = 0;
		try {
			conn = ds.getConnection();
			String qry = "select count(*) from authors;";
			ps = conn.prepareStatement(qry);
			rs = ps.executeQuery();
			if (rs.next()) {
				rowCount = rs.getInt(1);
			}
		} catch (SQLException e) {
			throw new MattizException();
		} finally {
			closeStatements(rs, ps, conn);
		}
		return rowCount;
	}

	private void closeStatements(ResultSet rs, PreparedStatement ps,
			Connection conn) throws MattizException {
		if (rs != null) {
			try {
				rs.close();
			} catch (Exception e) {
				throw new MattizException();
			}
			rs = null;
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (Exception e) {
				throw new MattizException();
			}
			ps = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
				throw new MattizException();
			}
			conn = null;
		}
	}
}

AuthorDTO.java is the Data Transfer Object (Value Object):

package com.mattiz.beans;

public class AuthorDTO {
	private String isbnCode;
	private String author;

	public AuthorDTO(String isbnCode) {
		this.isbnCode = isbnCode;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getIsbnCode() {
		return isbnCode;
	}
}

I use one exception class in the example and MattizException.java :

package com.mattiz.Exception;

public class MattizException extends Exception {
	public MattizException() {
		super();
	}

	public MattizException(String msg) {
		super(msg);
	}
}

web.xml configure the Faces Servlet which is the controller for the JSF framework, and the location of the faces-config.xml file which is the configuration file for JSF:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="2.4"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<context-param>
		<param-name>javax.faces.CONFIG_FILES</param-name>
		<param-value>/WEB-INF/faces-config.xml</param-value>
	</context-param>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.faces</url-pattern>
	</servlet-mapping>
</web-app>

faces-config.xml defines the navigation rules for the JSF framework and defines the managed beans (backing beans) for the jsp pages:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
	<navigation-rule>
		<navigation-case>
			<from-outcome>authorAdded</from-outcome>
			<to-view-id>/pages/authorAdded.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
	<navigation-rule>
		<navigation-case>
			<from-outcome>paginateAuthors</from-outcome>
			<to-view-id>/pages/authorPage</to-view-id>
		</navigation-case>
	</navigation-rule>
	<navigation-rule>
		<navigation-case>
			<from-outcome>back</from-outcome>
			<to-view-id>/pages/author.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
	<managed-bean>
		<description>JSF Backing Bean for adding/paginating author
		</description>
		<managed-bean-name>authorBean</managed-bean-name>
		<managed-bean-class>com.mattiz.web.managedBeans.AuthorBean</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>
</faces-config>

build.xml automates the build process. The compiled classes are placed under build/classes folder of root, later zipped as a jar, the jar included within lib of the war (the jar and war files are placed temporarily in build/dist). The war file is finally deployed to jboss deploy folder.

Run build.xml from the command prompt with the following command:
ant install

<?xml version="1.0" encoding="UTF-8"?>
<project name="Mattiz" default="install" basedir=".">
	<property environment="env" />
	<property name="build.dir" location="build" />
	<property name="build.classes.dir" location="${build.dir}/classes" />
	<property name="build.dist.dir" location="${build.dir}/dist" />
	<property name="java.src.dir" location="src/java" />
	<property name="web.src.dir" location="src/web/mattiz" />
	<property name="JBOSS_HOME" location="${env.JBOSS_HOME}" />
	<property name="install.jboss.deploy.dir" location="${JBOSS_HOME}/server/default/deploy" />
	<property name="jsfJars.dir" location="lib/jsfJars" />
	<path id="build.path">
		<pathelement location="${JBOSS_HOME}/server/default/lib/jboss-j2ee.jar" />
		<pathelement location="${JBOSS_HOME}/client/javax.servlet.jar" />
		<pathelement location="${jsfJars.dir}/jsf-api.jar" />
	</path>
	<target name="clean">
		<delete file="${build.dist.dir}/mattiz.jar" />
		<delete file="${build.dist.dir}/mattiz.war" />
		<delete file="${install.jboss.deploy.dir}/mattiz.war" />
		<delete dir="${build.classes.dir}" />
	</target>
	<target name="init">
		<mkdir dir="${build.dir}" />
		<mkdir dir="${build.classes.dir}" />
		<mkdir dir="${build.dist.dir}" />
	</target>
	<target name="compile" depends="init">
		<javac srcdir="${java.src.dir}" destdir="${build.classes.dir}"
			debug="on" deprecation="on" classpathref="build.path" optimize="off" />
	</target>
	<target name="jar" depends="compile">
		<jar destfile="${build.dist.dir}/mattiz.jar">
			<fileset dir="${build.classes.dir}" includes="**/*" excludes="**/*.java" />
		</jar>
		<copy todir="${build.classes.dir}">
			<fileset dir="${java.src.dir}" />
		</copy>
		<war destfile="${build.dist.dir}/mattiz.war" webxml="WebRoot/WEB-INF/web.xml">
			<webinf dir="WebRoot/WEB-INF" />
			<lib dir="${jsfJars.dir}" />
			<lib dir="${build.dist.dir}" includes="mattiz.jar" />
			<zipfileset dir="${web.src.dir}/pages" prefix="pages" />
		</war>
	</target>
	<target name="install" depends="jar">
		<copy overwrite="${FORCE}" todir="${install.jboss.deploy.dir}">
			<fileset dir="${build.dist.dir}">
				<include name="mattiz.war" />
			</fileset>
		</copy>
	</target>
</project>

The war file created has the following structure:

<meta-INF>
<pages>
     author.jsp
     authorAdded.jsp
     authorPage.jsp
<web-INF>
     web.xml
     faces-config.xml
     html_basic.tld
     jsf_core.tld
     <lib>
          commnons-collections.jar
          commons-beanutils.jar
          commons-digester.jar
          jsf-impl.jar
          jsf-api.jar
          {mattiz.jar}
               <meta-INF>
               <com>
                   <mattiz>
                          <beans>
                               AuthorDTO.class
                          <businessDelegates>
                               MattizDelegate.class
                          <dao>
                               MattizDAO.class
                          <exception>
                               MattizException.class
                          <web>
                               <managedBeans>
                                         AuthorBean.class

Start the application with the following URL:

http://localhost:8080/mattiz/pages/author.faces

You are provided with an option to add entries to the database, so that you have enough entries in the database to see pagination work.

Putting it all together:

The Faces Servlet configured in web.xml acts as the Front Controller. It is not manually coded as it is part of the JSF MVC architecture (implied). The request from the client reaches the Front Controller, usually implemented as a servlet. It may use JavaBeans to access the information from the database that is required to authenticate the request. Then, depending on the state of the client’s session, the Front Controller determines to which other worker objects the request will be forwarded for processing. Finally, after the request has been processed, the last worker servlet will forward the request to one of the JSP pages for presenting the data to the client.
During the processing of the request, multiple servlets or JSP pages may be involved, each one optionally creating and updating JavaBeans. In this case, the beans act as the model, the servlets are the controller, and the JSP pages are the view. The beans work closely with the Business Delegate objects to send and receive business data in the form of Value Objects to the business tier, where these data objects are processed and
transactions are performed.
All business objects then make use of Data Access Objects, to fetch and update data from the data store. The DAO shields the business objects from the management chores of the data stores.
Winding it up, in this example we have given examples of four design patterns — Value Object, MVC, Data Access Object, and Business Delegate—and the Front Controller.

Code can be downloaded here

About cuppajavamattiz
Matty Jacob - Avid technical blogger with interests in J2EE, Web Application Servers, Web frameworks, Open source libraries, Relational Databases, Web Services, Source control repositories, ETL, IDE Tools and related technologies.

Comments are closed.

%d bloggers like this: