JasperReports is a Java reporting tool that writes content to the printer, onto the screen, or into files such as PDF, XLS, RTF, CSV, XML, HTML, ODT and TXT. With Spring Boot, we can create APIs that let users download application data as reports.
The following example serves PDF reports using a Spring Boot REST API.
1. Goals
The demo application covers the following points.
- An API accepting the request to generate the report.
- Accepting multiple report formats such as PDF or XML.
- Creating a report from the Jasper template with data fetched from the backend database.
- Serving the report in an appropriate format that can be downloaded in the browser.
- Improving the performance by saving and loading the compiled Jasper report as the serialized object.
The image shows the generated report with the id and name of each item.

2. Setup
Before integrating JasperReports, we create a simple Spring boot application with database connectivity and some records in the database.
We use the H2 database to keep things simple. We also create a simple Item entity class with only two attributes, id and name.
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Item(String name) {
this.name = name;
}
}
To access the database, we create the ItemRepository which is a standard ListCrudRepository to perform database operations.
@Repository
public interface ItemRepository extends ListCrudRepository<Item, Long> {
//...
}
We initialize the application with 5 items using the CommandLineRunner interface, which executes the save() methods when the application starts.
@SpringBootApplication
public class App implements CommandLineRunner {
@Autowired
ItemRepository itemRepository;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Override
public void run(String... args) throws Exception {
itemRepository.save(new Item("Item 1"));
itemRepository.save(new Item("Item 2"));
itemRepository.save(new Item("Item 3"));
itemRepository.save(new Item("Item 4"));
itemRepository.save(new Item("Item 5"));
}
}
Let us add JasperReports to the demo application.
3. Maven
Add the latest version of Jasper reports library. We exclude commons-logging so the Spring’s configured logging is used.
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.20.5</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
4. Create Jasper Report Template
Creating the Jasper report template is out of the scope of this article. To create a template, download and install JasperReports Library, which provides the tools for creating reports and is available on the official JasperReports website.
The demo template file contains the report title and the item id and name fields of the report data.
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
https://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name="item-report" pageWidth="612" pageHeight="792" columnWidth="555" leftMargin="20" rightMargin="20"
topMargin="20" bottomMargin="20">
<parameter name="title" class="java.lang.String" isForPrompting="false"/>
<field name="id" class="java.lang.Long"/>
<field name="name" class="java.lang.String"/>
<title>
<band height="30" splitType="Stretch">
<textField>
<reportElement x="200" y="0" width="200" height="30"/>
<textElement/>
<textFieldExpression class="java.lang.String"><![CDATA[$P{title}]]></textFieldExpression>
</textField>
</band>
</title>
<detail>
<band height="20" splitType="Stretch">
<textField isStretchWithOverflow="true" isBlankWhenNull="true">
<reportElement x="100" y="0" width="100" height="20" />
<box>
<pen lineWidth="1.0" lineColor="#000"/>
</box>
<textElement/>
<textFieldExpression class="java.lang.Long"><![CDATA[$F{id}]]></textFieldExpression>
</textField>
<textField isStretchWithOverflow="true" isBlankWhenNull="true">
<reportElement x="200" y="0" width="200" height="20"/>
<box>
<pen lineWidth="1.0" lineColor="#000"/>
</box>
<textElement/>
<textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
</textField>
</band>
</detail>
</jasperReport>
5. Creating Jasper Report from Template
The browser downloads the report, so we create it in byte[] form and write it to the ServletOutputStream.
5.1. Loading Jasper Template
To load the template at runtime, we store the template file in the /src/main/resources folder. Spring ResourceUtils loads the file at runtime, and JasperCompileManager compiles it to the JasperReport instance.
An instance of JasperReport represents the compiled report template objects ready to fill with data and report generation.
public byte[] getItemReport(List<Item> items, String format) {
File file = ResourceUtils.getFile("classpath:item-report.jrxml");
JasperReport jasperReport = JasperCompileManager.compileReport(file.getAbsolutePath());
//...
}
To improve performance, we can avoid compiling the template every time by serializing the compiled report into the file system using the JRSaver, and loading it with the JRLoader.
public byte[] getItemReport(List<Item> items, String format) {
JasperReport jasperReport;
try {
jasperReport = (JasperReport) JRLoader.loadObject(ResourceUtils.getFile("item-report.jasper"));
} catch (FileNotFoundException | JRException e) {
try {
File file = ResourceUtils.getFile("classpath:item-report.jrxml");
jasperReport = JasperCompileManager.compileReport(file.getAbsolutePath());
JRSaver.saveObject(jasperReport, "item-report.jasper");
} catch (FileNotFoundException | JRException ex) {
throw new RuntimeException(e);
}
}
//...
}
5.2. Filling Data into Jasper Report
After compiling the template to JasperReport instance, we call the method JasperFillManager.fillReport() to populate the report with data.
For example, we pass the list of items using the JRBeanCollectionDataSource. We can also use other data sources such as CSV, JSON or JPA/Hibernate.
public byte[] getItemReport(List<Item> items, String format) {
File file = ResourceUtils.getFile("classpath:item-report.jrxml");
JasperReport jasperReport = JasperCompileManager.compileReport(file.getAbsolutePath());
//Set report data
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(items);
Map<String, Object> parameters = new HashMap<>();
parameters.put("title", "Item Report");
//Fill report
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
//...
}
The result is a JasperPrint instance that can be viewed, printed or exported to other file formats.
Report creation is a long running process, so we can make it an asynchronous operation. The API returns a JMS topic location, and the user periodically checks whether the report has been generated.
5.3. Exporting Filled Report to Various Formats
After obtaining the JasperPrint instance, we can use its export…() methods to export the filled report to targets such as byte[], OutputStream, File or serialized objects.
In the demo, we convert the JasperPrint to byte[] and use Spring’s ResponseEntity type to control the API response.
public byte[] getItemReport(List<Item> items, String format) {
//...
//Fill report
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
byte[] reportContent = JasperExportManager.exportReportToPdf(jasperPrint);
return reportContent;
}
To support multiple report formats, we can write a switch expression that picks the logic for the requested file type.
public byte[] getItemReport(List<Item> items, String format) {
//...
JasperPrint jasperPrint = null;
byte[] reportContent;
try {
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource);
switch (format) {
case "pdf" -> reportContent = JasperExportManager.exportReportToPdf(jasperPrint);
case "xml" -> reportContent = JasperExportManager.exportReportToXml(jasperPrint).getBytes();
default -> throw new RuntimeException("Unknown report format");
}
} catch (JRException e) {
//handle exception
}
return reportContent;
}
6. Returning Generated Reports from API Controller
To let the user download the generated report in the browser, we write the report content to the ServletOutputStream. We set the media type to MediaType.APPLICATION_OCTET_STREAM and use a response body with ByteArrayResource containing the generated byte[].
@Controller
public class ReportController {
@Autowired
ItemRepository itemRepository;
@Autowired
JasperReportService jasperReportService;
@GetMapping("item-report/{format}")
public ResponseEntity<Resource> getItemReport(@PathVariable String format) throws JRException, IOException {
byte[] reportContent = jasperReportService.getItemReport(itemRepository.findAll(), format);
ByteArrayResource resource = new ByteArrayResource(reportContent);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(resource.contentLength())
.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.attachment()
.filename("item-report." + format)
.build().toString())
.body(resource);
}
}
7. Demo
We call the API in the browser. When the report is ready, the browser prompts us to download it.
HTTP GET http://localhost:8080/item-report/pdf

When we open the downloaded report, it shows the item data in the layout of the report template.

To download the same report as XML, we call the HTTP GET http://localhost:8080/item-report/xml and the browser downloads the report as an XML file.
8. Conclusion
We created Jasper reports in a Spring Boot application and served them through a REST API. The steps were to add the JasperReports library, load and compile the report template, fill it with data, and export the filled report to PDF or XML.
Happy Learning !!