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.
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.
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.
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.
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 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.
<?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>
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]
The date in question depends of the report : it can be a target date, a reception date, ...
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
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
ant compile-customer integrate-customer-reports -Dcustomer-name=foo
This will integrate the reports for the customer named 'foo'.