Skip to content

Jasper reports

Rowdy edited this page Jul 17, 2019 · 24 revisions

Here you will learn how to create a Jasper report, include it into TG based application, produce a PDF file, and how to present it for printing or email it as an attachment.

Development of Jasper reports

Table of contents

  1. Tools
  2. Templates
    1. Report Styles
    2. Images
    3. Representing of data
    4. Development of sub-reports
    5. Including sub-report into the main report
  3. How to include a Jasper report into TG based application
  4. How to produce a PDF file and present it for printing
  5. How to email PDF file as attachment
  6. Running iReport on macOS

Tools

To develop a Jasper report for TG-based projects Jaspersoft iReport Designer 4.7.1 should be used.

This is a separate tool and installation could be downloaded from https://ireport.software.informer.com/4.7/.

Also more detail information about Jasper Reports could be obtain here: http://jasperreports.sourceforge.net/JasperReports-Ultimate-Guide-3.pdf

Templates

There are two types of reports:

  • simple report (all data is gathered from one or a few entities and is simply presented)
  • more complex report; such a report will contain one or more sub-reports (in this case data usually is presented as lines, with header and grid on the background)

Often some sort of logo is presented in the report.

For example for the TG PSA project Fielden's logo is stored at the following location:

tgpsa\tgpsa\tgpsa-web-server\src\main\resources\report_templates\images\logo.png

Two templates were created for the TG PSA project: SimpleReportWithLogo.jrxml and ReportWithSubreport.jrxml to simplify the process of developing reports. These templates are located at tgpsa\tgpsa\tgpsa-web-server\src\main\resources\report_templates.

Report Styles

Three report styles [1] are introduced: reportStyle, reportBoldStyle and reportStyleArialBlack, which are used in both report and sub-report.

Style reportStyleArialBlack is used for Report Title only.

Style reportBoldStyle is used for labeling of data. The word Description in a report or column headers: Item No, Description, Amount in the sub-report are using this style.

Style reportStyle is used for data fields.

Images

To include logo into the report the report parameter imagePath [2] is introduced to provide location for all images. This parameter is used in the Image Expression of the image object to specify what picture would be shown.

Representing of data

To represent data in the report new Fields [3] should be added. The same is applicable to the sub-reports. The field should be of type String as all formatting would be done in TG. Names are case sensitive! Better to use same rules as in TG when naming variables here and make sure when you provide real data in TG you are using the correct names.

Page Footer [4] contains some useful examples which are sometimes necessary when designing a report: today's date/time and page numbering.

Development of sub-reports

A sub-report usually contains two sections: Column Header and Details.

It should contain the same Styles [1] for consistency with the main report. Please specify meaningful names for Report name [2], and Page size [3] should be the size of the area used by the sub-report in the report, this is especially important when you have a few sub-reports! The Job Pack for TTGAMS is good example where five sub-reports are presented one after the other.

The sub-report has to be compiled and as result SimpleSubreport.jasper will be created. This Jasper file has to be committed and released to the client. The TG-based application will be looking for this Jasper file for sub-reports as only the main report could be compiled at run time.

To generate the Jasper file simply press the Preview option in the designer.

Including sub-report into the main report

Two additional parameters are introduced: subreport and subreportData [1].

Object Subreport [2] is added into the Details section, also some rectangles to represent a grid on the background.

Some properties of a sub-report object are very important.

For example Width and Height [2] should correspond to the Page size of the sub-report. sub-report Expression and Data Source Expression [2] should point to our additional parameters subreport and subreportData.

How to include a Jasper report into TG based application

Let's use the Tax Invoice report from the TG PSA project as an example.

InvoiceReport.jrxml was designed (it contains sub-report InvoiceLinesSubreport.jrxml) by using the templates described above as a base during development.

The next step is to create a Java class that will collect all the necessary data, provide it to the Jasper report, and compile it into a PDF file.

InvoiceReport.java was created in tgpsa-pojo-bl\src\main\java\fielden\project\reports\.

This class has a public method reportAsPdf(final InvoiceHeader invoiceHeader, final IInvoiceLine coInvoiceLine), which does all necessary steps.

    public byte[] reportAsPdf(final InvoiceHeader invoiceHeader, final IInvoiceLine coInvoiceLine) throws JRException {
        // retrieve all necessary data using provided invoiceHeader
        final List<Map<String, ?>> data = new ArrayList<>();
        data.add(createInvoiceHeader(invoiceHeader));

        // creating data source for lines sub report
        final List<EntityAggregates> lines = aggregateLines(invoiceHeader, coInvoiceLine);
        final JRRewindableDataSource jrsLines = new EntityAggregatesReportSource(lines.toArray(new EntityAggregates[] {}), subreportProps);

        final JasperReport report = JasperCompileManager.compileReport(reportTemplatePath);
        addPdfFontsToStyles(report.getStyles());

        // filling report template with data and parameters
        final JRMapCollectionDataSource dataSource = new JRMapCollectionDataSource(data);

        final Map<String, Object> params = new HashMap<>();
        params.put("imagePath", reportImagePath);
        params.put("subReport", subreportTemplatePath);
        params.put("subReportData", jrsLines);

        final JasperPrint print = JasperFillManager.fillReport(report, params, dataSource);

        // exporting to PDF file as byte array
        final JRPdfExporter exporter = new JRPdfExporter();
        exporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8");

        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
        exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
        // will automatically also show "Print" dialog upon opening
        exporter.setParameter(JRPdfExporterParameter.PDF_JAVASCRIPT, "this.print();");
        exporter.exportReport();

        return outputStream.toByteArray();
    }

Method createInvoiceHeader(invoiceHeader) retrieves all necessary data for the main report using the provided invoiceHeader.

Method aggregateLines(invoiceHeader, coInvoiceLine) retrieves all the necessary data for the sub-report, produces List<EntityAggregates> that is transformed into the JRRewindableDataSource.

After that the main report is compiled, three report styles are included, data is provided for additional parameters: imagePath, subReport and subReportData.

JasperFillManager and JRPdfExporter take care of the rest of the functionality: filling the report and producing output as a PDF file.

How to produce a PDF file and present it for printing

Usually an action has to be created to trigger the report creation and printing process.

In this particular case existing action InvoiceApprovalAction was used and extended with printing functionality (as invoice approval triggers Tax Invoice print out by design).

Three additional properties mime, fileName and data were added to the InvoiceApprovalAction entity.

public class InvoiceApprovalAction extends AbstractFunctionalEntityWithCentreContext<String>

    // the following 3 properties pertain to the creation of the PDF representation of the invoice
    @IsProperty
    @Readonly
    @Title(value = "MIME", desc = "File MIME Type.")
    private String mime; // application/pdf, application/vnd.ms-excel, text/plain, text/html

    @IsProperty
    @Title(value = "File Name", desc = "The name of file that Invoice report will produce. For eg. InvoiceNo_002161.pdf.")
    private String fileName;

    @IsProperty
    @Title(value = "Data", desc = "Raw binary data that was produced by Jasper report and needs to be persisted.")
    private byte[] data;

A value for property mime is provided in the new_() method:

    @Override
    public InvoiceApprovalAction new_() {
        return super.new_().setMime("PDF");
    }

A value for property fileName is provided in the producer:

    final String fileName = "InvoiceNo_" + invoiceHeader.getKey() + ".pdf";

    entity.beginInitialising();
    entity.setFileName(fileName);
    entity.endInitialising();

Property data will contain the outcome from Jasper report generation, which is done in the save() method:

    // let's generate report as PDF file with fileName
    try {
        final byte[] reportOutputStream = new InvoiceReport().reportAsPdf(invoiceHeader, co(InvoiceLine.class));
        entity.setData(reportOutputStream);
    } catch (final JRException e) {
        throw Result.asRuntime(e);
    }

Now to present this generated PDF file to the user for review and printing, the post action for this InvoiceApprovalAction should be modified and new FileSaverPostAction() used.

    final EntityActionConfig approvalAction = action(InvoiceApprovalAction.class)
            .withContext(context().withSelectionCrit().withSelectedEntities().build())
            .postActionSuccess(new FileSaverPostAction())
            .icon("icons:check-circle")
            .withStyle(CUSTOM_ACTION_COLOUR)
            .shortDesc("Approve selected Invoice")
            .longDesc("Approve selected Invoice")
            .build();

Jasper-related dependencies have to be included into pom files.

All fonts used during report development should be included in tgpsa-dao\pom.xml for testing purposes and tgpsa\tgpsa-web-server\pom.xml for production release.

    <dependency>
        <groupId>fielden</groupId>
        <artifactId>jasper-fonts</artifactId>
        <classifier>ariblk</classifier>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>fielden</groupId>
        <artifactId>jasper-fonts</artifactId>
        <classifier>calibri</classifier>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>fielden</groupId>
        <artifactId>jasper-fonts</artifactId>
        <classifier>times</classifier>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>fielden</groupId>
        <artifactId>jasper-fonts</artifactId>
        <classifier>wingding</classifier>
        <version>1.0</version>
    </dependency>

tgpsa\tgpsa-web-server\pom.xml should also contain instructions on coping of Jasper reports.

    <execution>
        <id>copy-jasper-reports</id>
        <phase>package</phase>
        <goals>
            <goal>copy-resources</goal>
        </goals>
        <configuration>
            <encoding>UTF-8</encoding>
            <outputDirectory>${staging.dir}/deployment/report_templates</outputDirectory>
            <resources>
                <resource>
                    <directory>${project.build.directory}/report_templates</directory>
                    <includes>
                        <include>**/*</include>
                    </includes>
                </resource>
            </resources>
        </configuration>
    </execution>

Sometimes SVG pictures have to be used and rendered (see ElectricalSafetyCertificate.jrxml from the TTGAMS project as an example). In this case Batik bridge is required to render SVG in JasperReports and should be introduced as a dependency in tgpsa-pojo-bl\pom.xml.

    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>batik-bridge</artifactId>
        <version>1.7</version>
    </dependency>

How to email PDF file as attachment

To send generated PDF file as attachment please use one of the methods provided in the IEmailSender interface.

 final File fileToAttach = new File(entity.getFileName());

                // Write the binary data into the file
                try (final FileOutputStream fos = new FileOutputStream(fileToAttach)) {
                    fos.write(entity.getData());

                    emailSender.sendPlainMessageWithAttachments(fromPersonEmail, toPersonEmail, subject, wholeMessage, fileToAttach.toPath()).flatMap(ex -> emailSendingErrorHandler.map(handler -> {
                        try {
                            handler.accept(invoiceHeader, ex);
                            return null;
                        } catch (final Exception e) {
                            return e;
                        }
                    }));
                } catch (final IOException e) {
                    throw Result.asRuntime(e);
                } finally {
                    fileToAttach.delete();
                }

Running iReport on macOS

Jaspersoft iReport Designer 4.7.1 is rather old, but does run on macOS Mojave, however it seems that it needs an older version of Java (e.g. 7) and must be run manually.

Installation is the usual "drag to Applications directory" simple process.

Attempting to start it will undergo upgrade due to GateKeeper. Although you can right-click the .app file and manually open it, it will still undergo upgrade to start.

Attempting to start it from the command line results in:

Rowdys-iMac:tgpsa rowdy$ open /Applications/Jaspersoft\ iReport\ Designer.app
LSOpenURLsWithRole() failed with error -10810 for the file /Applications/Jaspersoft iReport Designer.app.

Attempting to run the actual executable results in:

Rowdys-iMac:MacOS rowdy$ /Applications/Jaspersoft\ iReport\ Designer.app/Contents/MacOS/ireport 
Cannot find java. Please use the --jdkhome switch.

This is despite Java being correctly installed:

Rowdys-iMac:MacOS rowdy$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home

Rowdys-iMac:MacOS rowdy$ which java
/usr/bin/java

Rowdys-iMac:MacOS rowdy$ java -version
Picked up JAVA_TOOL_OPTIONS: -Djava.awt.headless=true
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

Specifying the -jdkhome option, as suggested above, results in the following:

Rowdys-iMac:MacOS rowdy$ /Applications/Jaspersoft\ iReport\ Designer.app/Contents/MacOS/ireport --jdkhome /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home
Picked up JAVA_TOOL_OPTIONS: -Djava.awt.headless=true
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0

Ah, yes, we have JAVA_TOOL_OPTIONS set to headless due to issues attempting to run almost every other Java application.

Trying again without that variable set:

Rowdys-iMac:MacOS rowdy$ JAVA_TOOL_OPTIONS= /Applications/Jaspersoft\ iReport\ Designer.app/Contents/MacOS/ireport --jdkhome /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home
Picked up JAVA_TOOL_OPTIONS: 
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0

The application does attempt to start, displays a splash screen, but again undergoes upgrade and silently exits.

Trying again with an older version of Java:

Rowdys-iMac:MacOS rowdy$ JAVA_TOOL_OPTIONS= /Applications/Jaspersoft\ iReport\ Designer.app/Contents/MacOS/ireport --jdkhome /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home
Picked up JAVA_TOOL_OPTIONS: 

And it actually seems to start and run.

Quitting the application raises the following exception:

2019-07-18 09:39:45.646 java[89711:13605019] org.netbeans.ExitSecurityException: Illegal attempt to exit early
        at org.netbeans.TopSecurityManager.checkExitImpl(TopSecurityManager.java:192)
        at org.netbeans.TopSecurityManager$PrivilegedCheck.run(TopSecurityManager.java:648)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.netbeans.TopSecurityManager$PrivilegedCheck.check(TopSecurityManager.java:673)
        at org.netbeans.TopSecurityManager$PrivilegedCheck.checkExit(TopSecurityManager.java:661)
        at org.netbeans.TopSecurityManager.checkExit(TopSecurityManager.java:153)
        at java.lang.Runtime.exit(Runtime.java:107)
        at java.lang.System.exit(System.java:962)
        at com.apple.eawt._AppEventHandler.performQuit(_AppEventHandler.java:145)
        at com.apple.eawt.QuitResponse.performQuit(QuitResponse.java:51)
        at com.apple.eawt._AppEventHandler$_QuitDispatcher.performDefaultAction(_AppEventHandler.java:390)
        at com.apple.eawt._AppEventHandler$_AppEventDispatcher.dispatch(_AppEventHandler.java:512)
        at com.apple.eawt._AppEventHandler.handleNativeNotification(_AppEventHandler.java:202)

For simplicity, a script was created and placed into a directory on the PATH as follows:

#!/usr/bin/env bash
JAVA_TOOL_OPTIONS= /Applications/Jaspersoft\ iReport\ Designer.app/Contents/MacOS/ireport --jdkhome /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home &

Now it starts, doesn't lock up the terminal while it is running, but still displays the stack trace upon exit. Seems like that's about as good as it will get.

Clone this wiki locally