JasperReports scriptlets
last modified July 13, 2020
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.
<?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.
<?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.
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.
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.
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.
<?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.
<?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.
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.
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.
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.