This example demonstrates how to use JasperReports Library in a Spring MVC application. JasperReports is a reporting engine. It takes input data coming from a data source (an implementation of Jasper's JRDataSource interface), fills the data source values into a user defined report template (an xml file with .jrxml extension) and then uses an exporter (an implementation of Jasper's JRExporter interface) to render resultant document in one of the variety of formats including HTML, PDF, Excel, OpenOffice and Word.
Example
In this example, we will use the following Jasper components:
- JRBeanCollectionDataSourceIt is an implementation of JRDataSource which is an Jasper abstraction of data source. This data source wraps a collection of JavaBean objects.
- HtmlExporterIt is an implementation of JRExporter interface which renders the report document in HTML format.
- Jrxml fileWe will also need to write a jrxml file to define the report template.
Additional Maven Dependency
pom.xml
<dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.4.1</version> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </exclusion> </exclusions> </dependency>
Above JasperReports dependency uses jackson-core 2.1.4. We need to exclude that because first of all we are not using any Jackson related feature in this example and secondly Spring expects Jackson's minimum version to be 2.5.0 for MappingJackson2HttpMessageConverter
to work which is automatically initialized if the Jackson's ObjectMapper is on the classpath.
Java Config class
In this example, we are going to use BeanNameViewResolver
for resolving the Jasper view. We will register an implementation of View
as a bean and will return the bean name as a view name from the Spring controller. Check out this simple example of BeanNameViewResolver
as well.
@EnableWebMvc @Configuration @ComponentScan public class MyWebConfig { @Bean public ViewResolver beanNameViewResolver() { BeanNameViewResolver resolver = new BeanNameViewResolver(); return resolver; } @Bean public ViewResolver jspViewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; } }
Implementing the View
Spring classes for JasperReport support are based on old deprecated Jasper API. We have to extend AbstractView
to use the new JasperReport API directly.
package com.logicbig.example; import net.sf.jasperreports.engine.*; import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; import net.sf.jasperreports.engine.export.HtmlExporter; import net.sf.jasperreports.export.SimpleExporterInput; import net.sf.jasperreports.export.SimpleHtmlExporterOutput; import org.springframework.stereotype.Component; import org.springframework.web.servlet.view.AbstractView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.util.List; import java.util.Map; @Component("forexView") public class CurrencyRateHtmlView extends AbstractView { private JasperReport currencyRatesReport; @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType("text/html"); List<CurrencyRate> rates = (List<CurrencyRate>) model.get("todayCurrencyRates"); //data source JRDataSource dataSource = getDataSource(rates); //compile jrxml template and get report JasperReport report = getReport(); //fill the report with data source objects JasperPrint jasperPrint = JasperFillManager.fillReport(report, null, dataSource); //export to html HtmlExporter exporter = new HtmlExporter(DefaultJasperReportsContext.getInstance()); exporter.setExporterInput(new SimpleExporterInput(jasperPrint)); exporter.setExporterOutput(new SimpleHtmlExporterOutput(response.getWriter())); exporter.exportReport(); } private JRDataSource getDataSource(List<CurrencyRate> rates) { JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(rates); return dataSource; } public JasperReport getReport() throws JRException { if (currencyRatesReport == null) {//compile only once lazily InputStream stream = getClass().getResourceAsStream("/rates.jrxml"); currencyRatesReport = JasperCompileManager.compileReport(stream); } return currencyRatesReport; } }
public class CurrencyRate { private String currencyPair; private Date date; private BigDecimal askPrice; private BigDecimal bidPrice; ............. }
src/main/resources/rates.jrxml
<?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 http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="rates-template" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="3737c8f8-35e7-4ea0-bf9f-c557f6bbbdf3"> <field name="currencyPair" class="java.lang.String"/> <field name="date" class="java.util.Date"/> <field name="askPrice" class="java.math.BigDecimal"/> <field name="bidPrice" class="java.math.BigDecimal"/> <columnHeader> <band height = "50"> <staticText> <reportElement x = "0" y = "0" width = "120" height = "50" /> <textElement verticalAlignment = "Middle"> <font size="16" isBold = "true" /> </textElement> <text><![CDATA[Currency Pair]]></text> </staticText> <staticText> <reportElement x = "120" y = "0" width = "120" height = "50" /> <textElement verticalAlignment = "Middle"> <font size="16" isBold = "true" /> </textElement> <text><![CDATA[Date]]></text> </staticText> <staticText> <reportElement x = "240" y = "0" width = "120" height = "50" /> <textElement verticalAlignment = "Middle"> <font size="16" isBold = "true" /> </textElement> <text><![CDATA[Ask Price]]></text> </staticText> <staticText> <reportElement x = "360" y = "0" width = "120" height = "50" /> <textElement verticalAlignment = "Middle"> <font size="16" isBold = "true" /> </textElement> <text><![CDATA[Bid Price]]></text> </staticText> </band> </columnHeader> <detail> <band height="25"> <textField> <reportElement x="0" y="0" width="120" height="25"/> <textElement> <font size="14" /> </textElement> <textFieldExpression class="java.lang.String"> <![CDATA[$F{currencyPair}]]></textFieldExpression> </textField> <textField pattern="dd.MM.yyyy"> <reportElement x="120" y="0" width="120" height="25"/> <textElement> <font size="14" /> </textElement> <textFieldExpression class="java.util.Date"> <![CDATA[$F{date}]]></textFieldExpression> </textField> <textField pattern="#,##0.00"> <reportElement x="240" y="0" width="120" height="25"/> <textElement> <font size="14" /> </textElement> <textFieldExpression class="java.math.BigDecimal"> <![CDATA[$F{askPrice}]]></textFieldExpression> </textField> <textField pattern="#,##0.00"> <reportElement x="360" y="0" width="120" height="25"/> <textElement> <font size="14" /> </textElement> <textFieldExpression class="java.math.BigDecimal"> <![CDATA[$F{bidPrice}]]></textFieldExpression> </textField> </band> </detail> </jasperReport>
Writing a Spring Controller
@Controller @RequestMapping("/") public class CurrencyRateController { @GetMapping("/") public String mainView() { return "main"; } @GetMapping("/exchangeRates") public String handleForexRequest(Model model) { model.addAttribute("todayCurrencyRates", getTodayForexRates()); return "forexView"; } private List<CurrencyRate> getTodayForexRates() { //dummy rates List<CurrencyRate> currencyRates = new ArrayList<>(); Date today = new Date(); List<Currency> currencies = new ArrayList<>(Currency.getAvailableCurrencies()); for (int i = 0; i < currencies.size(); i += 2) { String currencyPair = currencies.get(i) + "/" + currencies.get(i + 1); CurrencyRate cr = new CurrencyRate(); cr.setCurrencyPair(currencyPair); cr.setDate(today); BigDecimal bidPrice = new BigDecimal(Math.random() * 5 + 1); bidPrice = bidPrice.setScale(3, RoundingMode.CEILING); cr.setBidPrice(bidPrice); BigDecimal askPrice = new BigDecimal(bidPrice.doubleValue() + Math.random() * 2 + 0.5); askPrice = askPrice.setScale(3, RoundingMode.CEILING); cr.setAskPrice(askPrice); currencyRates.add(cr); } return currencyRates; } }
src/main/webapp/WEB-INF/views/main.jsp
<html> <body> <h2>Spring Jasper Report View Example</h2> <a href="/exchangeRates">Get the today's Currency Rates</a> </body> </html>
To try examples, run embedded tomcat (configured in pom.xml of example project below):
mvn tomcat7:run-war
Output
clicking on the link:
Example Project
Dependencies and Technologies Used:
- spring-webmvc 4.3.11.RELEASE: Spring Web MVC.
- javax.servlet-api 3.1.0 Java Servlet API
- jasperreports 6.4.1: JasperReports Library.
- JDK 1.8
- Maven 3.3.9
|