June 02, 2016

Getting JasperReports 5.x to Operate Smoothly with Java 8

Upgrading from Java 7 to 8 can involve a lot of moving parts, and there are a number of versions of common libraries that may not work well with Java 8, but most of them can be made to work with Java 8 with a little effort. This post is about getting JasperReports 5.x to operate smoothly with Java 8.

If you search for this topic on the Internet, you may find a few related solutions, but I would like to share details on how we solved this issue. Before going into more details of the workaround or proposed solution, let’s see what the out-of-the-box run-time behavior of JasperReports 5.x is with Java 8 during report compilation.

    Caused by: net.sf.jasperreports.engine.JRException: Errors were encountered when compiling report expressions
    class file:
    1. The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files
                         value = String.format(str("label.event_owner"), ((java.lang.String)parameter_userName.getValue())) + ", " + //$JR_EXPR_ID=9$
    <---------------------------------------------------------------------------------------->
    1 errors
          at net.sf.jasperreports.engine.design.JRAbstractCompiler.compileReport(JRAbstractCompiler.java:204)
          at net.sf.jasperreports.engine.JasperCompileManager.compile(JasperCompileManager.java:240)
          at net.sf.jasperreports.engine.JasperCompileManager.compile(JasperCompileManager.java:226)
          at net.sf.jasperreports.engine.JasperCompileManager.compileReport(JasperCompileManager.java:481)

If we observe the exception stack trace, it clearly states that it fails to compile the Jasper report template. Now, let’s talk about how this compilation process works. JasperReports API offers a façade class called, net.sf.jasperreports.engine.JasperCompileManager which has set of static methods for compiling for Jasper report templates. Documentation for this class says,

This class exposes all the library's report compilation functionality.
It has various methods that allow the users to compile JRXML report
templates found in files on disk or that come from input streams. It
also lets people compile in-memory report templates by directly passing
a JasperDesign object and receiving the corresponding JasperReport
object. Other utility methods include report template verification and
JRXML report template generation for in-memory constructed JasperDesign
class instances. These instances are especially useful in GUI tools
that simplify report design work.

The facade class relies on the report template language to determine an
appropriate report compiler. The report compilation facade first reads
a configuration property called net.sf.jasperreports.compiler. to
determine whether a compiler implementation has been configured for the
specific report language. If such a property is found, its value is
used as compiler implementation class name and the facade instantiates
a compiler object and delegates the report compilation to it. By
default, JasperReports includes configuration properties that map the
Groovy, JavaScript and BeanShell report compilers to the groovy,
javascript and bsh report languages, respectively.

If the report uses Java as language and no specific compiler has been
set for this language, the report compilation facade employs a built-in
fall back mechanism that picks the best Java-based report compiler
available in the environment in which the report compilation process
takes place.

And if you look at the “default.jasperreports.properties” or any other configuration properties of JasperReports 5.x, we don’t find any specific configurations for Java compiler, therefore it uses available Java compilers in the environment. Now if we look at the dependency graph for JasperReports

     +--- net.sf.jasperreports:jasperreports:5.0.1
     | +--- .
     | +--- jfree:jcommon:1.0.15
     | +--- jfree:jfreechart:1.0.12 (*)
     | +--- xml-apis:xml-apis:1.3.02
     | +--- eclipse:jdtcore:3.1.0
     | +--- com.fasterxml.jackson.core:jackson-databind:2.0.5
     | | +--- com.fasterxml.jackson.core:jackson-annotations:2.0.5
     | | \--- com.fasterxml.jackson.core:jackson-core:2.0.5
     | \--- com.fasterxml.jackson.core:jackson-annotations:2.0.5

You can find “eclipse:jdtcore:3.1.0” -Eclipse Incremental Java Compiler. After validating our observations against the issue reported as part of #3498-0 and proposed workarounds, we tried upgrading the version of jdtcore specified by Jasper, by over-riding the dependency in our Gradle build, and it worked.

Here is a Gradle script snippet with the fix we tried that works like a charm!

compile 'org.eclipse.jdt:core:3.1.+'
compile('net.sf.jasperreports:jasperreports:5.+'){
exclude group: 'eclipse', name: 'jdtcore'
}

Technically, this means “exclude the version of jdtcore provided by Jasper, and replace it with the most current version of the 3.1.x series”

Or you can even use “org.eclipse.jdt.core.compiler:ecj:4.4” and follow the same approach for Maven too. Probably the difference between ecj and jdtcore is a topic for another post. Once we are done with complete migration to Java 8, we are planning to upgrade the version of JasperReports to 6.x. I look forward to subsequent posts to share a few more Java 8 migration experiences.

References:

http://community.jaspersoft.com/jasperreports-library/issues/3498-0

http://community.jaspersoft.com/questions/844403/how-run-jasperreports-java-8

Mohan Kornipati

Software Architect