JasperReports scriptlets

In this tutorial, we work with JasperReport scriptlets.

JasperReports is an open-source reporting library. It can create reports in various formats including PDF, HTML, XLS, or CSV. JasperReports creates page-oriented, ready-to-print documents in a simple and flexible manner.

Scriptlets are Java classes that provide additional functionality to JasperReports. We can use scriptlets when report expressions cannot execute more complex operations. Scriptlets are executed every time a report event occurs. Values of report variables can be affected through scriptlets.

Calculating a geometric mean

The following application calculates geometric mean of interest rates. The geometric mean is defined as the nth root of the product of n numbers. It is used in growth rates, like population growth or interest rates, where simple arithmetic mean does not give accurate results.

$ tree
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── zetcode
        │           └── main
        │               ├── CommandLineRunner.java
        │               ├── JasperScriptletGeoMean.java
        │               └── MyScriptlet.java
        └── resources
            └── report2.xml

This is the project structure.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JasperScriptletGeoMean</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        
        <dependency>
            <groupId>net.sf.jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>6.4.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.6.1</version>
        </dependency>
        
    </dependencies>
    <name>JasperScriptletGeoMean</name>
>/project>

The Maven pom.xml file contains these dependencies: jasperreports and commons-math3. The jasperreports dependency is the JasperReports library; the commons-math3 library contains the formula for the geometric mean.

report2.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
   "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">

<jasperReport xmlns = "http://jasperreports.sourceforge.net/jasperreports"
              xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation = "http://jasperreports.sourceforge.net/jasperreports
                                    http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
              name="report2" pageWidth="595" pageHeight="842" 
              columnWidth="555" leftMargin="20" rightMargin="20"
              topMargin="20" bottomMargin="20">
    
    <scriptlet name="MyScriptlet" class="com.zetcode.main.MyScriptlet" />
    
    <parameter name="rets" class="java.util.List"/>
    <variable name="gmean" class="java.lang.Double"/>
     
    <summary>
        <band height="30">
            
            <staticText>
                <reportElement x="0" y="0" width="90" height="15" />
            
                <textElement />
            
                <text><![CDATA[Geometric mean: ]]></text>
            </staticText>                        
            
            <textField>
                <reportElement x="100" y="0" width="200" height="15"/>
             
                <textElement />
            
                <textFieldExpression class="java.lang.Double">
                    <![CDATA[$V{gmean}]]>
                </textFieldExpression>

            </textField>    
        </band>
    </summary>    

</jasperReport>

This is the report template file. The template contains the summary band where we have one variable: gmean.

<scriptlet name="MyScriptlet" class="com.zetcode.main.MyScriptlet" /> 

A scriptlet is referenced with the <scriptlet /> tag. It is a class that contains the calculation of the geometric mean.

<parameter name="rets" class="java.util.List"/>
<variable name="gmean" class="java.lang.Double"/>

We have a parameter and a variable. The parameter is a list of interest rate values. The variable is the computed geometric mean.

<textFieldExpression class="java.lang.Double">
    <![CDATA[$V{gmean}]]>
</textFieldExpression>

In the <textFieldExpression> tag we output the geometric mean.

JasperScriptletGeoMean.java
package com.zetcode.main;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;

public class JasperScriptletGeoMean {

    public void start() throws JRException {

        String xmlFile = "src/main/resources/report2.xml";
        JasperReport jreport = JasperCompileManager.compileReport(xmlFile);
        
        ArrayList<Double> rets = new ArrayList<>();

        rets.add(1.2);
        rets.add(1.8);
        rets.add(-1.2);
        rets.add(1.1);

        Map params = new HashMap();
        params.put("rets", rets);
                
        JasperPrint jprint = JasperFillManager.fillReport(jreport,
                params, new JREmptyDataSource());

        JasperExportManager.exportReportToPdfFile(jprint,
                "src/main/resources/report2.pdf");
    }
}

The JasperScriptletGeoMean creates a JasperPrint file from the data source. JasperPrint represents a page-oriented document that can be viewed, printed, or exported to other formats. In our case, it is going to be exported into a PDF file.

String xmlFile = "src/main/resources/table.xml";
JasperReport jreport = JasperCompileManager.compileReport(xmlFile);

We compile the XML template file into a JasperReport. JasperReport is a compiled template ready to be filled with data.

ArrayList<Double> rets = new ArrayList<>();

rets.add(1.2);
rets.add(1.8);
rets.add(-1.2);
rets.add(1.1);

These values are our interest rates.

Map params = new HashMap();
params.put("rets", rets);

The ArrayList is placed into the parameters. The list maps to the <parameter name="rets" class="java.util.List"/> element created in the report.

JasperPrint jprint = JasperFillManager.fillReport(jreport,
        params, new JREmptyDataSource());

A JasperPrint object is created; an object that can be viewed, printed, or exported to other formats.

JasperExportManager.exportReportToPdfFile(jprint,
        "src/main/resources/report2.pdf");    

The JasperExportManager.exportReportToPdfFile() method exports the JasperPrint into a PDF file.

MyScriptlet.java
package com.zetcode.main;

import java.util.List;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

public class MyScriptlet extends JRDefaultScriptlet {
    
    private DescriptiveStatistics stats;

    @Override
    public void afterReportInit() throws JRScriptletException {
        
        List<Double> rets = (List<Double>) getParameterValue("rets");

        stats = new DescriptiveStatistics();

        rets.forEach((ret) -> {
            stats.addValue(ret);
        });
    }

    @Override
    public void afterDetailEval() throws JRScriptletException {
    
        setVariableValue("gmean", stats.getMean());
    }
}

This is our scriptlet. It derives from the JRDefaultScriptlet. JRDefaultScriptlet provides default empty implementations for scriptlet events.

@Override
public void afterReportInit() throws JRScriptletException {
    
    List<Double> rets = (List<Double>) getParameterValue("rets");

    stats = new DescriptiveStatistics();

    rets.forEach((ret) -> {
        stats.addValue(ret);
    });
}

After the report has been initialized, we get the rets parameter containing the interest rate values and insert them into the DescriptiveStatistics.

@Override
public void afterDetailEval() throws JRScriptletException {

    setVariableValue("gmean", stats.getMean());
}

After the afterDetailEval event, the geometric mean is calculated and set to the gmean variable.

CommandLineRunner.java
package com.zetcode.main;

public class CommandLineRunner {

    public static void main(String[] args) throws Exception {

        JasperScriptletGeoMean app = new JasperScriptletGeoMean();
        app.start();
    }
}

The CommandLineRunner sets up the application.

Creating a chart

The following application creates a pie chart. It uses the JFreeChart library.

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           └── main
    │   │               ├── CommandLineRunner.java
    │   │               ├── JasperJFreeChart.java
    │   │               └── MyScriptlet.java
    │   └── resources
    │       └── report2.xml
    └── test
        └── java

This is the project structure.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JasperJFreeChart</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
   <dependencies>
        
        <dependency>
            <groupId>net.sf.jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>6.4.0</version>
        </dependency>
        
    </dependencies>
    <name>JasperJFreeChart</name>
</project>

We only need the jasperreports dependency; JFreeChart is already included in this dependency.

report2.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
   "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">

<jasperReport xmlns = "http://jasperreports.sourceforge.net/jasperreports"
              xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation = "http://jasperreports.sourceforge.net/jasperreports
                                    http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
              name="report2" pageWidth="595" pageHeight="842" 
              columnWidth="555" leftMargin="20" rightMargin="20"
              topMargin="20" bottomMargin="20">
    
    <scriptlet name="MyScriptlet" class="com.zetcode.main.MyScriptlet" />    

    <parameter name="langs" class="java.util.Map"/>
    <variable name="Chart" class="net.sf.jasperreports.renderers.JCommonDrawableRendererImpl" 
              calculation="System"/>
    
    <detail>
        <band height="430">
            <image scaleImage="Clip" hAlign="Center">
                <reportElement x="0" y="0" width="515" height="300" />
                <imageExpression>
                    <![CDATA[ $V{Chart} ]]>
                </imageExpression>
            </image>       
            
        </band>
    </detail>

</jasperReport>

In the report template template file we have the detail band where we have the <image> element, which displays the chart.

<scriptlet name="MyScriptlet" class="com.zetcode.main.MyScriptlet" /> 

The scriptlet is a Java class that creates the chart object.

<parameter name="langs" class="java.util.Map"/>
<variable name="Chart" class="net.sf.jasperreports.renderers.JCommonDrawableRendererImpl" 
            calculation="System"/>

We have a parameter and a variable. The parameter is a map of values to be displayed in the chart. The variable contains the chart object.

JasperScriptletGeoMean.java
package com.zetcode.main;

import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;

public class JasperJFreeChart {

    public void start() throws JRException {

        String xmlFile = "src/main/resources/report2.xml";
        JasperReport jreport = JasperCompileManager.compileReport(xmlFile);
        
        Map params = new HashMap();
        params.put("langs", createData());

        JasperPrint jprint = JasperFillManager.fillReport(jreport,
                params, new JREmptyDataSource());

        JasperExportManager.exportReportToPdfFile(jprint,
                "src/main/resources/report2.pdf");
    }
    
    private Map<String, Double> createData() {
        
        Map<String, Double> langs = new HashMap<>();
        langs.put("Java", 43.2);
        langs.put("C#", 10.0);
        langs.put("C/C++", 17.5);
        langs.put("PHP", 32.5);
        langs.put("Clojure", 1.1);
        
        return langs;
    }
}

The JasperJFreeChart compiles the report template and creates a PDF file.

Map params = new HashMap();
params.put("langs", createData());

The data is passed as a langs parameter.

private Map<String, Double> createData() {
    
    Map<String, Double> langs = new HashMap<>();
    langs.put("Java", 43.2);
    langs.put("C#", 10.0);
    langs.put("C/C++", 17.5);
    langs.put("PHP", 32.5);
    langs.put("Clojure", 1.1);
    
    return langs;
}

The values in this map are going to be displayed in the pie chart.

MyScriptlet.java
package com.zetcode.main;

import java.util.Map;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
import net.sf.jasperreports.renderers.JCommonDrawableRendererImpl;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.util.Rotation;

public class MyScriptlet extends JRDefaultScriptlet {

      @Override
      public void afterReportInit() throws JRScriptletException {
          
        Map<String, Double> langs = (Map<String, Double>) getParameterValue("langs");  
          
        DefaultPieDataset dataset = new DefaultPieDataset();
        
        langs.forEach((k,v) -> {
            dataset.setValue(k, v);
        });
        
        JFreeChart chart
                = ChartFactory.createPieChart3D(
                        "Computer languages",
                        dataset,
                        true,
                        true,
                        false
                );

        PiePlot3D plot = (PiePlot3D) chart.getPlot();
        plot.setStartAngle(290);
        plot.setDirection(Rotation.CLOCKWISE);
        plot.setForegroundAlpha(0.5f);
        plot.setNoDataMessage("No data to display");

        this.setVariableValue("Chart", new JCommonDrawableRendererImpl(chart));
    }
}

This scriptlet retrieves the data from the langs parameter and generates a pie chart. The chart is set to the report template variable.

Map<String, Double> langs = (Map<String, Double>) getParameterValue("langs"); 

After the report has been initialized, we get the langs parameter containing the chart data.

DefaultPieDataset dataset = new DefaultPieDataset();

langs.forEach((k,v) -> {
    dataset.setValue(k, v);
});

The data is set to the DefaultPieDataset.

JFreeChart chart
        = ChartFactory.createPieChart3D(
                "Computer languages",
                dataset,
                true,
                true,
                false
        );

PiePlot3D plot = (PiePlot3D) chart.getPlot();
plot.setStartAngle(290);
plot.setDirection(Rotation.CLOCKWISE);
plot.setForegroundAlpha(0.5f);
plot.setNoDataMessage("No data to display");

A 3D pie chart is generated.

this.setVariableValue("Chart", new JCommonDrawableRendererImpl(chart));

The generated chart is set to the Chart report template variable.

CommandLineRunner.java
package com.zetcode.main;

public class CommandLineRunner {

    public static void main(String[] args) throws Exception {

        JasperJFreeChart app = new JasperJFreeChart();
        app.start();
    }
}

The CommandLineRunner sets up the application.

In this tutorial, we have worked with scriptlets; we have calculated a geometric mean from interest rates and generated a 3D pie chart. You might also be interested in these related tutorials: JFreeChart tutorial, Creating a report with JasperReports API, Creating a report from CSV with JasperReports, and Java tutorial.