Richfaces - Modal Panel As A Wizard

Richfaces 'Modal Panel' component can be used to create a 'Dialog Box' or a 'Wizard' (or Wizard like behaviour), which is useful, when some part of the website/application has to ask for the input from the user, in steps. It can be customized, as per the requirements of that particular application, although everything that you implement doesn't sound like a standard mechanism, from the 'Richfaces' AJAX framework. Below is an example of creating a 'Wizard' out of a 'Richfaces Modal Panel' component:

1) Lets suppose that this 'Wizard' is opened, when a user clicks on some link (or any other AJAX/Javascript event)...and 2 steps, for that Wizard. So, the modal panel is on one page and there would be 2 other pages that are to be included, in the modal panel, to get that 'Wizard' like behavior. In my case, I'm using JSF and Facelets (Xhtml files). Below is a code snippet for the main page (where the modal panel is included):

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">

<ui:composition>
...........
...........
............

<rich:modalPanel id="panel1">
<f:facet name="header">
<h:outputText value="Test" />
</f:facet>

<f:facet name="controls">
<h:commandLink value="Close"
style="cursor:pointer"
onclick="Richfaces.hideModalPanel('panel1')" />
</f:facet>

<a4j:include binding="#{someBean.include}" viewId="/view/include1.xhtml"/>
</rich:modalPanel>
</ui:composition>
</html>


2) And below is the code snippet, for the first included page, on the modal panel:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">

<ui:composition>
...........
...........
............

<h:form>
<h:panelGrid columns="2">
<h:outputText value="#{msg.input}"
<h:inputText value="#{someBean.someText}"/>

<rich:spacer width="20"/>
<a4j:commandButton value="#{msg.submit}" action="include2" reRender="wizard"/>
</h:panelGrid>
</h:form>
</ui:composition>
</html>


Note that the above included page, doesn't need to have f:view tags. Also, there are a couple of important points to consider from the Richfaces documentation:

RichFaces allows to organize a page flow inside the component. This is a typical scenario for Wizard like behavior. The new content is rendered inside the area. The content is taken from the navigation rule of the faces configuration file (usually, the faces-config.xml). Note, that the content of the "wizard" is not isolated from the rest of the page. The included page should not have own (it does not matter if you use facelets). You need to have an Ajax component inside the to navigate between the wizard pages. Otherwize, the whole page update will be performed.

If you want to involve the server side validators and navigate to the next page only if the Validation phase is passed successfully, you can replace with and point to the action method that navigates to the next page. If Validation process fails, the partial page update will occur and you will see an error message. Otherwize, the application proceeds to the next page. Make sure, you define option for the navigation rule to avoid memory leaks.


3) And below is the "include2.xhtml", which is shown as a second step, for that wizard. This wizard appears only if the validation succeeds on the previously included page (in case, if you have any input validations).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j">

<ui:composition>
...........
...........
............

<h:form>
<a4j:commandButton value="#{msg.confirm}" action="#{someBean.doSomeBusinessShit}" reRender="someId,wizard"/>
<a4j:commandButton value="#{msg.cancel}" action="#{someBean.reset}" onclick="javascript:Richfaces.hideModalPanel('panel1')" reRender="wizard"/>
</h:form>
</ui:composition>
</html>


4) And the faces-config.xml configuration, is as follows:

<?xml version="1.0"?>
<!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>
<from-view-id>/view/include1.xhtml</from-view-id>
<navigation-case>
<from-outcome>include2</from-outcome>
<to-view-id>/view/include2.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>



...And hey, its not yet over. If you happened to implement the above stuff and if you navigated to the second step, clicked 'Cancel' and tried to open the 'Wizard' again, the second included page, opens up directly, without the first step (included page). To remedy this, I've to reset the "viewId" in the "a4j:include" component. The "a4j:include" tag, already includes a binding. The only thing that has to be done is to implement the "reset" method, as below:

package echo;

import org.ajax4jsf.component.html.Include;

public class SomeBean {
.........
.........
private Include include;

.......
.......
.......

public void reset() {
include.setViewId("/view/include1.xhtml");
}
}

Jasper Reports - Thermometer Report

Below is the code of 'ThermoBean', which holds the data for 'Thermometer Report':

package com.chetty.reporting.beans;

public class ThermoBean {
private String title;
private double value;
private double rangeMin;
private double rangeMax;
private double firstSubRangeMin;
private double firstSubRangeMax;
private double secondSubRangeMin;
private double secondSubRangeMax;
private double thirdSubRangeMin;
private double thirdSubRangeMax;

public void setTitle(String title) {
this.title = title;
}

public String getTitle() {
return title;
}

public void setValue(double value) {
this.value = value;
}

public double getValue() {
return value;
}

public void setRangeMin(double rangeMin) {
this.rangeMin = rangeMin;
}

public double getRangeMin() {
return rangeMin;
}

public void setRangeMax(double rangeMax) {
this.rangeMax = rangeMax;
}

public double getRangeMax() {
return rangeMax;
}

public void setFirstSubRangeMin(double firstSubRangeMin) {
this.firstSubRangeMin = firstSubRangeMin;
}

public double getFirstSubRangeMin() {
return firstSubRangeMin;
}

public void setFirstSubRangeMax(double firstSubRangeMax) {
this.firstSubRangeMax = firstSubRangeMax;
}

public double getFirstSubRangeMax() {
return firstSubRangeMax;
}

public void setSecondSubRangeMin(double secondSubRangeMin) {
this.secondSubRangeMin = secondSubRangeMin;
}

public double getSecondSubRangeMin() {
return secondSubRangeMin;
}

public void setSecondSubRangeMax(double secondSubRangeMax) {
this.secondSubRangeMax = secondSubRangeMax;
}

public double getSecondSubRangeMax() {
return secondSubRangeMax;
}

public void setThirdSubRangeMin(double thirdSubRangeMin) {
this.thirdSubRangeMin = thirdSubRangeMin;
}

public double getThirdSubRangeMin() {
return thirdSubRangeMin;
}

public void setThirdSubRangeMax(double thirdSubRangeMax) {
this.thirdSubRangeMax = thirdSubRangeMax;
}

public double getThirdSubRangeMax() {
return thirdSubRangeMax;
}
}


And below is the class file with business logic, used to generate a collection of 'ThermoBean' objects, which is passed on to the Jasper reporting engine, to dynamically generate the Thermometer report:

package com.chetty.reporting.business;

import java.util.ArrayList;

import com.chetty.reporting.beans.ThermoBean;

public class ThermoBeanMaker {
public ArrayList getThermoData() {
ArrayList thermoBeanList = new ArrayList();

thermoBeanList.add(create(-10.0, -40.0, 50.0, -40.0, 0.0, 0.0, 15.0, 15.0, 50.0));

return thermoBeanList;
}

private ThermoBean create(double value, double rangeMin, double rangeMax, double firstSubRangeMin, 
double firstSubRangeMax, double secondSubRangeMin, double secondSubRangeMax, 
double thirdSubRangeMin, double thirdSubRangeMax) {
ThermoBean thermoBean = new ThermoBean();

thermoBean.setTitle("Germany - Temparature Report");
thermoBean.setValue(value);
thermoBean.setRangeMin(rangeMin);
thermoBean.setRangeMax(rangeMax);
thermoBean.setFirstSubRangeMin(firstSubRangeMin);
thermoBean.setFirstSubRangeMax(firstSubRangeMax);
thermoBean.setSecondSubRangeMin(secondSubRangeMin);
thermoBean.setSecondSubRangeMax(secondSubRangeMax);
thermoBean.setThirdSubRangeMin(thirdSubRangeMin);
thermoBean.setThirdSubRangeMax(thirdSubRangeMax);

return thermoBean;
}
}


And below is the main class, that generates the report:

package com.chetty.reporting.engine;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.chetty.reporting.beans.GanttChartBean;
import com.chetty.reporting.beans.PieChartBean;
import com.chetty.reporting.beans.ThermoBean;
import com.chetty.reporting.business.GanttChartBeanMaker;
import com.chetty.reporting.business.PieChartBeanMaker;
import com.chetty.reporting.business.ThermoBeanMaker;

import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

public class Reporter {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream ("reports/thermochart.jrxml");

ThermoBeanMaker thermoBeanMaker = new ThermoBeanMaker();
ArrayList thermoBeanList = thermoBeanMaker.getThermoData();

JRBeanCollectionDataSource beanColDataSource = new JRBeanCollectionDataSource(thermoBeanList);

Map parameters = new HashMap();

JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, beanColDataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, "c:/reports/thermochart.pdf"); 
}
}


When you run the above class, the following report is generated:

Thermometer Report

You can find the report design file, here: thermochart.jrxml

Jasper Reports - Gantt Chart

Jasper Reports framework uses JFreeChart, to dynamically generate charts in a Java application. In this post, I'm gonna use the Java beans as data holders and pass a collection of these beans, to the Jasper reporting engine, to dynamically generate these reports. Below is the code for the Java bean:

package com.chetty.reporting.beans;

import java.util.Date;

public class GanttChartBean {
private String series;
private String task;
private Date startDate;
private Date endDate;

public void setSeries(String series) {
this.series = series;
}

public String getSeries() {
return series;
}

public void setTask(String task) {
this.task = task;
}

public String getTask() {
return task;
}

public void setStartDate(Date startDate) {
this.startDate = startDate;
}

public Date getStartDate() {
return startDate;
}

public void setEndDate(Date endDate) {
this.endDate = endDate;
}

public Date getEndDate() {
return endDate;
}
}


And below is the code for the Java class, that holds the business logic for creating these beans:

package com.chetty.reporting.business;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

import com.chetty.reporting.beans.GanttChartBean;

public class GanttChartBeanMaker {
public ArrayList getGanttChartData() {
ArrayList ganttChartDataList = new ArrayList();

Calendar calendar = Calendar.getInstance();

for(int i=1;i<8;i++) {
calendar.set(Calendar.HOUR_OF_DAY, i*1);

Date startDate = calendar.getTime();

calendar.set(Calendar.HOUR_OF_DAY, i*3);

Date endDate = calendar.getTime();

String series = (i % 2 == 0) ? "High Priority" : "Normal";

ganttChartDataList.add(create(series, "Meeting" + i, startDate, endDate));
}

return ganttChartDataList;
}

private GanttChartBean create(String series, String task, Date startDate, Date endDate) {
GanttChartBean ganttChartBean = new GanttChartBean();

ganttChartBean.setSeries(series);
ganttChartBean.setTask(task);
ganttChartBean.setStartDate(startDate);
ganttChartBean.setEndDate(endDate);

return ganttChartBean;
}
}
And below is the main class, which passes the collection of beans, to the Jasper reporting engine, to dynamically generate the Gantt Chart:
package com.chetty.reporting.engine;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.chetty.reporting.beans.GanttChartBean;
import com.chetty.reporting.beans.PieChartBean;
import com.chetty.reporting.business.GanttChartBeanMaker;
import com.chetty.reporting.business.PieChartBeanMaker;

import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

public class Reporter {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream ("reports/ganttchart.jrxml");

GanttChartBeanMaker ganttChartBeanMaker = new GanttChartBeanMaker();
ArrayList ganttChartBeanList = ganttChartBeanMaker.getGanttChartData();

JRBeanCollectionDataSource beanColDataSource = new JRBeanCollectionDataSource(ganttChartBeanList);

Map parameters = new HashMap();

JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, beanColDataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, "c:/reports/ganttchart.pdf"); 
}
}
When you run the code, the following report is generated: Gantt Chart The above report uses a different theme, which is not present in the jasperreports.jar file. To use that theme, you would have to include the 'jasperreports-chart-themes.jar' file. You can find the report design file, for the above report, here: ganttchart.jrxml

Jasper Reports - Example

Jasper Reports is one of the best 'Business Intelligence (BI)' tools/frameworks, which can be used for generating reports for Java/Groovy applications. It is just partly 'Open Source', but if you can practice or have good enough work experience in Java, you can easily build basic/complex reports.

'Jasper Reports' framework can handle different kinds of data sources, but in this post I will show how to generate a basic report, just by passing a collection of Java data objects (Java beans), to the Jasper Report Engine. Below is the code for the data object (Java bean):

package com.chetty.reporting.beans;

public class DataBean {
private String name;
private String occupation;
private String place;
private String country;

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

public String getName() {
return name;
}

public void setOccupation(String occupation) {
this.occupation = occupation;
}

public String getOccupation() {
return occupation;
}

public void setPlace(String place) {
this.place = place;
}

public String getPlace() {
return place;
}

public void setCountry(String country) {
this.country = country;
}

public String getCountry() {
return country;
}
}


And below is the code for the class file with business logic to generate a collection of java bean objects, which is further passed to the Jasper report engine, to generate the report:

package com.chetty.reporting.business;

import java.util.ArrayList;

import com.chetty.reporting.beans.DataBean;

public class DataBeanMaker {
public ArrayList getDataBeanList() {
ArrayList dataBeanList = new ArrayList();

//dataBeanList.add(produce("Babji, Chetty", "Engineer", "Nuremberg", "Germany"));
dataBeanList.add(produce("Albert Einstein", "Engineer", "Ulm", "Germany"));
dataBeanList.add(produce("Alfred Hitchcock", "Movie Director", "London", "UK"));
dataBeanList.add(produce("Wernher Von Braun", "Rocket Scientist", "Wyrzysk",                                               "Poland (Previously Germany)"));
dataBeanList.add(produce("Sigmund Freud", "Neurologist", "Pribor", "Czech Republic (Previously Austria)"));
dataBeanList.add(produce("Mahatma Gandhi", "Lawyer", "Gujarat", "India"));
dataBeanList.add(produce("Sachin Tendulkar", "Cricket Player", "Mumbai", "India"));
dataBeanList.add(produce("Michael Schumacher", "F1 Racer", "Cologne", "Germany"));

return dataBeanList;
}

private DataBean produce(String name, String occupation, String place, String country) {
DataBean dataBean = new DataBean();

dataBean.setName(name);
dataBean.setOccupation(occupation);
dataBean.setPlace(place);
dataBean.setCountry(country);

return dataBean;
}
}


And below is the main class file, which gets the java bean collection from the class (with business logic) and passes it to the Jasper report engine, to generate the report:

package com.chetty.reporting.engine;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.chetty.reporting.beans.DataBean;
import com.chetty.reporting.business.DataBeanMaker;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.xml.JRXmlLoader;

public class Reporter {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream ("reports/test_jasper.jrxml");

DataBeanMaker dataBeanMaker = new DataBeanMaker();
ArrayList dataBeanList = dataBeanMaker.getDataBeanList();

JRBeanCollectionDataSource beanColDataSource = new 
JRBeanCollectionDataSource(dataBeanList);

Map parameters = new HashMap();

JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, beanColDataSource);
JasperExportManager.exportReportToPdfFile(jasperPrint, "c:/reports/test_jasper.pdf"); 
}
}


To design the jasper report design file, you can either use iReport or the JasperAssistant Plugin for Eclipse. I used iReport, as it has more features and options. You can find the jasper report design file here: test_jasper.jrxml

When you run the 'Reporter' class, the following pdf file is generated:

Test Jasper

You can find all the source files here: jaspertest