Wednesday, September 9, 2009

Creating jasper reports in Grails

I like to work with Grails, so to continue making useful stuff i elaborated a little bit about how to make reports in Grails using Japser (which is a nice reporting tool)

My aim is to do reports in PDF and Excel. And since it don't think it is the f...ing business of the report file to know anything about the database I intend to use my own models to send into the report.

Create an application:
 grails create-app bookshelf

Install the plugin:
 grails install-plugin jasper
This usually takes a while since Plugin list cache is almost always out of date when installing plugins. So go take a coffe or something.

First of all I want a domain-class to hold books. So create that one and set up a few books in the BootStrap.groovy to have somthing to test with.
 grails create-domain-class Book


Add a few properties to the book, for example:

and add a few books in the BootStrap.groovy to have something

To create a report from Jasper, you need to create a .jrxml and then compile it to.jasper report file.


To create a report for my purpose, I used iReport. this is a tricky one because the jasper plugin which I got when installing is 0.9.7 and has a mistake in it that makes the reports fail. However to solve that look up the lib folder in the plugin-installment (using grails1.0.4 is in the project, but using 1.1.1 it ends up in your home folder) and remove the jasperreports-2.0.5.jar, otherwise you will end up with some NullPointerExceptions when tryring to run the report. Loocking there you see that the other jar file is jasperreports-3.1.2.jar, so the iReport version to download is 3.1.2.

I choosed the standalone version but there is a version that plugs into Netbeans.

Making the following report and compile it makes an .jasper file to use from grails.



Now things is starting to get a little bit interesting. We now need to make a report of this. I genereated all stuff for book by using the command:
 grails generate-all Book
we can then use the generated views to present the report.

Add the following line in the view to get a PDF and a XLS report:


(of course they should be tagged, but this stupid blog editor does not want to show it if if make them as tags)

for example under pagination, I used an action in the controller and a little bit of of code, not much really, to to what is needed.

That's it. Try it out yourself!

(For  the lazy one, a zip of the project can be found here: http://sites.google.com/site/ironicprogrammer/home/grails-and-jasper-1 as an attachement)

27 comments:

  1. Hi,your post is really grate n it helped me to solve my jasper report matter..

    Thanks! :-)

    ReplyDelete
  2. hi, Peter,

    is there any way to send an Integer parameter from my controller class to the jasper report?

    ReplyDelete
  3. Should work as you can set up parameters in jasper-reports and send them to the report. Should be more or less the same as for Java.

    ReplyDelete
  4. Hi Peter,

    Again me Erandi. In my grails application, there is a method which process a transaction with lots of domain classes. As an example;

    def updateAccount = {

    def account = Account.get(params.id)
    if(account) {
    account.amount = 100
    def log = account.log
    log.changedDate = new Date()
    account.save()
    log.save()

    }

    }

    So, my question is, Is there any way to roll-back the transaction if something wrongly happened? I looked for the withTransaction method in grails as well. But seems that it only supports for the same kinds of objects in a Domain. Since here I 'm saving lots of objects in different domains seems it doesn't work for this.

    ReplyDelete
  5. Actually have not worked that much with transactions in grails. However, have a look at this blog to see if it helps.

    http://sacharya.com/transactions-in-grails/

    Think you should do something like in a block-level transaction

    Account.withTransaction { status ->
    if(!account.validate()){
    status.setRollbackOnly()
    } else {
    account.amount = 100
    def log = account.log
    log.changedDate = new Date()
    account.save()
    log.save()
    }
    }

    ReplyDelete
    Replies
    1. You can make use the save(failOnError:true) code. SO your code should be like below:

      def updateAccount = {

      def account = Account.get(params.id)
      if(account) {
      account.amount = 100
      def log = account.log
      log.changedDate = new Date()
      account.save(failOnError:true)
      log.save(failOnError:true)

      }

      }

      Delete
  6. Hi, is there a way that I can send the pdf file to another controller? like a controller that would print the pdf/excel file?

    ReplyDelete
  7. Using the session and request, I guess you can do whatever you want with the PDF. However, of course you can not print it unless you have a contact/access with the printer the user want it printed to.

    ReplyDelete
  8. Hi, today i came across a new error in my project which still i was unable to sloved. The error is like this.

    Once i run the grails app it starts and within a jiffy again it restarted.(just like a loop) . this happens after i clean my project. At the moment it is not working and it doesn't give any error for such behavior.

    Have u ever come across such situation?? If so, please give me an advice to get rid of this.

    ReplyDelete
  9. Take a look at Rendering Plugin (http://grails.org/plugin/rendering)!

    You use just HTML and CSS to make PDF reports!

    Probably you won't need Jasper/iReports anymore - at least for simple reports!

    ReplyDelete
  10. Hi,

    Ur post was really helped me while coding. Now I am having a problem and my problem is regarding the Block-level Transaction.

    In my Offer domain class i used beforeUpdate event. Since in block level transaction keeps the object in session until the transaction is committed, the beforeUpdate event called as a loop and finally result in stachOverflow exception.

    How to avoid this situation?. Help me.

    Erandi

    ReplyDelete
  11. @erandi: seams that you end up in an endless recursive loop. Try to see so you do not have endless call, that is the beforeUpdate makes calls to your update etc.

    ReplyDelete
  12. Thank you for the post! Very helpful :)

    ReplyDelete
  13. Thank you Peter, it was very helpful ..!.

    ReplyDelete
  14. This got me in the right direction. Thank you!

    ReplyDelete
  15. great post. Its the most informative and easy to understand example on the net. I almost cant believe this is possible.... Thanks again.

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. Thanks Eugene, Anonymous, Jay and Roger

    ReplyDelete
  18. @Peter Great tutorial! It works fine. But then I've tried to pass a list to iReport I got this error:

    java.lang.NoSuchMethodException: Unknown property 'id' on class 'class java.lang.String'
    at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1313)

    at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:762)

    at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:837)

    at org.apache.commons.beanutils.PropertyUtils.getProperty(PropertyUtils.java:426)...

    I get this error when I run report from Grails app. If I run it from iReport it works fine.

    This is my action:

    def createReport = {
    def books = ["Jason Rudolph","Scott W. Ambler"]
    chain(controller:'jasper',action:'index',model:[data:books],params:params)}

    this is SQL query in iReport. The rest is the same. Do you have an idea how to solve this? Thanks.

    ReplyDelete
  19. Hi, Anonymous.
    It must be a list of doman classes. Your are passing a list of Strings, and since string does not have a id property it fails. In my example I pass a list of books and since Book is a domain class, Grails provides an id property behind the scenes.

    ReplyDelete
  20. Thanks a lot,This blog is very helpful.

    ReplyDelete
  21. Dear Peter,
    Your tutorial is so good. But i have some queries.
    i am new in Grails. I want to use Jasper report for my project report.
    Ypur code is here:
    def createReport = {
    def books = Book.list()
    chain(controller:'jasper',action:'index',model:[data:books],params:params)
    }

    where i can find the controller:'jasper'?? Is there any further tutorial?

    ReplyDelete
  22. years to go, blog is still useful. Except the latest jasper plugin version, I know this work with grails 2.3.7 is 1.6.0. There are issues with 1.7.0 and 1.8.0 version of jasper grails plugin

    ReplyDelete
  23. Really great tutorial!
    help me plz, i have four domain-class and I want to generate a pdf for all domain-class?

    ReplyDelete