Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Struts 2 and JQuery Example Project with Jasper for Reporting

DZone's Guide to

Struts 2 and JQuery Example Project with Jasper for Reporting

Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

In continuation to my last article I will give the code needed for a Struts 2 and JQuery project. I will call the project TransportBooking. The important additions we make to JQuery is Jasper reporting which will add reporting functionality to the results in our grid.

Please note I will try to outline as much of the source code as I can to aid and assist in getting a sample project up and running. I also focus on the lesser known parts like multiple search criteria for JQGrid. I have seen many questions on this on the web.

The outline of the project is:

The classes are as follows:

The EditTransportBookingAction and JsonTableAction use the JQuery plugin to give a web frontend that is searchable and has the ajax effects for the webpage. Hibernate is used as ORM and we use hibernate projections for the queries.

The EditTransportBookingAction.java looks like:

package za.co.transporter.transportbooking.action;

import java.util.Date;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.hibernate.Transaction;

import com.googlecode.s2hibernate.struts2.plugin.annotations.TransactionTarget;

import com.opensymphony.xwork2.ActionSupport;

import za.co.transporter.transportbooking.dao.TransportBookingDao;
import za.co.transporter.transportbooking.model.TransportBooking;

@Results( {
@Result(name = “error”, location = “messages.jsp”)
})
public class EditTransportBookingAction extends ActionSupport {

private static final long serialVersionUID = -3454448309088641394L;
private static final Log log = LogFactory.getLog(EditTransportBookingAction.class);

private TransportBookingDao TransportBookingDao = new TransportBookingDao();

private String oper = “edit”;
private String id;
private String name;
private String phone;
private String cellnumber;
private Date confirmBy;
private String transport;


@TransactionTarget
protected Transaction hTransaction;

public String execute() throws Exception
{
log.debug(“Edit Customer :” + getId());

TransportBooking customer;

try
{
if (getOper().equalsIgnoreCase(“add”))
{
log.debug(“Add Customer”);
customer = new TransportBooking();

int nextid = TransportBookingDao.nextTransportBookingNumber();
log.debug(“Id for ne Customer is ” + nextid);
customer.setBookingId(nextid);
customer.setName(getName());
customer.setPhone(getPhone());
customer.setCellnumber(getCellnumber());
customer.setConfirmBy(getConfirmBy());
customer.setTransport(getTransport());

TransportBookingDao.save(customer);
}
else if (getOper().equalsIgnoreCase(“edit”))
{
log.debug(“Edit Customer”);

customer = TransportBookingDao.get(Integer.parseInt(getId()));
log.debug(customer.getBookingId());
customer.setName(getName());
log.debug(customer.getName());
customer.setPhone(getPhone());
log.debug(customer.getPhone());
customer.setCellnumber(getCellnumber());
log.debug(customer.getCellnumber());
customer.setConfirmBy(getConfirmBy());
log.debug(customer.getConfirmBy());
customer.setTransport(getTransport());
log.debug(customer.getTransport());

TransportBookingDao.update(customer);
}
else if (getOper().equalsIgnoreCase(“del”))
{
StringTokenizer ids = new StringTokenizer(getId(), “,”);
while (ids.hasMoreTokens())
{
int removeId = Integer.parseInt(ids.nextToken());
log.debug(“Delete Customer ” + removeId);
TransportBookingDao.delete(removeId);
}
}

// Commit changes
hTransaction.commit();
}
catch (Exception e)
{
hTransaction.rollback();
addActionError(“ERROR : ” + e.getLocalizedMessage());

for(int i=0; i < e.getStackTrace().length && i < 4 ;i++)
addActionError(“ERROR : ” + e.getStackTrace()[i]);

addActionError(“Is Database in read/write modus?”);
return “error”;
}
return NONE;
}

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

public String getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setPhone(String phone) {
this.phone = phone;
}

public String getPhone() {
return phone;
}

public void setCellnumber(String cellnumber) {
this.cellnumber = cellnumber;
}

public String getCellnumber() {
return cellnumber;
}

public void setConfirmBy(Date confirmBy) {
this.confirmBy = confirmBy;
}

public Date getConfirmBy() {
return confirmBy;
}

public void setTransport(String transport) {
this.transport = transport;
}

public String getTransport() {
return transport;
}

public void setOper(String oper) {
this.oper = oper;
}

public String getOper() {
return oper;
}


}


The IndexAction.java looks like:

package za.co.transporter.transportbooking.action;

public class IndexAction {

}

The JsonTableAction.java looks like:

package za.co.transporter.transportbooking.action;

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

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.interceptor.SessionAware;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import za.co.transporter.transportbooking.dao.TransportBookingDao;
import za.co.transporter.transportbooking.model.TransportBooking;

import com.opensymphony.xwork2.ActionSupport;

@Result(type = “json”)
public class JsonTableAction extends ActionSupport implements SessionAware{

private static final long serialVersionUID = 5078264277068533593L;
private static final Log log = LogFactory.getLog(JsonTableAction.class);

// Your result List
private List<TransportBooking> gridModel;

// get how many rows we want to have into the grid – rowNum attribute in the
// grid
private Integer rows = 0;

// Get the requested page. By default grid sets this to 1.
private Integer page = 0;

// sorting order – asc or desc
private String sord = “asc”;

// get index row – i.e. user click to sort.
private String sidx;

// Search Field
private String searchField;

// The Search String
private String searchString;

// he Search Operation
// ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc']
private String searchOper;

// Your Total Pages
private Integer total = 0;

// All Records
private Integer records = 0;

//Multiple Search
private String filters;

private Map session;

private TransportBookingDao transportBookingDao = new TransportBookingDao();

public String execute()
{
log.debug(“Page ” + getPage() + ” Rows ” + getRows() + ” Sorting Order ” + getSord() + ” Index Row :” + getSidx());
log.debug(“filters :” + filters);

// Calcalate until rows ware selected
int to = (rows * page);

// Calculate the first row to read
int from = to – rows;

//JSON Object
JSONObject jsonFilter = null;

//Multiple search filters
if(filters != null){
log.debug(“filters :” + filters);
jsonFilter = (JSONObject) JSONSerializer.toJSON( filters );
}


// Criteria to Build SQL
DetachedCriteria criteria = DetachedCriteria.forClass(TransportBooking.class);


if(jsonFilter != null ){
String groupOp = jsonFilter.getString(“groupOp”);
log.debug(“groupOp :” + groupOp);

JSONArray rules = jsonFilter.getJSONArray(“rules”);
int rulesCount = JSONArray.getDimensions(rules)[0];
log.debug(“Count Rules :” + rulesCount);

for (int i = 0; i < rulesCount; i++) {
JSONObject rule = rules.getJSONObject(i);
log.debug(“field :” + rule.getString(“field”));
log.debug(“op :” + rule.getString(“op”));
log.debug(“data :” + rule.getString(“data”));


if (rule.getString(“field”).equals(“bookingId”)){

Integer searchValue = Integer.parseInt(rule.getString(“data”));
if (rule.getString(“op”).trim().equals(“eq”)) criteria.add(Restrictions.eq(rule.getString(“field”), searchValue));
else if (rule.getString(“op”).trim().equals(“ne”)) criteria.add(Restrictions.ne(rule.getString(“field”), searchValue));
else if (rule.getString(“op”).trim().equals(“lt”)) criteria.add(Restrictions.lt(rule.getString(“field”), searchValue));
else if (rule.getString(“op”).trim().equals(“gt”)) criteria.add(Restrictions.gt(rule.getString(“field”), searchValue));
else if (rule.getString(“op”).trim().equals(“ge”)) criteria.add(Restrictions.ge(rule.getString(“field”), searchValue));
else if (rule.getString(“op”).trim().equals(“le”)) criteria.add(Restrictions.le(rule.getString(“field”), searchValue));

}else if (rule.getString(“field”).equals(“name”) || rule.getString(“field”).equals(“phone”) || rule.getString(“field”).equals(“cellnumber”) || rule.getString(“field”).equals(“confirmBy”) || rule.getString(“field”).equals(“transport”)){

if (rule.getString(“op”).trim().equals(“eq”)) criteria.add(Restrictions.eq(rule.getString(“field”), rule.getString(“data”)));
else if (rule.getString(“op”).trim().equals(“ne”)) criteria.add(Restrictions.ne(rule.getString(“field”), rule.getString(“data”)));
else if (rule.getString(“op”).trim().equals(“lt”)) criteria.add(Restrictions.lt(rule.getString(“field”), rule.getString(“data”) + “%”));
else if (rule.getString(“op”).trim().equals(“gt”)) criteria.add(Restrictions.gt(rule.getString(“field”), “%” + rule.getString(“data”) + “%”));
else if (rule.getString(“op”).trim().equals(“ge”)) criteria.add(Restrictions.ge(rule.getString(“field”), “%” + rule.getString(“data”) + “%”));
else if (rule.getString(“op”).trim().equals(“le”)) criteria.add(Restrictions.le(rule.getString(“field”), “%” + rule.getString(“data”) + “%”));

}


}
}

// Count TransportBooking
records = transportBookingDao.countByCriteria(criteria);

// Reset count Projection
criteria.setProjection(null);
criteria.setResultTransformer(Criteria.ROOT_ENTITY);


// Get TransportBooking by Criteria
gridModel = transportBookingDao.findByCriteria(criteria, from, rows);

// Set to = max rows
if (to > records) to = records;

// Calculate total Pages
total = (int) Math.ceil((double) records / (double) rows);

if( getSession() != null){
log.debug(“Session exists”);

if(getSession().get(“reportsList”) != null){
getSession().remove(“reportsList”);
}
getSession().put(“reportsList”, gridModel);
}else{
log.debug(“Session does not exist. Creating New Session”);
setSession(new HashMap<String, Object>());
getSession().put(“reportsList”, gridModel);
}

return SUCCESS;
}

public String getJSON()
{
return execute();
}

/**
* @return how many rows we want to have into the grid
*/
public Integer getRows()
{
return rows;
}

/**
* @param rows
* how many rows we want to have into the grid
*/
public void setRows(Integer rows)
{
this.rows = rows;
}

/**
* @return current page of the query
*/
public Integer getPage()
{
return page;
}

/**
* @param page
* current page of the query
*/
public void setPage(Integer page)
{
this.page = page;
}

/**
* @return total pages for the query
*/
public Integer getTotal()
{
return total;
}

/**
* @param total
* total pages for the query
*/
public void setTotal(Integer total)
{
this.total = total;
}

/**
* @return total number of records for the query. e.g. select count(*) from
* table
*/
public Integer getRecords()
{
return records;
}

/**
* @param record
* total number of records for the query. e.g. select count(*) from
* table
*/
public void setRecords(Integer records)
{

this.records = records;

if (this.records > 0 && this.rows > 0)
{
this.total = (int) Math.ceil((double) this.records / (double) this.rows);
}
else
{
this.total = 0;
}
}

/**
* @return an collection that contains the actual data
*/
public List<TransportBooking> getGridModel()
{
return gridModel;
}

/**
* @return sorting order
*/
public String getSord()
{
return sord;
}

/**
* @param sord
* sorting order
*/
public void setSord(String sord)
{
this.sord = sord;
}

/**
* @return get index row – i.e. user click to sort.
*/
public String getSidx()
{
return sidx;
}

/**
* @param sidx
* get index row – i.e. user click to sort.
*/
public void setSidx(String sidx)
{
this.sidx = sidx;
}

public void setSearchField(String searchField)
{
this.searchField = searchField;
}

public void setSearchString(String searchString)
{
this.searchString = searchString;
}

public void setSearchOper(String searchOper)
{
this.searchOper = searchOper;
}

public void setFilters(String filters) {
this.filters = filters;
}

public String getFilters() {
return filters;
}

public void setSession(Map session) {
this.session = session;
}

public Map getSession() {
return session;
}


}

In the above class note that we have are using multiple search criteria in our queries meaning that on the front end we can filter by a group of items at once hence why we have filters and jsonFilter. Also we use hibernate projections to build our query.

Now we look at the JasperCsvAction.java. The is where we use jasper reports to generate our reports. This overcomes the problem that the Struts-JQuery plugin lacks reporting tools.

package za.co.transporter.transportbooking.action;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.views.jasperreports.*;

import net.sf.jasperreports.engine.JasperCompileManager;

import za.co.transporter.transportbooking.model.TransportBooking;

import com.opensymphony.xwork2.ActionSupport;

@Result(name=”success”, type=”jasper”, params={“location”, “/transportbooking_template.jasper”, “dataSource”, “reportsList”, “format”, “CSV”,”documentName”,”transportbooking.csv” })
public class JasperCsvAction extends ActionSupport implements SessionAware{

/** List to use as our JasperReports dataSource. */
private List<TransportBooking> reportsList;

private Map session;

private static final Log log = LogFactory.getLog(JasperAction.class);

public String execute() throws Exception {

log.debug(“Creating Reporting”);
log.debug(reportsList);

reportsList = (List<TransportBooking>) getSession().get(“reportsList”);
// Normally we would provide a pre-compiled .jrxml file
// or check to make sure we don’t compile on every request.
try {
JasperCompileManager.compileReportToFile(
“F:/programming-tools/apache-tomcat-7.0.2/webapps/web-deploy/transportbooking_template.jrxml”,
“F:/programming-tools/apache-tomcat-7.0.2/webapps/web-deploy/transportbooking_template.jasper”);
} catch (Exception e) {
e.printStackTrace();
for(int i = 0; i < e.getStackTrace().length; i++)
log.debug(e.getStackTrace()[i]);
return ERROR;
}

return SUCCESS;
}

public List<TransportBooking> getReportsList() {
return reportsList;
}

public void setReportsList(List<TransportBooking> reportsList) {
this.reportsList = reportsList;
}


public void setSession(Map session) {
this.session = session;
}

public Map getSession() {
return session;
}



}


Also note that the above is for illustrative purposes- In reallife we cannot recompile the jasper report for every user this could lead to a terrible bottle-neck in programs!

The other Jasper actions are similar we just change the output in the @Results as is shown for JasperExcelAction.java

package za.co.transporter.transportbooking.action;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.views.jasperreports.*;

import net.sf.jasperreports.engine.JasperCompileManager;

import za.co.transporter.transportbooking.model.TransportBooking;

import com.opensymphony.xwork2.ActionSupport;

@Result(name=”success”, type=”jasper”, params={“location”, “/transportbooking_template.jasper”, “dataSource”, “reportsList”, “format”, “XLS”,”documentName”,”transportbooking.xls” })
public class JasperExcelAction extends ActionSupport implements SessionAware{

/** List to use as our JasperReports dataSource. */
private List<TransportBooking> reportsList;

private Map session;

private static final Log log = LogFactory.getLog(JasperAction.class);

public String execute() throws Exception {

log.debug(“Creating Reporting”);
log.debug(reportsList);

reportsList = (List<TransportBooking>) getSession().get(“reportsList”);
// Normally we would provide a pre-compiled .jrxml file
// or check to make sure we don’t compile on every request.
try {
JasperCompileManager.compileReportToFile(
“F:/programming-tools/apache-tomcat-7.0.2/webapps/web-deploy/transportbooking_template.jrxml”,
“F:/programming-tools/apache-tomcat-7.0.2/webapps/web-deploy/transportbooking_template.jasper”);
} catch (Exception e) {
e.printStackTrace();
for(int i = 0; i < e.getStackTrace().length; i++)
log.debug(e.getStackTrace()[i]);
return ERROR;
}

return SUCCESS;
}

public List<TransportBooking> getReportsList() {
return reportsList;
}

public void setReportsList(List<TransportBooking> reportsList) {
this.reportsList = reportsList;
}


public void setSession(Map session) {
this.session = session;
}

public Map getSession() {
return session;
}



}


The folder for our web application which I will call web-deploy has the structure:

The web-xml looks like:

<?xml version=”1.0″ encoding=”UTF-8″?>
<web-app id=”struts2-jquery-showcase” version=”2.4″ xmlns=”http://java.sun.com/xml/ns/j2ee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”>

<display-name>PaltrackTransportBookingDemo</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>

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

</web-app>

 

The grid.jsp web page is where are the events take place:

<%@ taglib prefix=”s” uri=”/struts-tags”%>
<%@ taglib prefix=”sj” uri=”/struts-jquery-tags”%>
<%@ taglib prefix=”sjg” uri=”/struts-jquery-grid-tags”%>

<script type=”text/javascript”>
datePick = function(elem) {
$(elem).datepicker();
$(‘#ui-datepicker-div’).css(“z-index”, 2000);
}
</script>
<s:url id=”remoteurl” action=”json-table” />
<s:url id=”editurl” action=”edit-transport-booking” />
<s:url id=”pdfreporturl” action=”jasper” />
<s:url id=”excelreporturl” action=”jasper-excel” />
<s:url id=”csvreporturl” action=”jasper-csv” />
<s:url id=”htmlreporturl” action=”jasper-html” />
<sjg:grid
id=”transportbookingtable”
caption=”TransportBooking (Editable/Multiselect)”
dataType=”json”
href=”%{remoteurl}”
pager=”true”
navigator=”true”
navigatorSearch=”true”
navigatorSearchOptions=”{multipleSearch:true}”
navigatorAddOptions=”{
height:280,
reloadAfterSubmit:true,
afterSubmit:function(response, postdata) {
return isError(response.responseText);
}
}”
navigatorEdit=”true”
navigatorEditOptions=”{
height:280,
reloadAfterSubmit:true,
afterSubmit:function(response, postdata) {
return isError(response.responseText);
}
}”
navigatorView=”true”
navigatorDelete=”true”
navigatorDeleteOptions=”{
height:280,
reloadAfterSubmit:true,
afterSubmit:function(response, postdata) {
return isError(response.responseText);
}
}”
gridModel=”gridModel”
rowList=”10,15,20″
rowNum=”15″
editurl=”%{editurl}”
editinline=”false”
multiselect=”true”
viewrecords=”true”
>
<sjg:gridColumn name=”bookingId”
index=”bookingId”
key=”true”
title=”ID”
width=”50″
formatter=”integer”
sortable=”true”
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge']}”
/>
<sjg:gridColumn
name=”name”
index=”name”
title=”name”
width=”300″
sortable=”true”
editable=”true”
edittype=”text”
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge']}”
/>
<sjg:gridColumn
name=”phone”
index=”phone”
title=”phone”
sortable=”true”
editable=”true”
edittype=”text”
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge']}”
/>
<sjg:gridColumn
name=”cellnumber”
index=”cellnumber”
title=”cellnumber”
sortable=”true”
editable=”true”
edittype=”text”
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge']}”
/>
<sjg:gridColumn
name=”confirmBy”
index=”confirmBy”
title=”confirmBy”
sortable=”true”
editable=”true”
edittype=”text”
editoptions=”{size: 19, maxlength: 19, dataInit:datePick }”
formatter=”date”
formatoptions=”{newformat : ‘d.m.Y H:i’, srcformat : ‘Y-m-d H:i:s’}”
width=”90″
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge'], dataInit:datePick, attr:{title:’Your Search Date’}}”
/>
<sjg:gridColumn
name=”transport”
index=”transport”
title=”transport”
sortable=”true”
editable=”true”
edittype=”text”
search=”true”
searchoptions=”{sopt:['eq','ne','lt','gt','le','ge']}”
/>
</sjg:grid>

<br>
<br>
<br>
<br>
<div id=”nav”>
<div class=”hlist ui-widget-header”>
<ul>
<li class=”ui-widget-header”><s:a id=”reportlink” href=”%{pdfreporturl}” >PDF Report</s:a></li>
<li class=”ui-widget-header”><s:a id=”reportlink” href=”%{excelreporturl}” >Excel Report</s:a></li>
<li class=”ui-widget-header”><s:a id=”reportlink” href=”%{csvreporturl}” >CSV Report</s:a></li>
<li class=”ui-widget-header”><s:a id=”reportlink” href=”%{pdfreporturl}” >HTML Report</s:a></li>
</ul>
</div>
</div>


Here you can see how to add date pickers and filters for multiple criteria filters. Please not that I borrowed the theme from the showcase of the JQuery examples at http://www.weinfreund.de/struts2-jquery-showcase/index.action.

The end result is as follows:

The relevant libraries needed to build the project:

Enjoy! If you have any queries or would like more source code please email me and I can email you the source code zipped.

From http://gabrieljeremiahcampbell.wordpress.com/2011/02/28/struts-2-and-jquery-example-project-with-jasper-for-reporting/

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}