Running JasperReports with subreports in an app server environment
I’ll start with the moral of the story: read documentation carefully as it may contain the answer you seek. These things are covered in the pay-for documentation available from JasperSoft but you have to skip around a bit to get what you need.
With that out of the way I’ll note that if you don’t buy The Definitive Guide to JasperReports (or support for that matter) you don’t seem to have many good options for documentation aside from the forums which are hit or miss at best. And with the new version of the JasperForge website, which is unfortunately worse than the old one, even that is a dicey proposition. Because I now have clarity on how to get subreports working in an application server environment I’m going to share with others that they may avoid the struggle I did ;) In my particular case the application server is Weblogic 9.2 but I suspect the application server itself is irrelevant. The issue is with how JasperReports runs embedded subreports.
Briefly, the default method to run subreports is to use multiple threads. Each subreport inside a report gets its own Runnable via a call to JRThreadSubreportRunnerFactory.createSubreportRunner(). The default subreport runner is set via a property net.sf.jasperreports.subreport.runner.factory in the file default.jasperreports.properties (located in the JasperReports jar file). The default for this is net.sf.jasperreports.engine.fill.JRThreadSubreportRunnerFactory. Unfortunately this doesn’t work so well in an environment that discourages the use of multiple threads like an application server environment.
The solution from the guys at JasperSoft was to refactor the subreport runner facilities so that interchangeable implementations could be used. The facility that should be used to run reports with embedded subreports in an application server environment is JRContinuationSubreportRunner and it’s corresponding factory JRContinuationSubreportRunnerFactory. These require a different version of the JasperReports jar file as well as an additional jar from the Apache Commons project both of which are included with the JasperReports distribution. We now need to use jasperreports-2.0.5-javaflow.jar in place of jasperreports-2.0.5.jar (in my case….your case migh be a different version number). We also need to use commons-javaflow-20060411.jar.
So we’re good right? Problem solved. Well…not so fast. There are some oddities that need to be dealt with first.
- In the javaflow version of the JasperReports jar the default is still to use the threaded version of the subreport runner instead of the continuation version. Huh? Since we’re specifically using that version of the jar shouldn’t the default actually be the continuation version of the subreport runner?
- The javaflow version of the jar still contains the threaded subreport runner which is kind of confusing. I’m not sure why I’d want to use that since I’m already using a special version of the jar anyhow to *avoid* threading issues. I suppose it provides a fall-back but to me it’s confusing.
- The regular jar file contains the continuation version of the subreport runner but you can’t use it since it isn’t instrumented for javaflow use. Again this is confusing and I can’t understand why those classes are there if they can’t even be used. If you try to use it you get the following stack trace:
java.lang.IllegalStateException: stack corruption. Is class net.sf.jasperreports.engine.fill.JRContinuationSubreportRunner instrumented for javaflow?
at org.apache.commons.javaflow.bytecode.StackRecorder.execute(StackRecorder.java:102)
at org.apache.commons.javaflow.Continuation.continueWith(Continuation.java:170)
at org.apache.commons.javaflow.Continuation.startWith(Continuation.java:129)
at org.apache.commons.javaflow.Continuation.startWith(Continuation.java:102)
at net.sf.jasperreports.engine.fill.JRContinuationSubreportRunner.start(JRContinuationSubreportRunner.java:57)
Truncated. see log file for complete stacktraceIf I can’t use it why is it there in the first place?
The last “gotcha” is the need to set up a properties file so that you override the default of using the threaded version of the subreport runner. This is an easy workaround however as it only requires a couple of things:
- Set a system property called net.sf.jasperreports.properties that points to a properties file that contains the required JasperReports properties. It doesn’t need to be a specific one for JasperReports if you already have a properties file for other things. It can point to that or you can create a new one.The other thing to note about this is that you only need provide the properties that you have to override. The reporting framework is set up to use defaults for the ones you don’t provide. If you’re paranoid copy the one that comes with the jar file and change what you need in there and point to it with the property mentioned above.
- Paste the following in that properties file:
net.sf.jasperreports.subreport.runner.factory=net.sf.jasperreports.engine.fill.JRContinuationSubreportRunnerFactory
Now everything should work. Let’s recap the steps:
- Add the jasperreports-2.0.5-javaflow.jar to your classpath (or whichever version of JasperReports you are using).
- Add the commons-javaflow-20060411.jar to your classpath (or whichever version ships with your version of JasperReports).
- Set a system property called net.sf.jasperreports.properties that points to a properties file that contains the required JasperReports properties.
- Paste the following in that properties file:
net.sf.jasperreports.subreport.runner.factory=net.sf.jasperreports.engine.fill.JRContinuationSubreportRunnerFactory
I hope this post is useful to someone. I didn’t struggle all that long with this but I definitely would have appreciated having some good documentation without having to pay for it.
Update: As of JasperReports 3.0.1 Steps 3 & 4 above should no longer be necessary as the JRContinuationSubreportRunnerFactory is now the default.
Comments(8)