Menu

Custom-Report

Philippe Le Berre

Create customized reports with JasperReport

How to write a report

ESIS Framework relies on JasperReports for all that relates to reporting.

Reports are at minimum in 2 parts:

  • A main JasperReport, and optionnally subreports, which declares how data will be displayed on the report.

  • A Java class, which fills the JasperReport with data.

JasperReport description

Reports are written as an XML file describing the way different components are displayed on a report. There is then a compilation phase that create a .jasper file, which is a Java serialized object thus it is possible to call methods on parameters and fields. For a more details on JasperReport, a good reference is the The Jasper Reports Ultimate Guide.

A report has several parts. Here is the main structure.

 <jasperReport>
   <import>
   <parameter>
   <field>
   <title>
   <pageHeader>
   <columnHeader>
   <detail>
   <columnFooter>
   <pageFooter>
   <summary>
 </jasperReport>

The main tag is <jasperReport> : its attributes specify the size of the report, etc.

Each <import> tag specify a java library to import, for instance the Java Date:

<import value="java.util.Date" />

Each <parameter> tag defines a parameter that can be passed from the Java call, for instance :

<parameter name="title" isForPrompting="false" class="java.lang.String"/>

Each <field> corresponds to a field in the datasource. In the example, the datasource is an array of VulnerabilityInfoLine beans, so one must declare the attributes of VulnerabilityInfoLine that it wants to display.

<field name="vulnName" class="java.lang.String"/>
<summary>field name="description" class="java.lang.String"/>

The other tags will contain informations on how to dispose elements on the report.

Each of them are disposed vertically on the report as <band></band> of different heigth. All graphical elements are contained in band elements.

The band is displayed at the beginning of the report.

The <pageHeader></pageHeader> band is displayed at the beginning of each page.

The <columnHeader></columnHeader> is displayed at the begining of each column. Usually, reports have only 1 column and this band is not displayed. The column count is an attribute of the jasperReport tag.

The <detail></detail> band is displayed for each iteration of the datasource. If the datasource has been constructed from a Java collection c, the detail band will be repeated c.size() times. If the datasource has been created from a Java array ar[], the detail band will be repeated ar.length.

The <columnFooter></columnFooter> is displayed at the end of each column.

The <pageFooter></pageFooter> is displayed at the end of each page.

The <summary></summary> band is displayed at the end of the document.

It is also possible to group the dataset against one or more of these fields.

Example

Imagine a report displaying a list of vendors/products/versions. The datasource contains 3 fields :

<field name="vendor" class="java.lang.String"/>
<field name="product" class="java.lang.String"/>
<field name="version" class="java.lang.String"/>

With a classic organisation, we could display a list of vendors/products/versions like

Sun Solaris 8
Sun Solaris 9
Sun Solaris 10
Sun Java 1.5
Microsoft Word 2003

But by declaring 1 group for the vendor, and 1 group for the product, we can have a display like

Sun
     Solaris
          8       9       10
       Java
          1.5
Microsoft
      Word
          2003
<group  name="vendor" isReprintHeaderOnEachPage="true" >
            <groupExpression><![CDATA[$F{vendor}]]></groupExpression>
            <groupHeader>
            <band height="20"  strechType="Stretch" >
                <rectangle>
                    <reportElement
                        mode="Opaque"
                        x="0"
                        y="0"
                        width="535"
                        height="20"
                        forecolor="#8080FF"
                        backcolor="#000000"
                        key="rectangle"
                        positionType="Float"/>
                    <graphicElement stretchType="NoStretch" pen="None"/>
                </rectangle>
                <textField isStretchWithOverflow="false" pattern="" isBlankWhenNull="false" 
                                                    evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
                    <reportElement
                        mode="Transparent"
                        x="0"
                        y="1"
                        width="535"
                        height="17"
                        forecolor="#FFFFFF"
                        backcolor="#FFFFFF"
                        key="textField"
                        isPrintInFirstWholeBand="true"
                        isPrintWhenDetailOverflows="true"/>
                    <box topBorder="None" topBorderColor="#000000" leftBorder="None" leftBorderColor="#000000" 
                                                             rightBorder="None" rightBorderColor="#000000" bottomBorder="None" 
                                                             bottomBorderColor="#000000"/>
                    <textElement verticalAlignment="Middle">
                        <font size="14"/>
                    </textElement>
                <textFieldExpression   class="java.lang.String"><![CDATA[$F{vendor}]]></textFieldExpression>
                </textField>
            </band>
            </groupHeader>
            <groupFooter>
            <band height="1"  strechType="Stretch" >
            </band>
            </groupFooter>
        </group>

In this example, a group based on the value of the vendor field (<groupExpression></groupExpression>) is defined. Then a groupHeader and groupFooter will be displayed before and after several details sections. As one can see, there is a text element with the vendor inside in the header.

How to use parameters or fields in a report ?

Fields can only be used in the detail band, or in the definition of a group. Parameters can be used everywhere except in a group definition.

The different data type that can be directly incorporated in a report are text, images, or subreports.
So parameters or fields that we want to display have to be transformed in these types.

To get access to a string parameter in a dynamic text field,

<textFieldExpression   class="java.lang.String"><![CDATA[$P{title}]]></textFieldExpression>

will display in the containing text field the value of the 'title' parameter.

<textFieldExpression   class="java.lang.String"><![CDATA[$F{vendor}]]></textFieldExpression>

will display in the containing text field the value of the 'vendor' field.

Generating Charts

To generate an image from a JFreeChart object (charting API),

<imageExpression class="java.io.InputStream">
<![CDATA[new ByteArrayInputStream(ImageEncoderFactory.newInstance(ImageFormat.PNG).encode($P{chart}.createBufferedImage(1000, 400)))]]>
</imageExpression>

Here we use the chart parameter which is declared with type

<parameter name="chart" isForPrompting="false" class="org.jfree.chart.JFreeChart"/>

To integrate a subreport,

<subreport  isUsingCache="true">
                    <reportElement
                        x="0"
                        y="150"
                        width="535"
                        height="5"
                        key="subreport"
                        positionType="Float"/>
                    <subreportParameter  name="chart">  
                                               <subreportParameterExpression>
                                                      <![CDATA[$P{vulnsTriageCumuledEvolution}]]>
                                                </subreportParameterExpression>
                    </subreportParameter>
                    <subreportParameter  name="title">
                        <subreportParameterExpression>
                                                          <![CDATA["Triage on vulnerabilities (cumul)"]]>
                                                  </subreportParameterExpression>
                    </subreportParameter>
                    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.JREmptyDataSource()]]></dataSourceExpression>
                    <subreportExpression  class="net.sf.jasperreports.engine.JasperReport"><![CDATA[$P{ChartReport}]]></subreportExpression>
                </subreport>

Each subreport parameter is declared separately with a subreportParameter tag.

The datasource is declared with the dataSourceExpression tag (and can be constructed from a field of the main report (if it is a list for example)).

The subreport is declared with the tag subreportExpression, in this case it is the parameter ChartReport, so that implies we passed this subreport as parameter of the main report.
Look at existing reports to have more samples.

The Java side

The filling class extends net.sourceforge.esisframework.export.jasper.ReportFiller or net.sourceforge.esisframework.export.jasper.DateLimitedReportFiller if the report has to be limited by a period.
A report has to be loaded, then filled, then exported.

Subreports can be get by their names
JasperReport tsSubReport = getReportFromName("ProcessSubReport-Chart");

A report takes data by 2 ways : parameters and datasource.
Reports parameters is a Map filled of parameters of any type by using the setParameter(String param, Object value) method.

A JasperReport (JRDataSource) datasource is the way to load a list of beans that could be displayed in a tabular way. Datasource can be constructed from bean arrays, bean list, Map arrays, Map list, etc. More documentation for Datasource can be found in the [http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/JRDataSource.html JasperReport javadoc].

 @Override
 public JRDataSource fillDateLimitedReport(JasperReport report) throws Exception {
       VulnerabilityInfoLine[] vulns = listNewVulnerabilities();
       setParameter(« title », « New vulnerabilities »);
        return new JRBeanArrayDataSource(vulns);
}

The''' ESIS 1.1 API''' introduce the PortalRequestObject as well as more helper API to simplify the custom Java code.

import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JREmptyDataSource;

import net.sourceforge.esisframework.export.jasper.DateLimitedReportFiller;
import net.sourceforge.esisframework.export.PortalHelper;
import net.sourceforge.esisframework.objects.portal.PortalObject;
import net.sourceforge.esisframework.objects.portal.Top;
import net.sourceforge.esisframework.objects.portal.TopLine;
import net.sourceforge.esisframework.objects.portal.ObjectType;
import net.sourceforge.esisframework.objects.portal.PortalRequestObject;

 @Override
    public JRDataSource fillDateLimitedReport(JasperReport report) throws Exception {
        _logger.info("Filling date limited report ("+this.getClass().getName()+")");

        initReport();

        // These are ESIS default sub report for Chart & Top list
        setParameter("SubReport-Chart",             getReportFromName("Chart-390-185"));
        setParameter("SubReport-Top",               getReportFromName("Top-390"));

        // Simple chart call
        setParameter("SystemsWithMalwareAlertsEvolution", callPortalChart("getNumberOfAssetsWithMalwaresEvolution"));

        // Simple top object
        setParameter("TopSystemsWithVirusAlerts",         callPortalTop("getTopAssetsWithMalwaresBetweenDaysVirus"));

        // Top object with dynamic parameter, so it can be used multiple time in the report
        setParameter("TopSmtpSender",             callPortalTop("getSmtpDomainsBetweenDays", new String[] { "sent", "mixed" }));

        // Chart with parameter
        setParameter("QuarantineSpamsEvolution",  callPortalMultipleChart("getEmailEvolution",
                                                                        new String[] {   "E-mails mis en quarantaine",
                                                                                         "E-mails supprim\u00E9s",
                                                                                       "E-mails r\u00E9cup\u00E9r\u00E9s" },
                                                                        new String[][] { { "quarantine_received" }, 
                                                                                        { "quarantine_dropped" },
                                                                                        { "quarantine_released" } }));
        return new JREmptyDataSource();
   }

    /**
     * The method that initializes all the PortalRequestObject
     */
    private void initReport() {
        // timeScale has been provided as parameter when requesting the report
        // firstDate and lastDate are part of the DateLimitedReportFiller
        // Special characters must be Unicode encoded with \u, also be sure to check the <code>/opt/ESIS/share/ESIS/reports/style.jrtx</code>
        // template for encoding and fonts.

        //getNumberOfAssetsWithMalwaresEvolution
        PortalRequestObject pro = getDefaultPortalRequestObject("getNumberOfAssetsWithMalwaresEvolution");
        pro.setCallParameters(new String[] { "chart", "occurences", "others", "all_locations", timeScale, "all" });
        pro.setSerieTitle("Syst\u00E8mes avec alertes");
        pro.setYTitle("Nombre de syst\u00E8mes");
        pro.setExpectedObjectType(ObjectType.CHART);
        pro.setTimeScale(timeScale);
        portalCalls.put("getNumberOfAssetsWithMalwaresEvolution", pro);

       // getTopAssetsWithMalwaresBetweenDays (Virus)
        pro = getDefaultPortalRequestObject("getTopAssetsWithMalwaresBetweenDays");
        pro.setCallParameters(new String[] { "top", "occurences", "virus", "all_locations", DateHelper.HTMLDateOrNull(firstDate), DateHelper.HTMLDateOrNull(lastDate), "10" });
        pro.setExpectedObjectType(ObjectType.TOP);
        portalCalls.put("getTopAssetsWithMalwaresBetweenDaysVirus", pro);

       // getSmtpDomainsBetweenDays
        pro = getDefaultPortalRequestObject("getSmtpDomainsBetweenDays");
        pro.setTemplateParameters(new String[] { "top", DateHelper.HTMLDateOrNull(firstDate), DateHelper.HTMLDateOrNull(lastDate), "$1", "$2", "10" });
        pro.setExpectedObjectType(ObjectType.TOP);
        portalCalls.put("getSmtpDomainsBetweenDays", pro);

       // getEmailEvolution
        pro = getDefaultPortalRequestObject("getEmailEvolution");
        pro.setTemplateParameters(new String[] { "chart", timeScale, "$1", "all" });
        pro.setExpectedObjectType(ObjectType.CHART);
        pro.setTimeScale(timeScale);
        portalCalls.put("getEmailEvolution", pro);
    }

At this stage, the report is filled, all we need is to export it in any format. This is done by the servlet.
Subreports cannot be filled directly from the Java side, they have to be filled from the main report.
To use subreports, we have to pass them as parameters of the main report. Their datasource can be passed as a parameter of the main report, or taken in a field of the datasource of the main report.

Reports XML file sample

<?xml  version="1.0" encoding="ISO-8859-1" standalone="yes" ?>
<esis>
    <!-- Portal: Graphs -->
    <report name="Chart-390-185"        file_name="portal/chart-390-185.jasper"/>
    <report name="Chart-420-200"        file_name="portal/chart-390-185.jasper"/>
    <report name="Top-390"              file_name="portal/top-390.jasper"/>
    <report name="Top-420"              file_name="portal/top-420.jasper"/>
    <report name="Text-390"             file_name="portal/text-390.jasper"/>
    <!-- -->
    <report name="Model-Landscape"      file_name="portal/model_landscape.jasper"/>
    <!-- -->
    <report name="Model-Title"          file_name="portal/model_landscape_title.jasper"/>

    <!--            TESTS                       -->
    <report name="TestModel1Top"
        description="Test Model 1 Top"
        class_name="net.sourceforge.esisframework.export.jasper.test.Model1Top"
        module_class_name="net.sourceforge.esisframework.module.Admin"
        file_name="test/model_1_top.jasper" />

    <report name="TestModel2Chart"
        description="Test Model 2 Chart"
        class_name="net.sourceforge.esisframework.export.jasper.test.Model2Chart"
        module_class_name="net.sourceforge.esisframework.module.Admin"
        file_name="test/model_2_chart.jasper" />    
    <!--                                        -->
    <!--            ADMINISTRATION              -->
    <!--
                                            -->
    <!-- Administration: Groups report -->
    <report name="AdminGroups" 
        description="Generate a report for all groups and departments"
        class_name="net.sourceforge.esisframework.export.jasper.admin.AdminGroupsFiller"
        module_class_name="net.sourceforge.esisframework.module.Admin"
        file_name="admin/admin_groups.jasper"/>
    <report name="AdminSubReportGroupsMembers" 
        file_name="admin/admin_subreport_groups_members.jasper"/>
    <report name="AdminSubReportGroupsDomains" 
        file_name="admin/admin_subreport_groups_domains.jasper"/>

<!-- Administration: Locations report -->
    <report name="AdminLocations" 
        module_class_name="net.sourceforge.esisframework.module.Admin"
        description="Generate a report for all locations"
        class_name="net.sourceforge.esisframework.export.jasper.admin.AdminLocationsFiller"
        file_name="admin/admin_locations.jasper"/>
    <report name="AdminSubReportLocationsMembers" 
        file_name="admin/admin_subreport_locations_members.jasper"/>
    <report name="AdminSubReportLocationsGroups" 
        file_name="admin/admin_subreport_locations_groups.jasper"/>
    <report name="AdminSubReportLocationsNetworks" 
        file_name="admin/admin_subreport_locations_networks.jasper"/>
    <report name="AdminSubReportLocationsDomains" 
        file_name="admin/admin_subreport_locations_domains.jasper"/>
    </report>
    </esis>


  • name : the unique report name. This is the internal report name that is sometimes used to get a report from.
  • module_class_name. When the report is related to a module and not to a specific object (like a vulnerability), we must provide the module class name here. This way, the report will be listed in the report screen of the module.
  • description : A small explanation for what can be found in the report. This is a mandatory attribute of all module reports, but it is not other reports.
  • class_name : the class that has been designed to fill the report with data. Subreports do not have a class_name associated, they are filled from their main report.
  • file_name : the jasper file associated to the report.

Reports tag can have parameters tag associated: these tags will be used to limit the period of computation for the report. Parameters can only be used for module reports.

A module report will be displayed in the report list in the GUI as many times as there are different parameters tags associated in the XML file. With the preceding sample, 3 lines will be displayed on the screen :
Vulnerabilities management report for the last 60 days
Vulnerabilities management report for the last 15 days
* Vulnerabilities management report for the last 12 months

Different values possible :

[current | previous | | [+-]\d],[day | month | quarter | year]

  • « current,month » : report with boundaries date between the 1st and the last day of the current month
  • « -60,day » : report with boundaries date between 60 days ago and the current date
  • « -6,month » : report with boundaries date between 6 months ago and the current date
  • « +60,day » : report with boundaries date between the current date and in 60 days.
  • « previous,quarter » : report with boundaries date for the last quarter.

The date in question depends of the report : it can be a target date, a reception date, ...

Integration in the build environment

Reports files are first XML files (.jrxml). Then a compilation is needed to transform them to java serialized objects (.jasper).

This can be done with the following ant task :

ant reports.compile-dev

Resulting Jasper files are located in .dist/jasper. To integrate recently modified reports in the database, import the reports.xml file.

ant reports.integrate

or

scripts/cmd jasperreports reports/reports.xml .dist/jasper

Sample Reports

scripts/cmd documents export net.sourceforge.esisframework.export.jasper.test.Model1Top paramValue=current,year peopleId=1 timescale=month
scripts/cmd documents export net.sourceforge.esisframework.export.jasper.test.Model2Chart paramValue=current,year peopleId=1 timescale=month

Custom specific reports

ant compile-customer integrate-customer-reports -Dcustomer-name=foo

This will integrate the reports for the customer named 'foo'.


Related

Wiki: Build
Wiki: Home

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.