Jasper Reports is an open source tool used to generate printable reports from a data source and XML report definition files. The webwork/Jasper Reports integration allows you to use Webwork actions as data sources for Jasper Reports rather than talking directly to a database. This allows an easy way to provide reports using any data that your actions provide and may even allow you to create reports without changing your current business logic or action code at all. This is one of the benefits of MVC.
The webwork Jasper Reports support also allows access to the Jasper Reports subreport feature so that you can produce reports within reports to an arbitrary level of nesting. This allows a great deal of flexibility in how your reports are set up.
It is recommended that you obtain the latest source distribution of Jasper Reports (0.44 at the time of writing) from sourceforge as this contains a number of the jar files that you will need to place in the lib directory of your application to support Jasper Reports. These jars are also included with the webwork distribution for your convenience. If you are creating your own web applications you will need to package these jars in your WEB-INF/lib directory . The following jar files are required:
In order to use a Jasper Report as a mapped view from a Webwork action the action needs to provide an iterable property containing the data to be reported on. An iterable property is defined as any one of the following types:
Note: If you want to extract data from both the top level of your action and from objects contained in an iterable property
you will either have to use subreports or navigate the webwork value stack using
the expression language.
Write the Report
Jasper Reports uses report definition xml files that are compiled ahead of time and then "filled" using a data source provided to them by an application. The WebWork/JasperReports integration uses the webwork value stack as the data source and fills the report as part of the view process. This requires a couple of variations on normal Jasper Reports practice when producing reports to be used in conjunction with WebWork: fields and subreports (discussed below).
Fields are defined in Jasper Reports by use of the XML field
tag in the report definition file. The following is an example field
definition:
<field name="customerName" class="java.lang.String"> <fieldDescription>customer/name</fieldDescription> </field>Webwork uses the fieldDescription tag to get the information from the value stack using the expression language. This allows you to use object navigation to get the exact piece of data you require from your action and all associated objects. See the Expression Language Reference for further details on the syntax of the expression language.
Note: It is not necessary to put single quotes around the expression language as is sometimes necessary when writing a JSP view.
Note: For backward compatibility with earlier implementations, the field name itself is used as the expression language if no fieldDescription tag is present. In this case expression language characters that are not valid in Java identifiers cannot be used.
After you have defined your field you can use the field data value in Jasper Reports expressions by using the syntax:
$F{<field name>}where <field name> is the name of the field contained in the
name
property
in the field
tag
above.
The object that is at the head of the value stack and hence the starting point for your expression language navigation will be one of the objects in the collection found in the iterable property described in the section on Writing the Action above.
If you would like more instant graphical feedback when designing your report you may want
to look at the JasperEdit project
on SourceForge
Compile the Report
After you have produced a report definition it has to be compiled in order for Jasper Reports to use it. You can either use the method specified in the Jasper Reports documentation or use the report compilation utility provided with webwork for the purpose.
The report compilation utility is contained in the class webwork.view.jasperreports.CompileReport
and takes
command line arguments of the report definition XML files to compile. The following is an example of an ant task that uses
this class:
<!-- =================================================================== --> <!-- Compiles the Jasper Reports code --> <!-- =================================================================== --> <target name="jasperreports" depends="compile"> <java classname="webwork.view.jasperreports.CompileReport" fork="true"> <sysproperty key="org.xml.sax.driver" value="org.apache.xerces.parsers.SAXParser"/> <arg value="${src.dir}/resources/web/tests/jasperreports/orderReport.xml"/> <arg value="${src.dir}/resources/web/tests/jasperreports/orderList.xml"/> <classpath refid="jasperreports.class.path" /> </java> </target>
Note the definition of the SAX parser to use in the sysproperty
tag (equivalent to using -D on the command line).
There have been some reports of problems using the crimson XML parser so we recommend that you use Xerces
for report compilation if you are having problems.
Place the compiled report(s) (the *.jasper
files) in the same location as any JSP or other view files for your web application.
The webwork view mapping is where you tie the result returned by your action to the report definition you have compiled. View mappings with the
.jasper
extension are mapped by webwork to jasper reports. The following is an example of a views.properties mapping for a report called
orderList.xml
. actions.xml mappings are similar.
jasperTest.action=jasperreports.OrderList jasperTest.success=orderList.jasper?dataSource=orders
This mapping specifies that the action jasperreports.Orderlist
(which may have a package prefix defined in the webwork.action.packages
property of the webwork.properties
file) will use the report orderList.jasper
and use the property orders
as the data
source for data to iterate over. This is achieved by converting the iterable property to an iteration and
pushing each object onto the Value Stack in turn. Each time Jasper Reports requests a new data
value the previous one is popped off the value stack and a new one is pushed. See the section on
action classes for a list of the
property types that are considered iterable. This means that field references in the report
itself will start with the current
object in the iteration and not with the original action.
For example, lets say you had an action class foo with an iterable property called bar, which contained a java.util.List
of foobar objects.
Let's also say that you defined an action mapping of the form foobarReport.jasper?dataSource=bar. This would mean that if you had a field definition
in your report named myData then Jasper Reports would call the method getMyData() on all of the foobar objects (retrieved from the foo object by calling
its getBar() method) in the list in turn for each iteration through the report. This would also mean that when the list of foobar objects is exhausted
then the report would stop.
If you need to access fields on the original action object you can always use the ..
expression language construct to get to it (because the next object on gthe stack is the original action).
Note: If you do not specify a dataSource parameter or if the dataSource you specify is not iterable then webwork will most
probably throw a ClassCastException when it tries to cast it to a java.util.Iterator
. If you actually need a report to work off a
single instance of your action the way to get around this is to implement java.util.Iterator on your action in such a way that only a single
instance is returned.
Jasper Reports has the ability to output report data in the following formats:
jasperTest.action=jasperreports.OrderList jasperTest.success=orderList.jasper?dataSource=orders&format=XLSWould produce a report formatted for Microsoft Excel. The possible values for the format parameter are just the file extensions, that is:
Sometimes you want to be able to iterate over two or more levels of items in a report. An example of this is where you want to report on a number of orders and each of those orders has a number of line items. The way that you do this using Jasper Reports is to use subreports. See the subreport example in the Jasper Report source distribution for an example of a subreport.
The way that subreports know what to iterate over to get their data is by using the dataSourceExpression
tag inside the subreportExpression
tag in the report definition XML file. For webwork what you need to do is to give the subreport
a data source that will iterate over the
sub items to be reported on. For example if you had an order object on top of the value stack you would want to
pass a data source that would iterate over all of the line items in the order for your subreport.
To achieve this in webwork a little sleight of hand is necessary. In the section on writing the report you learned how to write expression language to define a field and return its value. What we didn't tell you then was that if you defined a field that turned out to be an iterable object like an array or a list the value that will be returned is not actually an array or a list. Instead it will be a new data source ready to iterate over the property you specified in your original query. This can then be used to pass the data to the subreport.
For example if there is an Order
object with a property called lineItems whose
getLineItems()
method returns a java.util.List
then a field definition of the form:
<field name="lineItems" class="java.lang.Object"> <fieldDescription>lineItems</fieldDescription> </field>would evaluate to an instance of a
dori.jasper.engine.JRDataSource
object ready to
iterate over the line items rather than
a list of line items. In this example you would define the data source for your subreport as follows:
<dataSourceExpression> $F{lineItems} </dataSourceExpression>
The other piece of the puzzle is how to define where the definition of your subreport is located.
This is achieved by the setting of a
special report parameter called reportDirectory
. Webwork sets this parameter for you when
it calls your top level report so you need to
define it in your report definition in order to get its value. This can be accomplished using the following:
<parameter name="reportDirectory" class="java.io.File"/>This gives you the directory on the server where webwork found your report definition. You can then use this parameter in the
subreportExpression
tag inside the subreport
tag to reference the file where your compiled subreport
definition is. For example if your
subreport was called orderReport.jasper and it was in the same directory as the master report
you were calling it from you could
use:
<subreportExpression class="java.lang.String"> $P{reportDirectory}.getCanonicalPath() + java.io.File.separator + "orderReport.jasper" </subreportExpression>to define the location of your subreport.
Internet Explorer sometimes has trouble recognizing that the byte stream returned from an action is a PDF and not some other form of data. To workaround this either change browsers :) or change your HTTP request from a POST to a GET. This may mean that your URL ends up being a bit ugly but at least you will get to see your PDF.