DynamicJasper for Grails

Plugin for grails available: see DJ Grails plugin

The purpose of this paper is to explain how DynamicJasper and Grails could be integrated. We are currently making so much progress, and plan to continue doing so, but our main interest is to share it, get feedback and go forward.

Why using DynamicJasper for grails reporting? They share a common philosophy: making development easier by using conventions, allowing the most common cases to be developed fast and easily.

This example is a favorite books database, with basic data such as: isbn, title, author, country, and a score based on my personal opinion.

class Book {
        String name
        String ISBN
        Date dateWritten
        String author
        String country
        Integer score

Make a Report in no time!

Once you have configured the project and are working on a relatively simple data model, your first report would be quite straightforward, within the Book controller:

def report = {
        FastReportBuilder drb = new FastReportBuilder();
        DynamicReport dr = drb.addColumn("Name", "name",
            String.class.getName(),30).addColumn("ISBN", "ISBN",
            String.class.getName(), 100).
            setTitle("My Favorite Books").

        JRDataSource ds = new JRBeanCollectionDataSource(Book.list());
        JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new
                   ClassicLayoutManager(), ds);


This way, you have defined a new action report:

no images were found

Which take us right to a JasperView with the data:

no images were found

So far we have demonstrated basic reporting capabilities, in order to show you how quickly the report could be made! We only needed to define a DynamicReport which finally turns into a JasperPrint with our favorite books list.

DynamicReport: is a domain model entity which models a report, including attributes such as title, subtitle, and footer, and many other common ones, such as columns, groups, repeating groups and so on. As we are going to see, pretty soon, each of these elements can be modified.

JasperPrint: Is the Jasper file compiled and filled, can be shown in different formats (PDF, HTML, XLS, etc).

Adding complexity

Honestly, the last list doesn?t really represent my preferences: The Being and the Nothing was really good, but A Clockwork Orange should not be in second place.

This is not related with DynamicJasper, but with the list created. Hence, I am going to define an ordered list:

def list = Book.list()

        def scoreComparator = [
                               compare: {
                a,b -> a.score > b.score ? -1: 1
        }] as Comparator

        JRDataSource ds = new JRBeanCollectionDataSource(list.sort(scoreComparator));

And as ISBN is not crucial, I will replace it with Country and Score:

no images were found

These are my favorites, and the report makes more sense now. However, I would like to know which author prevails on my library.

In order to do so, let?s define the columns individually:

SimpleColumn columnName = ColumnBuilder.getNew().
        setColumnProperty("name", String.class.getName()).

SimpleColumn columnAuthor = ColumnBuilder.getNew().
        setColumnProperty("author", String.class.getName()).

SimpleColumn columnCountry = ColumnBuilder.getNew().
        setColumnProperty("country", String.class.getName()).

SimpleColumn columnScore = ColumnBuilder.getNew().
        setColumnProperty("score", Integer.class.getName()).

SimpleColumn : Is the entity which models a column in the report. Is one of the children of AbstractColumn, among others, such as PropertyColumn or ExpressionColumn (which allows to use averages or percentages).
As group without ordering is not always the best way to present data, we are going to show them ordered by author and score:
        def authorComparator = [
         compare: {
                a,b -> if (a.author.equals(b.author)) {
                                        return a.score > b.score ? -1 : 1
                           } else {
                                        return a.author > b.author ? 1 : -1
        }] as Comparator
FInally, we define the group and a variable to sum how many elements match the criteria set before.
        GroupBuilder gb1 = new GroupBuilder()
        gb1.addFooterVariable(columnAuthor, DJCalculation.COUNT)
      DJGroup g1 = gb1.build();

As you can see, we have a new report grouped by author, and counting the number of books of every author.

no images were found

The concept of group is pretty intuitive and useful. Looking at the report I would say that the Author column, which is the one used to group, should be first. This could be easily solved by changing the order when adding the columns to the report.

This is pretty interesting when working with agile methodologies, where change is always embraced and an xml template could be disturbing compared to this approach.

Adding more complexity and a chart

Let?s do the same, but ordering by Country and including a Pie Chart:

def countryComparator = [compare: {
                                        a,b -> a.country > b.country? 1 : -1
                          ] as Comparator
First I order by Country, to define the group and the chart after:
        DJChartBuilder chartBuilder = new DJChartBuilder()

Now we have made, pretty quickly, a report ordered by Country and including a Pie Chart. As you can see Argentina Authors has the higher percentage in my library, followed by USA authors.

no images were found

PDF Report

Before moving on, I will do some refactoring!

SimpleColumn columnName = ColumnBuilder.getNew().
        setColumnProperty("name", String.class.getName()).setTitle("Name").build()
SimpleColumn columnAuthor = ColumnBuilder.getNew().
        setColumnProperty("author", String.class.getName()).setTitle("Author").build()
SimpleColumn columnCountry = ColumnBuilder.getNew().
        setColumnProperty("country", String.class.getName()).setTitle("Country").build()
SimpleColumn columnScore = ColumnBuilder.getNew().
        setColumnProperty("score", Integer.class.getName()).setTitle("Score").build()           

        GroupBuilder gbCountry = new GroupBuilder()
        gbCountry.with {
                addFooterVariable(columnCountry, DJCalculation.COUNT)
  DJGroup groupCountry = gbCountry.build();

        GroupBuilder gbAuthor = new GroupBuilder()
        gbAuthor.with {
                addFooterVariable(columnAuthor, DJCalculation.COUNT)

  DJGroup groupAuthor = gbAuthor.build();

  DJChartBuilder chartBuilder = new DJChartBuilder()

        chartBuilder.with {

        FastReportBuilder drb = new FastReportBuilder();
        drb.with {
                setTitle("My Favorite Books by Country and Author ")

        DynamicReport dr = drb.build();       

        def list = Book.list()

        def countryComparator = [
                           compare: {
                                        a,b -> a.country > b.country? 1 : -1
                          ] as Comparator

        JRDataSource ds = new JRBeanCollectionDataSource(list.sort(countryComparator));
    JasperPrint jp = DynamicJasperHelper.generateJasperPrint(dr, new ClassicLayoutManager(), ds);
    ReportWriter reportWriter = ReportWriterFactory.getInstance().getReportWriter(jp, 'PDF', [:]);

Finally, we only added some Groovy notation and a last line, where we set to export to PDF, instead of using JasperView.

Send a Message
Fork me on GitHub