Spring MVC Tutorial – Paging Through Hibernate and Selection

December 30, 2009 · Leave a Comment
Filed under: Featured, Hibernate, JAVA, Springs 

In this installment, I’m expanding the project done in Spring MVC Tutorial – Hibernate Integration to include:

  1. Browsing/paging through a table with a navigation bar found in various forum sites and ASP.NET GridView (e.g. First 3 4 5 6 7 Last)
  2. Showing checkboxes against each row of the table for selective action

The related project code download is ibank-v2.zip

Handling Navigation/Pagination/Paging

There are free libraries out there like Google’s Jmesa, DisplayTag, etc. and also Spring has support through PagedListHolder and such. But I was in a different mood and decided to go for my own implementation.

Also, this helped me understand the logics of displaying a navigation bar. Maybe I’ll learn about the libraries later and share with you.

NavigationInfo

This is the workhorse for the navigation bar:

package org.himu.ibank.service.vo;

public class NavigationInfo {

private int currentPage;
private int pageSize;
private int rowCount;
private int maxIndices;

public NavigationInfo() {
currentPage = 0;
rowCount = 0;
maxIndices= 5;
pageSize = 5;
}

public int getCurrentPage() {
return currentPage;
}

public void setCurrentPage(int currentPage) {
if (currentPage < 0)
this.currentPage = 0;
else if (currentPage > getPageCount() – 1)
this.currentPage = getPageCount() – 1;
else
this.currentPage = currentPage;
}

public int getPageSize() {
return pageSize;
}

public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}

public int getRowCount() {
return rowCount;
}

public void setRowCount(int rowCount) {
this.rowCount = rowCount;
}

public int getMaxIndices() {
return maxIndices;
}

public void setMaxIndices(int maxIndices) {
this.maxIndices = maxIndices;
}

public int getPageCount() {
return (int) Math.ceil((double) rowCount / pageSize);
}

public int getPrevIndex() {
int prev = currentPage – 1;
return prev < 0 ? 0 : prev;
}

public int getNextIndex() {
int lastIndex = getPageCount() – 1;
int next = currentPage + 1;
return next > lastIndex ? lastIndex : next;
}
public boolean isFirstPage() {
return 0 == currentPage;
}
public boolean isLastPage() {
return (getPageCount() – 1) == currentPage;
}

public int[] getIndexList() {
int[] range = getIndexRange();
int[] ilist = new int[range[1] – range[0] + 1];
for (int i = 0; i < ilist.length; i++) {
ilist[i] = range[0] + i;
}
return ilist;
}
public int[] getIndexRange() {
// determine the standard window
int start = currentPage – maxIndices / 2;
int end = start + maxIndices – 1;
// shift to right if start underflows 0
if (start < 0) {
end -= start; // end – -start = end + start = shift right
start = 0;
}
// now maybe the window overflows pageCount – so shift to left again
int lastIndex = getPageCount() – 1;
if (end > (lastIndex)) {
start -= (end – lastIndex);
end = lastIndex;
}
// we have finalized end, now if start < 0 then truncate it
if (start < 0)
start = 0;
return new int[] {start, end};
}
}

The page indices are 0-based. The class has some defaults which can be overridden if required.

The starting point for this class is setRowCount(). Once the number of rows is determined you can use getPageCount() which in turn allows all other navigation methods to work.

setCurrentPage(), getPrevIndex() and getNextIndex() make sure you don’t fall out of valid page indices.

getIndexRange() is where all the muscle is. It uses currentPage and maxIndices (maximum number of links to be displayed in the navigation bar) to determine the start and end of page links in the navigation bar.

getIndexList() is a convenience method for generating the entire list of navigation indices from the range calculated by getIndexRange().

PagedCustView

This class is used to send the navigation information and the current page of customers to the view.

package org.himu.ibank.service.vo;

import java.util.List;

import org.himu.ibank.domain.Customer;

/**
* Hold necessary information for paged view of customer list
*/
public class PagedCustView {

private NavigationInfo navInfo = new NavigationInfo();
private List<Customer> customers;

public NavigationInfo getNavInfo() {
return navInfo;
}

public void setNavInfo(NavigationInfo navInfo) {
this.navInfo = navInfo;
}

public List<Customer> getCustomers() {
return customers;
}

public void setCustomers(List<Customer> customers) {
this.customers = customers;
}

public Customer getCustomer(int i) {
return (Customer) customers.get(i);
}

public void setCustomer(int i, Customer customer) {
this.customers.add(i, customer);
}
}

The actual page of customers is fetched in the controller and the navigation information is set accordingly.

Writing the Controller

You’re going to browse all registered but unauthorized customers.

The NewCustListController expects a page parameter in the query string of its calling URL. If this is not found then page 0 (the first page) is assumed.

package org.himu.ibank.controller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.himu.ibank.service.CustomerRegistrationService;
import org.himu.ibank.service.vo.PagedCustView;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

public class NewCustListController extends AbstractController {

private CustomerRegistrationService custRegService;
public void setCustRegService(CustomerRegistrationService custRegService) {
this.custRegService = custRegService;
}

@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
Map<Object, Object> model = new HashMap<Object, Object>();
PagedCustView pcustv = new PagedCustView();

pcustv.getNavInfo().setRowCount(custRegService.getUnauthorizedCustomerCount());

String page = (String)request.getParameter("page");
if (null == page)
pcustv.getNavInfo().setCurrentPage(0);
else
pcustv.getNavInfo().setCurrentPage(Integer.parseInt(page));

pcustv.setCustomers(custRegService.getUnauthorizedCustomers(
pcustv.getNavInfo().getCurrentPage(), pcustv.getNavInfo().getPageSize()));

request.getSession().setAttribute("pagedcust", pcustv);
model.put("pagedcust", pcustv);

return new ModelAndView("admin/newcustlist", model);

}
}
  1. First you determine the customer count and tell it to NavigationInfo.
  2. Then you set the current page.
  3. Based on the current page and page size as specified in NavigationInfo, you then fetch the only the specific page of customers through Hibernate. This is the reason I opted for my own implementation.
  4. Return the model and view name as usual.

The Service and DAO Classes

I’ve refactored CustomerRegistrationService and CustomerDao interfaces and their implementations a bit. Also note that I’ve corrected a mistake in HibernateCustomerDao’s paging code.

CustomerRegistrationService

public interface CustomerRegistrationService {

Long registerCustomer(Customer c);
void authorizeCustomer(Customer c);
Customer getCustomer(Long id);
List<Customer> getUnauthorizedCustomers(int startPage, int pageSize);
List<Customer> getCustomers(int startPage, int pageSize);
List<Customer> getAllCustomers(int startPage, int pageSize);
public int getUnauthorizedCustomerCount();
}

StandardCustomerRegistrationService

public class StandardCustomerRegistrationService implements CustomerRegistrationService { private CustomerDao custDao; public void setCustDao(CustomerDao custDao) { this.custDao = custDao; } @Override public Long registerCustomer(Customer c) { return custDao.addNew(c); } @Override public void authorizeCustomer(Customer c) { if (c.getStatus() == Customer.STATUS_REGISTERED) { c.setStatus(Customer.STATUS_ACTIVE); custDao.update(c); // … mailer code for notifying customer … } } @Override public Customer getCustomer(Long id) { return custDao.findById(id); } /** * Get registered but unauthorized customers using optional paging. * * @param startPage Starting page number, first page is 0 * @param pageSize Size of a single page, <=0 means no paging */ @Override public List<Customer> getUnauthorizedCustomers(int startPage, int pageSize) { Customer c = new Customer(); c.setStatus(Customer.STATUS_REGISTERED); return custDao.listByExample(c, startPage, pageSize); } @Override public int getUnauthorizedCustomerCount() { return custDao.countCustomers(Customer.STATUS_REGISTERED); } /** * Get registered and authorized customers using optional paging. * * @param startPage Starting page number, first page is 0 * @param pageSize Size of a single page, <=0 means no paging */ @Override public List<Customer> getCustomers(int startPage, int pageSize) { Customer c = new Customer(); c.setStatus(Customer.STATUS_ACTIVE); return custDao.listByExample(c, startPage, pageSize); } @Override public List<Customer> getAllCustomers(int startPage, int pageSize) { return custDao.listAll(startPage, pageSize); } }

CustomerDao

public interface CustomerDao {

Long addNew(Customer c);
void delete(Customer c);
void update(Customer c);
Customer findById(Long id);
Customer findByUserId(String uid);
List<Customer> listAll();
List<Customer> listAll(int startPage, int pageSize);
List<Customer> listByExample(Customer c);
List<Customer> listByExample(Customer c, int startPage, int pageSize);
int countCustomers(int status);
}

HibernateCustomerDao

public class HibernateCustomerDao extends HibernateDaoSupport implements
CustomerDao {

@Override
public Long addNew(Customer c) {
return (Long) getHibernateTemplate().save(c);
}

@Override
public void delete(Customer c) {
getHibernateTemplate().delete(c);
}

@Override
public void update(Customer c) {
getHibernateTemplate().update(c);
}

@Override
public Customer findById(Long id) {
return (Customer) getHibernateTemplate().get(Customer.class, id);
}

@SuppressWarnings("unchecked")
@Override
public Customer findByUserId(String uid) {
List<Customer> clist =  getHibernateTemplate().find("from Customer c where c.userId = ?", uid);
if (clist.isEmpty())
return null;
else
return clist.get(0);
}

@SuppressWarnings("unchecked")
@Override
public List<Customer> listAll() {
return getHibernateTemplate().find("from Customer c");
}

@SuppressWarnings("unchecked")
@Override
public List<Customer> listAll(int startPage, int pageSize) {
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class);
criteria.addOrder(Order.asc("id"));
return getHibernateTemplate().findByCriteria(criteria, startPage * pageSize, pageSize);
}

@SuppressWarnings("unchecked")
@Override
public List<Customer> listByExample(Customer c) {
return getHibernateTemplate().findByExample(c);
}

@SuppressWarnings("unchecked")
@Override
public List<Customer> listByExample(Customer c, int startPage, int pageSize) {
return getHibernateTemplate().findByExample(c, startPage * pageSize, pageSize);
}

@Override
public int countCustomers(int status) {
DetachedCriteria criteria = DetachedCriteria.forClass(Customer.class)
.add(Restrictions.eq("status", status))
.setProjection(Projections.rowCount());
return (Integer)getHibernateTemplate().findByCriteria(criteria).get(0);
}
}

The Browsing JSP

The Model and Controller codes are done. Now you write the view – newcustlist.jsp. Create it by copying admin.jsp as usual. Following is the relevant additions you need to write:
<div id="content"> <div align="center"> <c:if test="${!pagedcust.navInfo.firstPage}"> <a href="newcustlist.htm?page=0">First</a>&nbsp; </c:if> <c:forEach var="i" items="${pagedcust.navInfo.indexList}"> <c:choose> <c:when test="${i != pagedcust.navInfo.currentPage}"> <a href="newcustlist.htm?page=${i}">${i}</a>&nbsp; </c:when> <c:otherwise> <b>${i}</b>&nbsp; </c:otherwise> </c:choose> </c:forEach> <c:if test="${!pagedcust.navInfo.lastPage}"> <a href="newcustlist.htm?page=${pagedcust.navInfo.pageCount – 1}">Last</a> </c:if> </div> <form action="<%=request.getContextPath()%>/authorizecust.htm" method="post"> <table> <thead> <tr> <td>User ID</td> <td>First Name</td> <td>Last Name</td> <td>Email</td> </tr> </thead> <tbody> <c:forEach var="cust" items="${pagedcust.customers}"> <tr> <td>${cust.userId}</td> <td>${cust.firstName}</td> <td>${cust.lastName}</td> <td>${cust.email}</td> <td><input type="checkbox" value="${cust.id}" name="authorized"/></td> </tr> </c:forEach> </tbody> </table> <input type="hidden" name="linkBackIndex" value="${pagedcust.navInfo.currentPage}"/> <input type="submit" value="Authorize"/> </form> </div>
  1. First, generate the navigation banner aligned in the center of the page. It shows optional First and Last links based on the current page. The center <c:forEach> loops through the index list taken from navInfo and generates the necessary page links excluding the current page.
  2. Declare a form which allows us to submit authorization approvals of selected customers. The code for authorization will be written later in another controller.
  3. Looping through the customer list is trivial. The important thing is generating a checkbox against each customer for selecting for approval. The name of the checkboxes must be same (authorize in the above code) but there values will be different – the internal ID of each customer. This ID is required for Hibernate.
  4. A hidden input field linkBackIndex is also declared which helps us to return to the current page after authorization is made. It will be clear in the authorization controller.

Processing Actual Authorization and Returning to the New Customer Browser

First, the code:

package org.himu.ibank.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.himu.ibank.domain.Customer;
import org.himu.ibank.service.CustomerRegistrationService;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.view.RedirectView;

public class AuthorizeCustController extends AbstractController {

private CustomerRegistrationService custRegService;

public void setCustRegService(CustomerRegistrationService custRegService) {
this.custRegService = custRegService;
}

@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String[] custids = request.getParameterValues("authorized");
String linkBackIndex = request.getParameter("linkBackIndex");
System.out.println("################### INSIDE AuthorizeCustController #############");
for (String custid: custids) {
Customer cust = custRegService.getCustomer(Long.parseLong(custid));
custRegService.authorizeCustomer(cust);
System.out.println("## Authorized: " + custid);
}
return new ModelAndView(new RedirectView(request.getContextPath() + "/newcustlist.htm?page=" + linkBackIndex));
}
}

The logic is simple:

  1. Get the list of selected customer IDs using request.getParameterValues(“authorized”). It will contain only those IDs whose checkboxes are marked.
  2. Iterate through the list as ask our service class to make the authorizations.
  3. We use the linkBackIndex to redirect the flow to NewCustListController using its Spring MVC defined URL, i.e. /newcustlist.htm.

Notice how we are redirecting in the return statement. The RedirectView class helps us to go to back to the new customer browser and also pass the last page used as a query parameter. An alternative is to define the view in the bean configuration file using the pattern ‘redirect:<url>’, e.g. ‘redirect:/newcustlist.htm’ but we cannot pass the query parameter then.

Modified ibank-servlet.xml

Finally, our gluing file. I won’t be discussing it as there is nothing special.

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

<bean id="simpleUrlMapping">
<property name="mappings">
<props>
<prop key="/home.htm">homePageController</prop>
<prop key="/adminhome.htm">adminHomePageController</prop>
<prop key="/createcust.htm">newCustomerController</prop>
<prop key="/newcustlist.htm">newCustomerListController</prop>
<prop key="/authorizecust.htm">authorizeCustController</prop>
</props>
</property>
</bean>
<bean id="viewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="homePageController"/>
<bean id="adminHomePageController">
<property name="viewName" value="admin/admin"/>
</bean>
<bean id="newCustomerController">
<property name="custRegService" ref="customerRegService"/>
<property name="formView" value="admin/newcustomer"/>
<property name="successView" value="admin/newcustomer-success"/>
<property name="commandName" value="customer"/>
<property name="commandClass" value="org.himu.ibank.domain.Customer"/>
</bean>
<bean id="newCustomerListController">
<property name="custRegService" ref="customerRegService"/>
</bean>
<bean id="authorizeCustController">
<property name="custRegService" ref="customerRegService"/>
</bean>
</beans>

Hope to hear from you.

Lazymans Google XML sitemap

December 30, 2009 · Leave a Comment
Filed under: Featured, General, Google API, SEO 

This is a good sitemap module, that doesnt require you to keep updating the sitemap in the back office, as it updates the sitemap everytime the file is called.

Read and download the module here

Things to do after installing Ubuntu9.10 Karmic Koala

December 30, 2009 · Leave a Comment
Filed under: Featured, Karmic Koala, Ubuntu 
Ubuntu 9.10 Karmic koala will be released in few hours,  after you install karmic or upgrading, you will need to add some softwares, repositories,  you can also make the look of karmic better by adding cairo dock menu for example. You can always add more themes , see this article : Great themes for Ubuntu9.10 Karmic koala Here are things i see are important to do after installing Ubuntu karmic Koala :

1- Add Medibuntu repositories :

Medibuntu is a packaging project dedicated to distributing software that cannot be included in Ubuntu for various reasons, related to geographical variations in legislation regarding intellectual property, security and other issues. by adding Medibuntu repostories you will be able to install many softwares like Google-Earth , opera ,Win32codecs , Msfonts  and more by the normal command :

sudo-apt-get install <software-name>

Click here and Follow the steps to add medibuntu repostories to ubuntu 9.10 karmic Koala in my previous post.

2- Install PlayOnLinux :

PlayOnLinux is a piece of sofware which allows you to easily install and use numerous games and softwares designed to run with Microsoft®’s Windows®.
You can install many windows games like callofduty, doom ,…. Msfonts ,Itune ,Microsoft office 2007 (see our howto install MS office 2007 using playonlinux)

You can install playonlinux from Ubuntu software center

Or by using the command :

sudo apt-get install playonlinux





3- Enchance launch-bar menu in karmic with cairo-dock :

Cairo-Dock is an animated application launch bar for the desktop, comparable to the dock in Mac OS X or Rocket Dock (for Windows).  It is compatible with Compiz, but it can also run without a composite manager . Cairo-Dock can run under GNOME, KDE and XFCE.


Open the terminal and copy and past the command bellow :

sudo -v
echo "deb http://repository.cairo-dock.org/ubuntu $(lsb_release -sc) cairo-dock ## Cairo-Dock-Stable" | sudo tee -a /etc/apt/sources.list
wget -q http://repository.cairo-dock.org/cairo-dock.gpg -O- | sudo apt-key add -  

Update the installation :

sudo apt-get update

Now install cairo-dock :

sudo apt-get install cairo-dock cairo-dock-plug-ins

4- Add screenlets to your Desktop :

Screenlets are small owner-drawn applications that can be described as “the virtual representation of things lying/standing around on your desk”. Sticknotes, clocks, rulers, … the possibilities are endless. The goal of the Screenlets base-classes is to simplify the creation of fully themeable mini-apps that each solve basic desktop-work-related needs and generally improve the usability and eye-candy of the modern composited Linux-desktop.

sudo apt-get install screenlets

Read more in our previous article

5- Ubuntu Gaming made easy with PlayDeb

If you are a fun of gaming so is important to add Playdeb repositories to your Karmic koala. Playdeb is a gaming repository for Ubuntu – aimed to provide titles already available on getdeb.net in an easier to install and update format. You can install many games by a simple click

Please see how to add playdeb.net repository in this link : Ubuntu Gaming made easy with PlayDeb

6- Important softwares you must have :

These softwares you can install from Ubuntu software center :

- Vlc : The best media player for Linux it play almast everything , he has many features that you can not find in any other media player , see this article to know more about vlc : Things you didn’t know VLC media player can do


-Vuze : the most popular Bitorrent client :

- Moovida Media center : Moovida is much more than a simple media player… it is a cutting edge media center bringing the best of the internet to your TV screen. Automatically creating your own digital library you can browse from your sofa with a remote control. The elegant and easy to use interface automatically displays artwork and fan art throughtout and gives you access to movie synopsis and artist info.

- Install Thunderbird:  the best mail / News client from Mozilla :

- Filezilla the best ftp client for linux :

- Pidgin the best messenger client and 30 plugins, you can enjoy chat with freinds using voice and cam :


- Openshot video editor : Openshot is a video editor for linux, is one of the best ones existing actually for linux. The news is that now has a PPA this mean easy install for Ubuntu users, but the big news is that the new version come with 30 new effects. See the video demonstration what you can do with Openshot : Install openshot on karmic koala

jQTouch – jQuery plugin for mobile web development

December 30, 2009 · Leave a Comment
Filed under: Featured, JQuery, JavaScript 
jQTouch - jQuery plugin for mobile web development

jQuery plugin for mobile web development

http://jqtouch.com/

A jQuery plugin for mobile web development on the iPhone, Android, Palm Pre, and other forward-thinking devices. what a plugin

Resolving “Page Errors on the GAM…” Error Message

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 
SYS-CON Media (press release)

By Gram Smith The metadata structure of Microsoft SQL database is divided into numerous allocation units, such as, IAM (Index Allocation Map) pages,

See all stories on this topic

DuraSpace and Fedora Release Fedora 3.3

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 

Newly added features include integration of the Fedora Enhanced Security Layer, improvements to the Fedora REST API, support for Microsoft SQL Server,

See all stories on this topic

Hierarchies, SQL and Java | JavaWorld's Daily Brew

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 

Get the latest Java technology news and reports from JavaWorld.com.


JavaWorld's Daily Brew – Starting… – http://www.javaworld.com/community/

Installing SQL Server 2008 Failover Cluster on a Windows Server

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 

I did a demo fest on installing SQL Server 2008 Failover Cluster on a Windows Server 2008 system a few weeks back for a user group event and the attendees requested that I post more information about how to do a slipstream of service


Third Tier – http://www.thirdtier.net/

Installing SQL Server 2005 on a Windows 2008 R2 Cluster

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 

I was installing SQL Server 2005 on a Windows Server 2008 R2 cluster. The cluster validates properly (use the cluster validation tool in 2008 R2, it will save you problems) and failed over properly prior to installation.


syscomments – http://weblogs.sqlteam.com/chrism/

SQL Server 2008 DBA & TSQL programming training (Albany

December 30, 2009 · Leave a Comment
Filed under: MySQL 5.1, MySql, MySql 5.0 

SQL Server 2008 DBA & TSQL programming training starts on January -15th. Please call me @267 718 1533 & enroll in advance. Serious enquires only please. I have 9 years extensive experience in SQL Server 2008/2005/2000 database


Telecommute Job List – Free list… – http://telecommutejoblist.com/

Next Page »