How to Create PDF Reports Using Atlassian JIRA

The Brief - Enhancing Atlassian JIRA plugins with PDF export functionality using Apache FOP

Clearvision were approached by a client to write a custom Atlassian JIRA reporting plugin which would allow them generate a report from JIRA as a PDF. They also requested that PDF to have specific page formatting; such as page breaks between sub headings; not present in the generated HTML report displayed within JIRA.

It should be said that Atlassian JIRA is a great platform to extend (the best platform I’ve worked with) and the JIRA reports functionality is no exception.

However, the current JIRA reports plugin module has one major drawback. It only allows initial output as an HTML web page embedded within the JIRA interface, with an additional option (if it is enabled for the report) to be downloaded as an Excel spreadsheet. Great for your CFO, but what about the rest of us? We want the swanky HTML report as an even swankier PDF download!

Finding the right technology

With this in mind, the Clearvision development team set about finding a suitable PDF rendering technology that would both fit the clients specification needs and to allow multi-discipline development – such as the use of a front-end developer to mark-up the PDF document just like an HTML web page.

The technology the development team decided upon was Apache’s FOP (Formatting Objects Processor) Java library.

Apache FOP takes XSL-FO (Extensible Stylesheet Language Formatting Objects) files as input and produces among other formats, PDF documents as output.

XSL-FO is an XML style language used to format XML data, and thus, is natural to pick up by those already familiar with XML/HTML. It also allows developers to maintain an MVC (Model–view–controller) design pattern, with the XSL-FO acting as the “view” and the FOP processor acting as the “controller”. And just as with HTML output XSL-FO elements can be included in an Apache Velocity file, allowing you to insert any “model” data in at runtime.

Example XSL-FO
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="10pt">
<fo:page-sequence master-reference="pages">
<fo:static-content flow-name="xsl-region-before">
<fo:block font-size="20pt" background-color="#1d3944" color="#ffffff" width="100%" padding="1cm">
<fo:block text-align="center" text-transform="uppercase">Example XSL-FO Title</fo:block>
</fo:block>
</fo:static-content>
</fo:page-sequence>
</fo:root>

Using Apache FOP within your Atlassian JIRA plugin

In order to use Apache FOP within your plugin during development, add the following dependency to your pom.xml file.

pom.xml dependency
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>0.94</version>
<scope>compile</scope>
</dependency>

Apache FOP can then be used in the following manner. Where “foo.fo” is the XSL-FO input mentioned above. In this case a file name reference, but could just as easily be an Apache Velocity parser output.

Apache FOP example code (taken from the Apache FOP website)
private FopFactory fopFactory = FopFactory.newInstance();
private TransformerFactory tFactory = TransformerFactory.newInstance();

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
try {
response.setContentType("application/pdf");
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF,
response.getOutputStream());
Transformer transformer = tFactory.newTransformer();
Source src = new StreamSource("foo.fo");
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
} catch(Exception ex) {
throw new ServletException(ex);
}
}

Getting it to work once the plugin is installed in Atlassian JIRA!

Assuming you have written a version 2 plugin for a recent version of JIRA and installed it via the UPM (Universal Plugin Manager), chances are it installed and enabled OK and you were able to continue using JIRA as normal.

However you will invariably discover the same issue that the Clearvision development team did. Chances are the moment you take any sort of action, such as clicking on a relevant link, to produce the required PDF, you will be presented with the following error message. Hardly the swanky PDF we were expecting!

JIRA runtime error when using Apache FOP
java.lang.LinkageError: loader constraint violation: when resolving method "javax.xml.transform.sax.SAXResult.<init>(Lorg/xml/sax/ContentHandler;)V"
the class loader (instance of org/apache/felix/framework/ModuleImpl$ModuleClassLoader) of the current class, com/clearvision/jira/reports/CustomerReport,
and the class loader (instance of <bootloader>) for resolved class, javax/xml/transform/sax/SAXResult, have different Class objects for the type orm.sax.SAXResult.
<init>(Lorg/xml/sax/ContentHandler;)V used in the signature

This error essentially means that there has been a conflict between some XML processing classes that already existed as part of JIRA and those that were introduced by our plugin as part of the Apache FOP dependency.

In order to resolve the conflict, we need to instruct our plugin to use the XML processing classes that JIRA already knows about. This can be achieved using the OSGi bundle instruction <Import-Package> which can be added directly to your atlassian-plugin.xml plugin descriptor file by nesting it inside a <bundle-instructions> element, which itself is nested inside the <plugin-info> element. The full <Import-Package> declaration is shown below. Make sure to use *;resolution:=optional at the end of the declaration so as not to produce ClassNotFoundException‘s relating to other class dependencies within the plugin when it is installed into JIRA.

<Import-Package> declaration
<plugin-info>
<bundle-instructions>
<Import-Package>
javax.xml.parsers,javax.xml.transform,javax.xml.transform.sax,org.xml.sax,
javax.xml.transform.dom,javax.xml.namespace,org.xml.sax.ext,org.w3c.dom,*;resolution:=optional
</Import-Package>
</bundle-instructions>
</plugin-info>

Now your plugin should run smoothly and produce the PDF export you always wanted.

Future developments

Currently this approach has only been tested in recent versions of JIRA. I.e. 5.2 +. But I see no reason why this approach wouldn’t work in any other Atlassian product such as Confluence that used the version 2 plugin framework, that is OSGi compatible.

Finally this approach currently only works with Apache FOP version 0.94. Upgrading the Apache FOP dependency to a newer version introduces the following runtime exception:

Runtime exception when upgrading to newer versions of Apache FOP
javax.xml.transform.TransformerException: Can't transform a Source of type javax.xml.transform.stream.StreamSource

Conclusion

With this functionality exporting to PDF should be simple; producing beautiful, accessible reports from JIRA. This is yet another example of how Clearvision were given a problem and through our agile software development process delivered a concise solution that was on time and within budget. For more on how Clearvision can help with the development of custom plugins like this one for Atlassian JIRA please visit our custom plugin development page.

About

Technical Consultant at Clearvision-CM

Rob is an experienced and enthusiastic software developer, having worked on all aspects of software development for large bespoke web based systems, which underpin the businesses that rely on them. Renowned for his ability to work efficiently and to a very high standard, Rob has technical and problem solving analytical skills that are often called upon.

View all posts by Robert Giddings
2 Comments
Ferenc Kiss  
November 13, 2013 at 3:02 pm

Great article, Robert! Let me give you an idea how to save major efforts when developing custom PDF based document exports and maintaining them from one JIRA version to the next.

Our JIRA PDF View Plugin solves the same problem and is essentially based on the same technologies (JIRA + PDF + Velocity + FOP) like what you have outlined here. It also comes with support for custom reporting logic in Groovy scriping, data visualization, and some more advanced features. The nice thing is that it allows you to fully customize the FO templates, the Groovy scripts, all other resources, allowing you to focus on your custom report, not on the “plumbing code”. In this sense, it is a platform for developing reports.

Plugin page: https://marketplace.atlassian.com/plugins/com.midori.jira.plugin.pdfview

Documentation: http://midori.hu/products/jira-pdf-view-plugin/documentation/template-development

Matt Doar  
November 18, 2013 at 8:45 pm

This is useful, thanks for documenting how you worked around those errors. There’s also further information in the Atlassian docs at https://developer.atlassian.com/display/DOCS/Troubleshooting

The Midori plugin is a good general-purpose way of generating PDFs from within JIRA

Personally, FOP drove me crazy when I used it to create PDFs for O’Reilly books. I’m glad that O’Reilly handles the toolchain for their authors now.

Leave a Reply