Collections of web application techniques

Friday, July 24, 2009

Using JasperReports in JSF Applications

JasperReports is a reporting tool that can be used to generate reports in multiple formats such as PDF for printing and CSV for manipulation in a spreadsheet tool. This blog describes how you can integrate a JapserReport file (.jrxml) into a JSF-based application.

Although the jrxml template is just a text file, it is easier to use a tool such as iReport. iReport is good for general layout and can save you from tedious work but it is not perfect. Occasionally it is better to edit the xml file directly. GVim is an excellent text editor if you have a Linux background but any other text editor will do the job.

The jrxml file should be copy into a directory readable by the class loader. I put mine in /WEB-INF/reports. You can use the ReportGenerator class included to generate the desired report.

Typically a report is generated when a user clicks a button or a link which invoke an action listener. The action listener method then calls the ReportGenerator’s printReport() and a report in the desired format (HTML, PDF, CSV, EXEL) is returned to the user. PDF format with JasperReports is excellent as it uses absolute positioning. However HTML and EXEL are not very good as the outcomes can be surprisingly different. I recommend that you provide PDF for printing and CSV for data manipulations to the users as those two formats seem to work best.

Below is an example on the variation that a report should be opened on a new window after user fill out the criteria on a form. To make it a little more user friendly, error checks were performed on the back end and will be displayed on the same page. Therefore, we can’t just set the target to _blank to force the browser to open a new window whenever an action is submitted.

In your form, you might have a button like this:

<h:commandButton value="Generate" action="#{report.submit}"/>


Your action listener then validates the inputs and if there is any error, it will be sent back to the same page:

public final String submit() {
if (!criteria.isValid()) {
facesMessages.add(Severity.ERROR, "Some error messages"
return ERROR;
}

return NEXT;
}


When everything is well, we can pass control to the report generator. Wait! What if users had some errors previously which where displayed on the form? Clearing them would be good.


Here is an example that works for JBoss Seam (if you are not, you definitely need to check it out). The trick is to reload the form and open the new window for the report. The navigtion rule below specifies that the view will be report.jsp.


<navigation from-action="#{report.submit}">
<rule if-outcome="next">
<redirect view-id="/app/report/report.jsp"/>
</rule>
</navigation>


Below is the content for report.jsp. It refreshes the main form (reportForm.jsp) and opens display.jsp.

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script language="JavaScript">
function displayReport() {
reportUrl = "display.x";
newWin = window.open(reportUrl,"_blank",
"location=0,resizable=1,status=1,scrollbars=1,menubar=1,toolbar=0");
newWin.focus();

document.getElementById("report:location").click();
}
</script>
</head>
<body onload='displayReport()'>
<h:form id="report">
<s:button id="location" style="display:none" view="/app/report/reportForm.jsp" value="return" propagation="join">
</s:button>
</h:form>
</body>
</html>

</ui:composition>


Below is the content for display.jsp:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Expires" content="-1"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script language="JavaScript">
function displayReport() {
document.getElementById("report:location").click();
}
</script>
</head>
<body onload="document.body.style.cursor='wait'; displayReport();">
<h:form id="report" >
<div style="width:90%; padding:5px; margin:40px; border: 1px solid #FFCC00; background-color: #F0F8FF;">
<h:outputText value="Please wait..."/>
<h:commandButton id="location" style="display:none" action="#{report.display}"/>
</div>
</h:form>
</body>
<header>
<meta http-equiv="Expires" content="-1"/>
</header>
</html>
</ui:composition>


Displaying a message with some progress indicator would be even better on this last page. To do that, you can specify a refresh rate in this last page to get the progress. This is much easier than create a thread on the server end.

<meta http-equiv="refresh" content="1"/>

And here is the method display invoking the report generator:

public final String display() {
ReportPreparer prep = new ReportPrepFactory().getPreparer(criteria);

String template = "sample"; // Suppose your jrxml file is sample.jrxml

Map p = new HashMap(); // if your report has parameters, they should be set here

ReportGenerator gen = new ReportGenerator();
if (gen.printReport(
"P", // P for PDF
p,
new StringBuffer("/WEB-INF/reports/").append(template)
.append(".jrxml").toString(),
template)) {
return NEXT;
}

return ERROR;
}


This is the code to generate report:

5 comments:

  1. IS THIS CODE VALID WITH SEAM ?

    ReplyDelete
  2. Hi sir,

    Could you give us explanation how about if report running in JSF +EJB3+JPA using EJBQL connection in iReport?
    because i already try but it's seem still cannot connect using EJBQL connection.
    Iam using NB7.0.1, iReport 4.0.2 on MacOSX Lion with JDK 1.6.0_26

    Thank you sir.

    my Email : ferilauw@gmail.com

    ReplyDelete
  3. how to call the reportGenerator class. Thanks

    ReplyDelete
  4. I need jasper report with jsf running on web application. The given class was exactly what I needed by I dont know how use it. Please help, I needed it ASAP. Thanks :)

    ReplyDelete
  5. Good stuff. How about if I want to create a PDF and just shoot it out to a printer without ever displaying it?

    ReplyDelete