Pdf Generation Using Jasper In A Grails App

The jasper reporting engine provides a free, open source tool for report generation in java. It can produce reports in a variety of formats, including pdf, which makes it very handy in creating all types of data filled documents. Suppose you had a report you just finished constructing and wanted to integrate with your latest Groovy/Grails application. There is a grails plugin that provides tools for displaying your report within a page and also nice looking links to download your report in various formats. Depending on the size of your report, this may be the best option. If your report is small, you may also choose to stream the contents of your report back to the browser window. This is particularly nice if you’d like the pdf to display in the browser’s window as if it were a direct link to the file, using the browser’s pdf plugin. If your goal is the latter, here’s how, utilizing the jasper reporting engine, and some of the conveniences of groovy and grails.

1. Get your report ready to be integrated into your application.

A few things to note here – the most important of which is getting your .jasper files onto the classpath of your application after it is built. For this, place your files under ‘%APPLICATION_DIR%/src/java’, where APPLICATION_DIR refers to the root of your grails project. I would recommend creating a ‘resources’ folder in that location, and possibly sub-directors based on your application’s needs. I had a fun time with this particular detail – that’s for another post…

2. Get your data into your report

These small reports are sometimes easiest generated using the XML datasource feature of jasper. This provides the ability to easily test your code and layout offline, as well as flexibility, if your domain model is significantly different than what you’d like to include in your report.

I’ll skip the XML generation. The code below assumes your XML has been provided as a String.

		import org.apache.commons.io.IOUtils 
		import net.sf.jasperreports.engine.JasperFillManager 
		import net.sf.jasperreports.engine.JasperPrint 
		import net.sf.jasperreports.engine.data.JRXmlDataSource 
		import net.sf.jasperreports.engine.JRException 
		import net.sf.jasperreports.engine.JasperExportManager
		
		private byte[] getJasperReport(String xml, String reportName) throws JRException {
			//add any properties you need passed to your report here.
			HashMap hm = new HashMap();
			
			//convert your xml and .jasper file to input streams.  Commons IO provides a nice way to do this.
			InputStream xmlStream = IOUtils.toInputStream(xml)
			InputStream reportStream = this.getClass().getClassLoader().getResourceAsStream(reportName)
			
			byte[] byteArray = null
			JRXmlDataSource jrxmlds = new JRXmlDataSource(xmlStream);
			JasperPrint jasperPrint = JasperFillManager.fillReport(reportStream, hm, jrxmlds);
			byteArray = JasperExportManager.exportReportToPdf(jasperPrint); 
			
			//this is your report, available as a byte array.  
			return byteArray		
		}

3. Get those bytes back to the browser.

Here, we’ll stream the bytes back to the browser. Since this report is ‘small’, we’ll configure the cache settings accordingly. This code belongs in a controller, as we’ll be dealing directly with the servlet response. pre-check & post-check sets how often (in seconds) the cached object should be checked for freshness before and after it is shown to the user. must-revalidate specifies that the cached object requires revalidation before access. Given the settings below, basically, we’re never going to get this object from the cache. You may choose to configure your caching differently.

	def generatePdf = {
		try {
			//generate or retrieve your XML
			String xml = thisMethodShouldReturnXml()
		
			byte[] bytes = getJasperReport(xml, 'thePath/andNameOfYourReport.jasper')
		
			//get the output stream from the common property 'response'
			ServletOutputStream servletOutputStream = response.outputStream

			//set the byte content on the response. This determines what is shown in your browser window.		
			response.setContentType('application/pdf')
			response.setContentLength(bytes.length)
		
			response.setHeader('Content-disposition', 'inline; filename=quote.pdf')
			response.setHeader('Expires', '0');
			response.setHeader('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
			servletOutputStream << bytes
		}
		catch(Exception e) {
			//deal with your exception here
			e.printStackTrace()
			//redirect, etc
		}
	}

The servlet response is available as a common property to any controller – a convenience feature of grails. Note groovy’s handling of pushing the report bytes onto the output stream. Also note the lack of calls to ‘flush’ and ‘close’ on the stream. Both conveniences added by groovy.

Depending on your browser, and whether or not that browser has pdf-handling features (via a plugin
or otherwise), you may see different results. A browser with pdf-handling features will render the pdf directly in the browser. If no pdf-handling capability is present, the user is prompted to download or open the rendered pdf, and is provided with the filename specified in the response header. If the user is prompted to download, the page from which the request for the pdf was issued will remain in the browser window.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s