Generating online reports using JasperReports and WebSphere Studio

Until recently, you had two options for the common requirement to generate Web reports from business data in an enterprise repository -- design and write the code from scratch, or buy a commercial reporting product such as Crystal Reports. This article explores a third option: using WebSphere Studio with JasperReports, the open-source Java tool that uses XML templates to generate reports that can be saved as PDF or CSV, displayed in a browser, or sent to a printer. A sample Web application shows you how to create reports in HTML and PDF, using DB2 as the enterprise data repository and WebSphere Application Server to deploy the application.

Share:

Ricardo Olivieri (roliv@us.ibm.com), Software Engineer, IBM Austin Lab

Ricardo Olivieri is a Software Engineer in IBM Global Services. He is part of the development team for the Mixed Address Database application (a registration tool for IBM network devices). His areas of expertise include design and development of enterprise Java applications for WebSphere Application Server, administration and configuration of WebSphere Application Server, and distributed software architectures. He is a certified Java developer and a certified WebSphere Application Server administrator. He has a B.S. in computer engineering from the University of Puerto Rico. You can reach Ricardo at roliv@us.ibm.com.



17 November 2004

Introduction

Generating online reports from business data stored in an enterprise repository is a common business requirement for application developers. This article shows you how to address this requirement using JasperReports. This popular open source Java™ reporting tool uses XML templates to generate reports that can be saved as PDF or CSV, displayed on a browser, or sent to a printer. In this article, a sample Web application shows you how to use JasperReports with IBM® WebSphere® Studio Application Developer (hereafter called Application Developer), to create dynamic reports in HTML and PDF. You will deploy the sample application to WebSphere Application Server V5.1, and use DB2 UDB as an enterprise data repository. The article provides an SQL script file to create and populate the database tables for the sample application. To gather the data that will go into the report from the DB2 database, JasperReports uses an SQL query.


Assumptions

You should be familiar with Application Developer and its J2EE perspective, and know how to import EAR files into the workspace. Knowledge of the directory structure of an expanded EAR file is also required. You should also know how to define datasources and how to deploy an EAR application to a WebSphere Application Server V5.1 test instance, and have a good understanding of XML and SQL.


Install and run the sample Web application

Download and unzip the ZIP file provided with this article. It contains create-db.sql and ReportsDemoEAR.ear. Use the following command to run create-db.sql to create and populate a sample database named reportdb:

db2 -svtf create-db.sql

The sample application is contained within an EAR file, so installation in Application Developer should be straightforward. Import ReportsDemoEAR.ear into your workspace, using the default options offered by Application Developer. A new Web project named ReportsDemoWEB should appear in your workspace. Navigate to this project and go to the WebContent/WEB-INF/lib folder, where you should see all of the JAR files required by JasperReports. This article uses JasperReports V0.6.1 -- for information on other versions, see the JasperReports home page. For information on JasperReports dependencies, click on Requirements on the left nav of the JasperReports home page.

The next step is to configure a DB2 UDB datasource for the WebSphere Application Server V5.1 test instance where you will run the sample Web application. Specify jdbc/reportDB as the JNDI name and reportdb as the database name in the definition for the datasource -- otherwise, the application will not be able to find the datasource.

Deploy the sample application to your test server instance and start it. Once the test server instance is up and running, right-click on the ReportsDemoWEB project and select Run on server. An HTML page named report-welcome.html will open and enable you to select one of six different reports to view:

Figure 1. Welcome page for sample application.
Welcome page

The first three options in the pull-down create online HTML reports, while the last three generate the same reports in PDF. Here is the structure of the three different reports generated by the sample application:

Basic report

A basic report contains a list of all customers in Texas for a fictional company named XYZ:

Figure 2. Basic Report.
Basic Report

Group report

A group report has groups of data inserted into the report, each with its own header and footer section. This example report shows all customers for company XYZ, grouped by state:

Figure 3. Group Report.
Group Report

Multi report

A multi report merges multiple reports into one. Each individual reports uses its own SQL query to pull data from the reportdb database. This example report shows the order history and customer referral history for a specific customer:

Figure 4. Multi Report.
Multi Report

Generating reports

To generate a report using JasperReports, you need to create an XML document that defines the composition of the report. Think of this XML document as a reusable template that the reporting engine populates with data that it extracts from the enterprise repository. You use this template file to provide the information that the reporting engine needs to create the report, such as a query string, fonts, report title, names of image files, page headers and footers, column headers, and a summary section. Creating this file will occupy most of your time when you use JasperReports to generate reports.

The structure of a JasperReports XML template is defined in an XML DTD file named rjasperreport.dtd that is included in the JasperReports distribution. This DTD contains the different sections that can be included in the XML template. Short descriptions of the most important sections are included below. Each of the sections except for queryString has a specified height and width, and can contain report objects such as lines, rectangles, images, and text fields:

Title
Specifies the title for the report. It appears only once at the beginning of the report.
queryString
Specifies the SQL query that will be used to obtain the data to fill the report. This section of the XML template does not have a height or a width since it is not a visible section in the report.
PageHeader
Defines a page header that is printed at the beginning of each page on the report.
PageFooter
Defines a page footer that is printed at the bottom of each page on the report.
ColumnHeader
Specifies what information should be displayed as heading before inserting into the report the data that is retrieved from the data repository.
ColumnFooter
Specifies what information should be displayed as footer after inserting into the report the data that is retrieved from the data repository.
Group
Defines a collection of related records in the report. As part of this definition, you have to specify the criterion for grouping the records. For instance, if you are creating a report that includes all the customers for a company and you would like to group together the customers by state, then state is your criterion.
GroupHeader
Specifies the contents of the header section for a group.
GroupFooter
Specifies the contents of the footer section for a group.
GroupFooter
Specifies the contents of the footer section for a group.
Detail
Specifies what data from the data repository should be included in the report. The <detail> element on the XML template is applied to each line of data supplied by the report's data source.
Summary
Specifies what information should be shown at the end of the report.

As a reference, go over the XML template for the Basic Report and Group Report. Look at the different sections and XML elements defined in these documents. Once you have written your XML template file, then you can start generating reports in your Web application. Here are the high-level steps you need to implement in your Web application code:

  1. The first step is to set the JVM property jasper.reports.compile.class.path so that it includes the JasperReports distribution JAR file as well any supporting classes you may have created for the report:
    // Set class path for compiling XML templates
    System.setProperty(
    	"jasper.reports.compile.class.path",
    	context.getRealPath("/WEB-INF/lib/jasperreports-0.6.1.jar")
    		+ System.getProperty("path.separator")
    		+ context.getRealPath("/WEB-INF/classes/"));
  2. Before you can use them to generate reports, XML templates must be "compiled" to verify their validity and consistency. Compiling an XML template yields a serialized object that you can use multiple times on different collections of data. Think of this serialized object as an in-memory representation of the XML template that the reporting engine uses whenever it needs to create a report. The reporting engine uses this serialized object to generate the SQL query it should execute and lay out in the report the results obtained from the data repository.

    Next, you need to specify a default directory to store the object that represents a compiled XML template. Whenever an XML template is compiled and written to the file system, it will be saved in this folder with the extension .jasper. Specify this folder's name by setting a value for the JVM property jasper.reports.compile.temp:

    // Specify a default folder for storing
    // compiled XML templates
    	System.setProperty(
    		"jasper.reports.compile.temp",
    		context.getRealPath("/reports/"));
  3. Generate a compiled version of the XML template and store it either in memory or in the file system. The following code shows how to compile an XML template and write the resulting object to the file system:

    JasperCompileManager.compileReportToFile(
    	getServletContext().getRealPath(
    	"/reports/BasicReport.xml"));

    As mentioned above, the generated file will have the extension .jasper, with the filename determined by the name attribute of the <jasperReport> element in the XML template.

  4. Load the compiled object into memory using the JRLoader.loadObject method, which returns a JasperReport instance:

    File reportFile = new File(getServletContext().getRealPath(
    	"/reports/BasicReport.jasper"));
    JasperReport jasperReport =
    	(JasperReport) JRLoader.loadObject(reportFile.getPath());

    As mentioned before, a compiled version of the report can be used many times, so you need to compile your XML template only once. Then you can load it from the file system and keep it in memory for as long as the program runs. Each time you need to generate a dynamic report, the reporting engine can use the same compiled version to create a new report -- the data repository is accessed, data is retrieved from it, and a report is created from the compiled version.

  5. Use the JasperManager.fillReport method to generate a report. This method returns a JasperPrint object, which represents an instance of a report:

    JasperPrint jasperPrint =
    	JasperManager.fillReport(jasperReport, parameters, conn);

    The jasperReport argument is an instance of a compiled XML template. The parameters argument is a java.util.Map instance that contains the parameters that will be passed into the report. JasperReports lets you pass object references when creating a report from a compiled report object, which is useful when you need to modify report data at run time, such as the report title, or a WHERE clause of the query string that will be used to collect the data from the data repository. Think of these parameters as data that probably won't be available in the information collected from the data repository. To pass in parameters, create an instance of the java.util.HashMap class, put the parameters inside the HashMap instance, and then pass this object as the second parameter to the JasperManager.fillReport method, as shown above. The conn parameter is a java.sql.Connection instance that points to the data repository, in our case the reportdb database. This java.sql.Connection object is obtained from the datasource object that was defined earlier (whose JNDI name is jdbc/reportDB).

  6. Once you have an object reference to a report (a JasperPrint object), you can export the contents of the report in your choice of formats. Use the exportReport methods provided in the classes that implement the net.sf.jasperreports.engine.JRExporter interface to export the report in PDF, XML, or HTML. JasperReports provides several concrete classes that implement this interface. A reference to the JasperPrint instance obtained from the JasperManager.fillReport method is passed to the JRExporter instance. The following code shows you how to export a report in HTML using the JRHtmlExporter class. For a more thorough explanation of this code, see A deeper look into the sample application below.

    JRHtmlExporter exporter = new JRHtmlExporter();
    exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
    exporter.setParameter(
    		JRExporterParameter.OUTPUT_WRITER,
    		response.getWriter());
    Map imagesMap = new HashMap();
    request.getSession().setAttribute("IMAGES_MAP", imagesMap);
    exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesMap);
    exporter.setParameter(
    		JRHtmlExporterParameter.IMAGES_URI,
    		"image?image=");
    exporter.exportReport();

A deeper look into the sample application

Images

In the sample.servlets package (under the Java Resources folder in the ReportsDemoWEB Web project), you will find the ImageServlet class, which was written by Teodor Danciu (author of the JasperReports library). It is included in the JasperReports distribution package as part of the code for one of the sample applications. This servlet is needed as part of your Web application in order to include images in your HTML reports (this servlet serves the images that are included in your report). The URL mapping for the ImageServlet must match the path value you specify for the JRHtmlExporterParameter.IMAGES_URI variable. As shown in the previous code sample, you must specify a value for this variable when generating an HTML report. In our case, the URL mapping for the ImageServlet is /image, which means we need the value of the JRHtmlExporterParameter.IMAGES_URI variable to be "image?image=":

exporter.setParameter(
		JRHtmlExporterParameter.IMAGES_URI,
		"image?image=");

If the URL mapping for the ImageServlet were /servlets/image, then the value for JRHtmlExporterParameter.IMAGES_URI should be "servlets/image?image=" instead of "image?image=" (see the generateHtmlOutput method in the ReportServlet class).

For the ImageServlet to be able to serve images, place a java.util.Map instance identified by a key value of IMAGES_MAP in the HTTP session object. Also, you must pass a reference to this object to the JRExporter instance. The JRExporter object will then populate the map object with the images needed by the report:

request.getSession().setAttribute("IMAGES_MAP", imagesMap);
exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, imagesMap);

To insert an image in your report, include the <image> element in your XML template. Inside this element, specify the name of the image file you want to include. The Basic Report in the sample application includes the customer.gif image at the top left of report:

<image scaleImage="Clip" vAlign="Middle">
	<reportElement x="0" y="5" width="32" height="32" />
	<graphicElement />
	<imageExpression class="java.io.File">
		<![CDATA[new File($P{BaseDir}, "customer.gif")]]>
	</imageExpression>
</image>

In this example, the <imageExpression> sub-element uses a java.io.File object to locate the image file that needs to be loaded. To instantiate a java.io.File object, the File(File parent, String child) constructor is used. The first argument (BaseDir) that is passed to this constructor is enclosed by $P{}, because report parameters in JasperReports are referenced using $P{parameter_name}. JasperReports lets you pass object references inside a java.util.Map instance as parameters to a report. In this case, BaseDir is a passed-in parameter that points to the directory where the image file is located.

Even if you do not explicitly have any images in your HTML reports, you still need the ImageServlet in your Web application, because JasperReports by default uses several images to format an HTML report and it uses this servlet to get these images.

Multiple SQL queries

As described above, you use an XML template to specify the SQL query that you want to use to obtain the data for a report. In some cases, you might need more than one SQL query to obtain the data, and in those cases, you use the JasperReports sub-reporting feature and create a parent report that includes N sub-reports. Each of these reports (the parent report and the sub-reports) has its own SQL query.

The report named Multi Report in the sample Web application uses more than one SQL query to obtain its data. The output from this report contains three different pieces of information, each one in its own area of the document: 1) customer's name and location, 2) order history, and 3) customer referral history. The structure of each one of these document areas is defined by MultiReport.xml, OrderHistory.xml, and ReferralHistory.xml, respectively. MultiReport.xml is the parent XML template, while OrderHistory.xml and ReferralHistory.xml are the sub-report XML templates. The query string contained in MultiReport.xml simply gets the customer's name and location:

<queryString>
	<![CDATA[SELECT first_name, last_name, city, state FROM
	sample.CUSTOMER WHERE CUSTOMER_ID=$P{customer_id}]]>
</queryString>

The query string contained in OrderHistory.xml obtains the order history for the customer:

<queryString>
	<![CDATA[SELECT order_id, order_date, status, placed_by FROM
	sample.CUSTOMER_ORDER WHERE customer_id=$P{customer_id}]]>
</queryString>

The query string for the ReferralHistory.xml gets a list of all the customers that have been referred by this customer:

<queryString>
	<![CDATA[SELECT first_name, last_name, city, state, email FROM
	sample.CUSTOMER as a inner join sample.CUSTOMER_REFERRAL as b on
	(a.customer_id=b.referred_customer_id)
	where b.customer_id=$P{customer_id}]]>
</queryString>

To include a sub-report in a parent report, use the <subreport> element. Here is the section of the MultiReport.xml template that includes the two sub-reports:

<subreport>
	<reportElement positionType="Float" x="0" y="20" width="1"
	height="1" isRemoveLineWhenBlank="true" backcolor="#99ccff" />
	<subreportParameter name="customer_id">
		<subreportParameterExpression>
			<![CDATA[$P{customer_id}]]>
		</subreportParameterExpression>
	</subreportParameter>
	<connectionExpression>
		<![CDATA[$P{REPORT_CONNECTION}]]>
	</connectionExpression>
	<subreportExpression class="net.sf.jasperreports.engine.JasperReport">
		<![CDATA[$P{OrderHistory}]]>
	</subreportExpression>
</subreport>
<subreport>
	<reportElement positionType="Float" x="0" y="22" width="1"
	height="1" isRemoveLineWhenBlank="true" backcolor="#99ccff" />
	<subreportParameter name="customer_id">
		<subreportParameterExpression>
			<![CDATA[$P{customer_id}]]>
		</subreportParameterExpression>
	</subreportParameter>
	<connectionExpression>
		<![CDATA[$P{REPORT_CONNECTION}]]>
	</connectionExpression>
	<subreportExpression class="net.sf.jasperreports.engine.JasperReport">
		<![CDATA[$P{ReferralHistory}]]>
	</subreportExpression>
</subreport>

Each <subreport> element contains a <subreportExpression> sub-element. This sub-element specifies which report to load as a sub-report in the parent report. In our example, the parent report determines which sub-report to load by using two passed-in parameters: $P{OrderHistory} and $P{ReferralHistory}. These parameters are compiled versions (instances of the JasperReport class) of the OrderHistory.xml and ReferralHistory.xml XML templates, respectively. The parent report uses these parameters to reference each sub-report from a <subreport> element. Remember, JasperReports lets you pass object references to a concrete instance of a report. The following code shows how the compiled versions of the sub-reports are created and then passed into the parent report:

Map parameters = new HashMap();
...
// Get compiled version of OrderHistory.xml
JasperReport orderHistory = ...
// Get compiled version of ReferralHistory.xml
JasperReport referralHistory = ...
// Place compiled reports as parameters in map object
parameters.put("OrderHistory", orderHistory);
parameters.put("ReferralHistory", referralHistory);
// Get compiled version of MultiReport.xml
JasperReport multiReport = ...
// Object containing the parameters is passed in
// as an argument to the fillReport() method,
// which returns a concrete report instance
JasperPrint jasperPrint =
	JasperManager.fillReport(multiReport, parameters, conn);

Another important capability of the sub-reporting feature is that you can pass in parameters from the parent report to sub-reports using the <subreportParameter> sub-element:

<subreportParameter name="customer_id">
	<subreportParameterExpression>
		<![CDATA[$P{customer_id}]]>
	</subreportParameterExpression>
</subreportParameter>

In this example, the parent report is passing the customer_id parameter to a sub-report. A sub-report can then reference this parameter using the parameter notation explained earlier ($P{parameter_name}).

Finally, just as a parent report needs a connection to access the data repository to collect the data, sub-reports do as well. You can pass this connection from a parent report to a sub-report using the <connectionExpression> sub-element:

<connectionExpression>
	<![CDATA[$P{REPORT_CONNECTION}]]>
</connectionExpression>

REPORT_CONNECTION is a built-in system parameter that contains a reference to the java.sql.Connection object that is used by a report to access a data repository. Since it is a built-is system parameter, it is ready to be used in expressions.


Conclusion

JasperReports is a viable open-source option when you need to generate online reports from business data in an enterprise repository. It can deliver the report contents in several formats to the screen or a printer.

This article provided a sample Web application that generates three different types of reports in HTML and PDF formats. It also provided a DB2 UDB script to create the sample database that the application needs in order to execute and generate the reports. XML documents that conform to a pre-defined DTD define the composition of the reports. These XML documents are reusable templates that the reporting engine fills in with the data it extracts from the data repository. You can use the XML templates in the sample application as blueprints for you own reports, choosing the template most appropriate for your requirements. Regardless of the output format of the report, the XML template is the same.


Download

DescriptionNameSize
Code samplereports-demo.zip  ( HTTP | FTP )1.8 MB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=31896
ArticleTitle=Generating online reports using JasperReports and WebSphere Studio
publish-date=11172004