Based on my research, I've found the following usage methods. The methods begin with the most direct (naive) approach involving less up front complexity / configuration and evolve to become more abstract but with more dependencies on Spring / more complex Spring configuration.
Method 1: Use the JasperReports API directly in the Controller
------------------------------------------------------------------
Just write out the content to the servlet output stream.
@RequestMapping(value = "helloReport1", method = RequestMethod.GET)
@ResponseBody
public void getRpt1(HttpServletResponse response) throws JRException, IOException {
InputStream jasperStream = this.getClass().getResourceAsStream("/jasperreports/HelloWorld1.jasper");
Map<String,Object> params = new HashMap<>();
JasperReport jasperReport = (JasperReport) JRLoader.loadObject(jasperStream);
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());
response.setContentType("application/x-pdf");
response.setHeader("Content-disposition", "inline; filename=helloWorldReport.pdf");
final OutputStream outStream = response.getOutputStream();
JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
}
----------
Method 2: Inject JasperReportPdf View into Controller
-----------------------------------------------------
Given the JasperReportsPdfView bean:
@Bean @Qualifier("helloWorldReport2")
public JasperReportsPdfView getHelloWorldReport() {
JasperReportsPdfView v = new JasperReportsPdfView();
v.setUrl("classpath:jasperreports/HelloWorld2.jasper");
v.setReportDataKey("datasource");
return v;
}
This view can be injected or wired into the Controller for use:
@Autowired @Qualifier("helloWorldReport2")
private JasperReportsPdfView helloReport;
@RequestMapping(value = "helloReport2", method = RequestMethod.GET)
public ModelAndView getRpt2(ModelAndView modelAndView) {
Map<String, Object> parameterMap = new HashMap<>();
parameterMap.put("datasource", new JREmptyDataSource());
modelAndView = new ModelAndView(helloReport, parameterMap);
return modelAndView;
}
Note that using the `JasperReportsPdfView` (or the more versatile `JasperReportsMultiFormatView`) requires a dependency on spring-context-support:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.3</version>
</dependency>
----------
Method 3: Use XML or ResourceBundle view resolver to map logical view names to JasperReport views
-----------------------------------------------------
Configure a new view resolver, in this case the `ResourceBundleViewResolver` to run before the `InternalResourceViewResolver`. This is based on the order values being set (0 happens before 1):
@Bean
public ResourceBundleViewResolver getResourceBundleViewResolver() {
ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
resolver.setBasename("jasperreport-views");
resolver.setOrder(0);
return resolver;
}
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setOrder(1);
return resolver;
}
Then, at the root of our classpath, the `jasperreport-views.properties` file can contain the logical view name paired with the class and property values (i.e. url and reportDataKey) pertinent to rending a JasperReport:
helloReport3.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
helloReport3.url=classpath:/jasperreports/HelloWorld3.jasper
helloReport3.reportDataKey=myDataSourceKey
The controller code looks like this:
@RequestMapping(value = "helloReport3", method = RequestMethod.GET)
public ModelAndView getRpt3(ModelMap modelMap, ModelAndView modelAndView) {
modelMap.put("myDataSourceKey", new JREmptyDataSource());
return new ModelAndView("helloReport3", modelMap);
}
I like this approach. Controllers stay "dumb" and only deal with String values and the mapping of names to views can happen all in one location.
----------
Method 4: Use JasperReportsViewResolver
-----------------------------------------------------
Configure a zero-ordered `JasperReportViewResolver` and the trick is use `setViewNames` to tell Spring which logical view names you want this resolver to deal with (otherwise you end up with "Could not load JasperReports report from class path resource [jasperreports/index.jasper]" type errors):
@Bean
public JasperReportsViewResolver getJasperReportsViewResolver() {
JasperReportsViewResolver resolver = new JasperReportsViewResolver();
resolver.setPrefix("classpath:/jasperreports/");
resolver.setSuffix(".jasper");
resolver.setReportDataKey("datasource");
resolver.setViewNames("rpt_*");
resolver.setViewClass(JasperReportsMultiFormatView.class);
resolver.setOrder(0);
return resolver;
}
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setOrder(1);
return resolver;
}
And inside the controller:
@RequestMapping(value = "helloReport4", method = RequestMethod.GET)
public ModelAndView getRpt4(ModelMap modelMap, ModelAndView modelAndView) {
modelMap.put("datasource", getWidgets());
modelMap.put("format", "pdf");
modelAndView = new ModelAndView("rpt_HelloWorld", modelMap);
return modelAndView;
}
This is my preferred approach. Controllers resolve jasper reports in a very similar fashion to how jsp views are resolved using the `InternalResourceViewResolver` and there is therefore no need for an explicit mapping file as with the xml or properties file approach in method #3 above.
**EDIT**
The [javadocs for `JasperReportsPdfView`][1] mention it uses the deprecated `JRExporter` API. Is there a better (newer) JasperReports view to use? Perhaps opting for the [`JasperReportsMultiFormatView`][2] is a better option as it does not appear to use `JRExporter`.
[1]:
[To see links please register here]
[2]:
[To see links please register here]