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.

  1. 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?
  2. 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.
  3. 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 stacktrace

    If 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:

  1. 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.
  2. 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:

  1. Add the jasperreports-2.0.5-javaflow.jar to your classpath (or whichever version of JasperReports you are using).
  2. Add the commons-javaflow-20060411.jar to your classpath (or whichever version ships with your version of JasperReports).
  3. Set a system property called net.sf.jasperreports.properties that points to a properties file that contains the required JasperReports properties.
  4. 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.

12 thoughts on “Running JasperReports with subreports in an app server environment

  1. Teodor-

    Thanks for the reply. I didn’t file a bug/request because the docs say that #1 was intentional.

    Like

  2. Hi,

    We have modified the default properties file of the Javaflow-based JAR so that the subreport runner using Java continuations is the default, as suggested at the above point #1.
    Things like these can be solved quicker if they are posted as feature requests or even bugs on the JasperReports trackers.

    We have not yet complied with points #2 and #3 which talk about removing some of the subreport runner implementations from the respective JAR distributions, because we think these are less important.

    Thank you,
    Teodor

    Like

  3. Hi !

    This was a good find. I think. The problem I was hitting today is that it couldn’t locate the subreports when they were there. All works fine in the designer however once deployed to the server it pukes. I removed the sub report and the report ran fine. I am going to assume it is this issue and give it a try at work tomorrow. The actual exception was unable to locate sub report “XXX” .

    Like

  4. Chris-

    Not sure this is the answer to your problem. You might want to try setting the path in your main report when you set the subreport properties. For instance: “./sub_report.jasper”. That will ensure it will look in the same directory.

    Also, I believe it depends on which compiler you’re using. If you’re using the JDK then it will not use the same classpath as JDK your application is running on. This is different if you are running on the Eclipse JDT (which I think is the default). In the case of the JDK you might need to set the property net.sf.jasperreports.compiler.classpath and include the folder where you’re storing the compiled reports. In the case of the Eclipse compiler it *will* “inherit” the classpath of the app server JDK. The Jasper guide says “This property is not used by the JDT-based report compiler, which simply uses the parent
    application’s classpath during Java source file compilation.”

    Like

  5. thanks for the follow up. The application is deployed to tomcat. I am testing your suggestion right now

    Like

  6. I did a test and it still doesn’t find it. The original code does have an absolute file reference and it still can’t .

    The error is actually could not load object from location ./MY.jasper

    Like

  7. I got it working just fine. The problem was the naming of the tomcat directory and the code in the controller was encoding the name… its working thanks

    Like

  8. Hi all,

    I actually had the same issues using JRThreadSubreportRunnerFactory resulting in my system freezing and an exception which is java.lang.ThreadDeath thrown by the app server (Sun Java System App Server 9.1)until I now reconfigured the SubreportRunnerFactory in the properties file to JRContinuationSubreportRunnerFactory and equally added the commons-javaflow-20060411.jar that came with the iReport-2.0.1 release. Now the subreports are working but I have peculiar case of a complex report which requires four different subreports in a master report. Now out of those four subreports, one of the subreports contains nested three subreports.

    To make it more analytical and descriptive, I have a master report which contains four subreports namely :

    a) subreport_1.jasper
    b) subreport_2.jasper
    c) subreport_3.jasper
    d) subreport_4.jasper

    Out of these four subreports, subreport_4.jasper contains another three subreports inside it. On running subreport_4.jasper with its nested subreports seperately, everything runs fine. Also on running subreports 1 to 3 with the master report, everything runs fine too. But when I eventually merge all the subreports together within the master reports, my system chokes or hangs and the app server throws java.lang.OutOfMemoryError.

    Can anyone help, I’ve been on this issue for close to a week running…

    Like

  9. Just a note for anybody who finds this article searching for JasperReports hanging or something of that nature. I had a problem with my appserver hanging after every 8th invocation of a report. This constant was obviously a clue and no coincidence with the fact that I only had 8 connections in my DB connection pool.

    Call me stupid, but it wasn’t obvious to me from the JR sample code etc that I needed to explicitly close the connection I gave to the JasperFillManager. Once I did that I was on my way again. So just check and make sure that any connections you’re pulling from a DataSource, you’re also closing. JR is not closing the connection for you (in hindsight, this is somewhat obvious cos it shouldn’t be if I want to continue using that connection for something else).

    Like

  10. I have used the suggestion mentioned in this website and jasper is not creating multithreads per report (specifically for subreports). I have the following question:
    Using the approach mentioned in this website, the jasper report will only create single thread per report instead of creating multiple threads. Is my assumption correct?

    Like

  11. The best resource would be the JasperSoft support forums. The javaflow version of the jasper jar is the one that should be used in a multithreaded environment like an application server. Whether the library will only create a single thread…I can’t answer that. It would seem from your comment that it is in fact doing this. I haven’t dug into it that as the javaflow version of the library solved the problems we were having.

    Like

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Google photo

You are commenting using your Google 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 )

Connecting to %s

%d bloggers like this: