Sunday, January 3, 2010

Configure Jasperreports 3.x Spring 3.x Maven 2.x by Roo 1.x

In this post we are going to see how easy and fast we could integrate Jasperreports to the Spring-MVC based projects by Spring-Roo. Spring Roo is a next-generation rapid application development tool for Java developers.
Note: Spring Roo uses Maven2 as a default build tool for generated projects.


In the first step we will create sample project with Spring-Roo and in the second step we will add required configurations manually.
Before going to steps, have a look at my test environment :
jasperreports-3.5.2
springframework-3.0.0.RELEASE
spring-roo-1.0.0.RELEASE
apache-maven-2.2.1
sun-jdk1.6.0_16 (64bit)
linux-ubuntu-8.04 (64bit)

And don't worry about the differences to yours , you just need to have Spring-Roo-1.0.0.RELEASE installed, then you can expect same result as me.

Step one: create sample project with Spring Roo
$mkdir sample
$cd sample
$roo
roo> project --topLevelPackage com.company.sample
roo> persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT
roo> entity --class ~.domain.Customer
roo> field string --fieldName name --class ~.domain.Customer
roo> controller scaffold --class ~.web.CustomerController --entity ~.domain.Customer 
roo> quit
$mvn clean tomcat:run


Now you can go to http://localhost:8080/sample/ and check the CRUD functionality that you have created with 'controller scaffold' add-ons.

Step two: modify files manually:
You may want to edit files in Eclipse IDE or STS :
Note: STS has all required plugins installed.
$cd sample
$mvn eclipse:eclipse

sample/pom.xml
      <dependencies>
...
        <dependency>
            <groupid>org.springframework</groupId>
            <artifactId>org.springframework.context.support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>3.5.3</version>
        </dependency>
      </dependencies>

You may ask why jasperreports version 3.5.3? while last stable version is 3.7.0? the answer is I couldn't find any higher version in mvnrepository.com . If you could find a higher version in any other Maven2 repositories, please let me know.

sample/src/main/webapp/WEB-INF/spring/webmvc-config.xml

<beans ...>
...
    <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
       <property name="basename" value="views"/>
    </bean>
</beans>


sample/src/main/webapp/WEB-INF/classes/views.properties
customerReportList.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
customerReportList.url=/WEB-INF/reports/customerReportList.jasper
customerReportList.reportDataKey=customerReportList

sample/src/main/webapp/WEB-INF/reports/customerReportList.jrxml
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="customerList" pageWidth="612" pageHeight="792" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
  <field name="name" class="java.lang.String"/>
  <columnHeader>
    <band height="31" splitType="Stretch">
      <staticText>
        <reportElement x="0" y="0" width="100" height="20"/>
        <text><![CDATA[NAME]]></text>
      </staticText>
    </band>
  </columnHeader>
  <detail>
    <band height="24" splitType="Stretch">
      <textField>
        <reportElement x="0" y="0" width="100" height="20"/>
        <textElement/>
        <textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
      </textField>
    </band>
  </detail>
</jasperReport>


sample/src/main/webapp/WEB-INF/reports/customerReportList.jasper
You have different options to compile this file, in my case jasperreports-maven-plugin from mojo.codehaus.org
sample/pom.xml
...
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jasperreports-maven-plugin</artifactId>
    <configuration>
      <sourceDirectory>src/main/webapp/WEB-INF/reports</sourceDirectory>
      <outputDirectory>src/main/webapp/WEB-INF/reports</outputDirectory>
    </configuration>
    <executions>
      <execution>
        <goals>
          <goal>compile-reports</goal>
        </goals>
      </execution>
    </executions>
    <dependencies>
      <dependency>
        <groupId>jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>3.5.3</version>
      </dependency>
      <dependency>
        <groupId>org.apache.log4j</groupId>
        <artifactId>com.springsource.org.apache.log4j</artifactId>
        <version>1.2.15</version>
      </dependency>                    
     </dependencies>
  </plugin>
</plugins>
</build>


sample/src/main/java/com/company/sample/web/CustomerController.java
package com.company.sample.web;

import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

import org.springframework.roo.addon.web.mvc.controller.RooWebScaffold;
import com.company.sample.domain.Customer;

import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.stereotype.Controller;

@RooWebScaffold(path = "customer", automaticallyMaintainView = true, formBackingObject = Customer.class)
@RequestMapping("/customer/**")
@Controller
public class CustomerController {

 @RequestMapping(value ="/customer/report/pdf", method = RequestMethod.GET)
 public String fireReport(ModelMap modelMap) {
  JRBeanCollectionDataSource jrDataSource = new JRBeanCollectionDataSource(Customer.findAllCustomers(),false);
  modelMap.put("customerReportList", jrDataSource);
  return "customerReportList";
 }
}

That's all, add some customers and check 'http://localhost:8080/sample/customer/report/pdf' .

What else?
I also tried to get multiple report format + changing report name at runtime:
sample/src/main/webapp/WEB-INF/spring/webmvc-config.xml
<beans ...>
...
  <bean id="jasperReportsMultiFormatView" name="jasperReportsMultiFormatViewBean"
class="com.company.sample.web.report.CustomJasperReportsMultiFormatView">

    <!-- The value _rep_name_ will be replaced with the report name -->
    <property name="contentDispositionMappings">
      <props>
        <prop key="html">attachment; filename=_rep_name_.html</prop>
        <prop key="pdf">attachment; filename=_rep_name_.pdf</prop>
        <prop key="xls">attachment; filename=_rep_name_.xls</prop>
        <prop key="csv">attachment; filename=_rep_name_.csv</prop>
      </props>
    </property>
  </bean>
</beans>

sample/src/main/webapp/WEB-INF/classes/views.properties
customerReportList.(class)=com.company.sample.web.report.CustomJasperReportsMultiFormatView

sample/src/main/java/com/company/sample/web/report/CustomJasperReportsMultiFormatView.java
package com.company.sample.web.report;

import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import javax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JasperPrint;
import org.springframework.web.servlet.view.jasperreports.JasperReportsMultiFormatView;

/**
 * This calss override renderReport() method for replace report name .
 */
public class CustomJasperReportsMultiFormatView extends JasperReportsMultiFormatView {

 protected void renderReport(JasperPrint populatedReport, Map model, HttpServletResponse response) throws Exception {
        super.renderReport(populatedReport,model,response);
        
        // replace content disposition header filename with the report names.
        Properties contentDispositions = this.getContentDispositionMappings();
        
        Enumeration enumContDispKeys = contentDispositions.keys();
        // iterate over all disposition mappings and replace the word _rep_name_ with the reportName
        while(enumContDispKeys.hasMoreElements()){
            Object contDispKey = enumContDispKeys.nextElement();
            // check whether string before cast.
            if(contDispKey instanceof String){
                // get the disposition string
                String dispositionStr = contentDispositions.getProperty((String)contDispKey);
                // set the new value in the properties
                contentDispositions.setProperty((String)contDispKey,dispositionStr.replace("_rep_name_",populatedReport.getName()));
            }
        }
    }
}

I found the above code from this post . But I got no success to change report name.I also moved super.renderReport(..) to the bottom of method but there was no difference. Maybe it is not working because of my jasperreports version, If you find the problem please let me know, thanks.

And finally sample/src/main/java/com/company/sample/web/CustomerController.java
 @RequestMapping(value ="/customer/report/{format}", method = RequestMethod.GET)
 public String fireReport(ModelMap modelMap, @PathVariable("format") String format) {
  JRBeanCollectionDataSource jrDataSource = new JRBeanCollectionDataSource(Customer.findAllCustomers(),false);
  modelMap.put("customerReportList", jrDataSource);
  modelMap.put("format", format);
  return "customerReportList";
 }

Now you could see different formats like this:
http://localhost:8080/sample/customer/report/pdf
http://localhost:8080/sample/customer/report/csv
http://localhost:8080/sample/customer/report/html
http://localhost:8080/sample/customer/report/xls

Note: for xls format we need to add below dependency to pom.xml
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <version>3.2-FINAL</version>
</dependency>


What last?
Spring-Roo is awesome ;-) give it a try and join it's community. I really had a good time with Roo in my recent project. Actually reading Roo's forum and Roo's twittes are my favorite reads now! :-)
I could feel Roo's potential, for example look at this post and what we have done in step two , All of these could be done with Roo add-ons in the near future. If you want to make that happen sooner please vote for this issue : https://jira.springsource.org/browse/ROO-228.

Hopefully this will help you to integrate jasperreports to your Spring-Roo based project. If you have any suggestions to make these configurations better, please let me know.

15 comments:

Anonymous said...

Looks like jasperforge is publishing maven artifacts here:
http://jasperreports.sourceforge.net/maven2/jasperreports/jasperreports/

Thanks for the post!

Anonymous said...

Thank you for the post.

I've already configured Spring MVC to generate Json and xml via the ContentNegotiatingViewResolver. Do I need to implement the new controller methods in your example? or can I somehow leverage my existing roo generated controllers?

Thanks,
jon

Anonymous said...

Hi, good post, can you help me?. I am working like you with spring and jasper, i can get a report in pdf but i want to generate the report in the file system but i dont know how. Can you help me?

I want to generate in har drive and show in the web.

Thanks

Elvis Ratzlaff said...

Hi I use the next configuration

net.sf.jasperreports
jasperreports
3.7.3
compile

Anonymous said...

Apparently, the JasperReports bean configured in spring configuration file won't be picked up by spring. that's why Properties contentDispositions = this.getContentDispositionMappings();

contentDispositions is always null. I'm wondering how to instruct spring to pick up the bean in its context instead of Initializing one with default the constructor.

Sid said...

You can see our last configuration that is working fine : http://code.google.com/p/wha/source/browse/trunk/src/main/java/nl/hajari/wha/web/report/CustomJasperReportsMultiFormatView.java

Cheers,
Sid

pink geek said...

Great tutorial! :) thanks for posting! :)

TIm said...

Excellent post!
perhaps the http://mvnrepository.com/ can be of use to you.
Typing in jasperreports gives you all the dependencies and their pom configuration known in the mvn repositories.

regards,

Tim

Sid said...

If you are interested, @digitalface wrote a SpringRoo Add-on: Jasperoo :-) for automating this tutorial and achiving higher productivity. As you could read in my post, I said we will have this add-on in near future and it took about one year to have it now, thanks to @digitalface to make that happened.

Cheers,
Sid

Anonymous said...

Hi,
Thanks for sharing. How much work is it needed to make this feature work for GWT component of Roo

Thanks,
Arash

Unknown said...

higher version than 3.5.3 on : http://mvnrepository.com/artifact/jasperreports/jasperreports

Unknown said...

Use this
http://repo2.maven.org/maven2/net/sf/jasperreports/jasperreports/

for latest version

Unknown said...

Try Apache wicket

http://wicket.apache.org/

Unknown said...

Hi, Can you share the source code of this tutorial please.

digitalface jasperoo setup not working for me.

please share me your code,.

Sid said...

Below commit shows how jasperoo worked for me.

https://github.com/smoradi/runfun/commit/953e36dcf1acab07628f24917c7de7469b366352