Spring Web MVC – Spring Web Flow Working With JasperReports

Home

Deprecated

This blog has been deprecated, proceed to go to my new Blog.

Source Code Here

An important requirement in a lot of applications is reporting. It can be internal reports (same application jsp/html) or to an external format like xls,pdf or xml etc. A popular reporting engine is JasperReports. In the past few months I have seen a common posting theme in Spring Community Forums (even asking me via email) related about integration between JasperReports with Spring Web MVC and Spring Web Flow (SWF) and especially with Hibernate

A common architectural pattern is Model-View-Controller (MVC). A situation would be as follows: Some user from the View layer (jsp/html) would click some button to request a desired report (i.e pdf,xls); the request should be handled in the Controller layer ( a controller class for Spring Web MVC or Action class for SWF), the controller/Action should call the Model layer (Service/BO) to delegate its work in that layer. The Service/BO would call DAO classes to get some data (rows from the database to fill and generate the report). After that, the Service/BO should send the report back to the Controller layer which should then send the report to the View layer.

Furthermore, a common approach when you work with Spring is to use Hibernate. If you work with Hibernate, we are using POJO classes to represent our entities, or instance Customer, Provider, etc. A simple report would be a list of all our Customers and other perhaps all our Providers, then, it logical working with a list (i.e. ArrayList) for these entities.

In my experience,many applications require some complex reports. For instance, maybe a special report required by your boss. After some analysis, you can conclude that a solution would be the result of do a query in the database working with two to six entities related (or even worst not related!). Here Hibernate would be not so useful, so the classic SQL query syntax is required (JOIN clause in this case) – you can use jdbcTemplate instead of Hibernate here.

Now suppose that for these six tables, each one has twenty fields and for your report you only need four fields of each table, then we have another problem. You are retrieving a lot of fields that you don’t need. I used to work with my own wrap classes, now the question would be, how do I know what fields or variables I must create for my wrapclass?. The solution is this, from your yourreport.jrxml, open it and you must see something like (about structure)

     <field name="idCustomer" class="java.lang.String"/>
     <field name="nameCustomer" class="java.lang.String"/>
     <field name="phoneCustomer" class="java.lang.String"/>

Don’t forget that you can design your own reports using the IReport tool. Then when you write your own sql statements (with/inside IReport of course) to test your reports, in the same time implicitly in a dynamic way, you are creating a "<field…" for each field retrieved from the query. Each field should be a variable in your wrap class.

For this example and for tutorial purposes I will create a simple wrap class and avoid the DB connection. Before we start consider these situations:

  • If you are working with SWF the last page of your flow process(i.e: 5/5 ) should let you finish the process or generate a pdf report, you can choose one of them. If you choose the report, it should open a pdf report (Adobe Reader would be executed) but not disposing the last jsp page of the flow process, to let you finish the flow. The other case is when you arrive to the last jsp page and you don’t want the report yet, then you only finish the flow process.
  • If in the flow process mentioned above you didn’t generate the report and terminated the process, but after of some time now it is need it, you would call some option in the web menu and request the desired report (maybe provide some request parameter) – for this case it is enough to use the Spring Web MVC. SWF wouldn’t be logical here.

Now you should have clear that SWF and Spring Web MVC need call the same Service/BO and retrieve the desired report. Before to start, the frameworks and other tools used (no all are really necessary).

  • Spring 2.5.0
  • Spring Web Flow 1.0.5
  • JasperReports 2.0.3
  • Eclipse 3.4
  • Tomcat 5.5.25
  • TomcatPlugin 3.2.1
  • Spring IDE 2.1.0

This tutorial assumes basic-medium level about Spring and its sub-projects

Step 1: Creating the structure folders

To do this tutorial quickly and easily, create a Tomcat project called springjasperreports in the webapps folder of your Tomcat installation. I will show you a structure of all folders inside the project (would change according your logic). At first glance it can be a little confusing but later it will be easier to understand.



Step 2: Configuring web.xml

     <display-name>springjasperreports</display-name>
        <context-param>
           <param-name>webAppRootKey</param-name>
           <param-value>springjasperreports.root</param-value>
        </context-param>
        <session-config>
           <session-timeout>60</session-timeout>
        </session-config>
        <context-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>
                    /WEB-INF/springjasperreports-servlet.xml
                    /WEB-INF/xml/context/applicationContext-swf.xml
                    /WEB-INF/xml/context/applicationContext-jasperreports.xml
                    /WEB-INF/xml/context/applicationContext-jasperreportsengine.xml
           </param-value>
        </context-param>
                
        <servlet>
           <servlet-name>springjasperreports</servlet-name>
           <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
           <init-param>
               <param-name>detectAllViewResolvers</param-name>
               <param-value>false</param-value>
           </init-param>
        </servlet>
        
        <listener>
           <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        
        <servlet-mapping>
           <servlet-name>springjasperreports</servlet-name>
           <url-pattern>*.htm</url-pattern>
        </servlet-mapping>
        
        <welcome-file-list>	  
           <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
               
        <taglib>
           <taglib-uri>/spring</taglib-uri>
           <taglib-location>/WEB-INF/spring.tld</taglib-location>
        </taglib>

An important part would be contextConfigLocation, there you can see:

  • /WEB-INF/springjasperreports-servlet.xml (for view resolver,url mapping, and some Web MVC beans)
  • /WEB-INF/xml/context/applicationContext-swf.xml (for flow configuration)
  • /WEB-INF/xml/context/applicationContext-jasperreports.xml (for a controller to handle a request )
  • /WEB-INF/xml/context/applicationContext-jasperreportsengine.xml (for our report engine)

Step 3: Engine and Wrap Class

An important part is the strong>EngineSomeEntitiesJasperReport class, because it creates and fills the object JRBeanCollectionDataSource with data (here I am using the WrapSomeEntities, a simple pojo) filled explicitly. Then this class must be called by controller/action by S-MVC and SWF.

public class EngineSomeEntitiesJasperReport {
	
         public JRBeanCollectionDataSource engine(){
	         
                 WrapSomeEntities wrapSomeEntitiesOne = new WrapSomeEntities();
                 wrapSomeEntitiesOne.setIdCustomer("MJE-88");		
                 wrapSomeEntitiesOne.setNameCustomer("Manuel Jordan");
                 wrapSomeEntitiesOne.setPhoneCustomer("222222");	
                 wrapSomeEntitiesOne.setIdProvider("XYZ-123");
                 wrapSomeEntitiesOne.setNameProvider("Company A");
                 wrapSomeEntitiesOne.setPhoneProvider("457898");
                 wrapSomeEntitiesOne.setIsbn("1590599799");
                 wrapSomeEntitiesOne.setTitleBook("Spring Recipes");
                 wrapSomeEntitiesOne.setPriceBook(new BigDecimal("49.99"));

                 WrapSomeEntities wrapSomeEntitiesTwo = new WrapSomeEntities();
                 wrapSomeEntitiesTwo.setIdCustomer("MJE-88");	
                 wrapSomeEntitiesTwo.setNameCustomer("Manuel Jordan");
                 wrapSomeEntitiesTwo.setPhoneCustomer("222222");
                 wrapSomeEntitiesTwo.setIdProvider("XYZ-777");
                 wrapSomeEntitiesTwo.setNameProvider("Company B");
                 wrapSomeEntitiesTwo.setPhoneProvider("697451");
                 wrapSomeEntitiesTwo.setIsbn("020161622X");
                 wrapSomeEntitiesTwo.setTitleBook("The Pragmatic Programmer");
                 wrapSomeEntitiesTwo.setPriceBook(new BigDecimal("45.99"));

                 List<WrapSomeEntities> myList = new ArrayList<WrapSomeEntities>();
                 myList.add(wrapSomeEntitiesOne);
                 myList.add(wrapSomeEntitiesTwo);

                 JRBeanCollectionDataSource jRBeanCollectionDataSource = new JRBeanCollectionDataSource(myList);	
                 return jRBeanCollectionDataSource;
          }
}

Don’t Forget that our pojo WrapSomeEntities must contain all variables created like <field name="……."/> in the *.jrxml file by the query done in IReport.

A more realistic code listing is:

public class EngineLineasArticulosJasperReport {
	
        private JdbcTemplate jdbcTemplate;
        
        public void setJdbcTemplate(DataSource dataSource) {
                this.jdbcTemplate = new JdbcTemplate(dataSource);
        }

        /**
         *  <p>
         * 	Metodo que llena y retorna el JRBeanCollectionDataSource
         *  </p>
         *  <p>
         * 	Method that fill and return the JRBeanCollectionDataSource
         *  </p>
         * 
         * @return JRBeanCollectionDataSource
         */
         public JRBeanCollectionDataSource engine(){

           String query=" SELECT *, a.descripcion as descripcionarticulo  " +
                        " FROM lineacategoria l, articulo a, medida d WHERE " +
                        " l.idLineaCategoria = a.idLineaCategoria AND a.idMedida=d.idMedida order by l.idLineaCategoria ";
                        
           Collection mycollecion = this.jdbcTemplate.query(
                        query ,
                        new RowMapper() {
                              public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
	                              
                                      WrapLineasArticulos a = new WrapLineasArticulos();                                      
                                      a.setIdLineaCategoria(rs.getString("idLineaCategoria"));
                                      a.setDescripcion(rs.getString("descripcion"));
                                      a.setIdArticulo(rs.getString("idArticulo"));
                                      a.setStockactual(rs.getBigDecimal("stockactual"));
                                      a.setPrecioUnitario(rs.getBigDecimal("precioUnitario"));
                                      a.setPrecioUnitarioVenta(rs.getBigDecimal("precioUnitarioVenta"));
                                      a.setTotalValorizado(rs.getBigDecimal("totalValorizado"));
                                      a.setXtraTextUnoArticulo(rs.getString("xtraTextUnoArticulo"));
                                      a.setXtraNumDosArticulo(rs.getBigDecimal("xtraNumDosArticulo"));
                                      a.setIdMedida(rs.getString("idMedida"));
                                      a.setNombre(rs.getString("nombre"));
                                      a.setDescripcionarticulo(rs.getString("descripcionarticulo"));
                                      return a;
                              }
                        });

           JRBeanCollectionDataSource jRBeanCollectionDataSource = new JRBeanCollectionDataSource(mycollecion);
           return jRBeanCollectionDataSource;
         }
}

If you look carefully the sql statement, it works for related tables (Pk/Fk relation). Another case could be for unrelated tables (working with JOIN clause). The rest of the code is obvious.

Below the declararion of our WrapSomeEntities class.

public class WrapSomeEntities {

        private String idCustomer;
        private String nameCustomer;
        private String phoneCustomer;
        private String idProvider;
        private String nameProvider;
        private String phoneProvider;
        private String isbn;
        private String titleBook;
        private BigDecimal priceBook;

        public String getIdCustomer() {
                return idCustomer;
        }
        public void setIdCustomer(String idCustomer) {
                this.idCustomer = idCustomer;
        }
        public String getNameCustomer() {
                return nameCustomer;
        }
        public void setNameCustomer(String nameCustomer) {
                this.nameCustomer = nameCustomer;
        }
        public String getPhoneCustomer() {
                return phoneCustomer;
        }
        public void setPhoneCustomer(String phoneCustomer) {
                this.phoneCustomer = phoneCustomer;
        }
        public String getIdProvider() {
                return idProvider;
        }
        public void setIdProvider(String idProvider) {
                this.idProvider = idProvider;
        }
        public String getNameProvider() {
                return nameProvider;
        }
        public void setNameProvider(String nameProvider) {
                this.nameProvider = nameProvider;
        }
        public String getPhoneProvider() {
                return phoneProvider;
        }
        public void setPhoneProvider(String phoneProvider) {
                this.phoneProvider = phoneProvider;
        }
        public String getIsbn() {
                return isbn;
        }
        public void setIsbn(String isbn) {
                this.isbn = isbn;
        }
        public String getTitleBook() {
                return titleBook;
        }
        public void setTitleBook(String titleBook) {	
                this.titleBook = titleBook;
        }
        public BigDecimal getPriceBook() {
                return priceBook;
        }
        public void setPriceBook(BigDecimal priceBook) {
                this.priceBook = priceBook;
        }
}

Our EngineSomeEntitiesJasperReport must be declared in our Spring context. For this bean, it is declared in applicationContext-jasperreportsengine.xml

     <beans>
         <bean id="idEngineSomeEntitiesJasperReport"
               class="com.springjasperreports.model.jasperreport.engine.EngineSomeEntitiesJasperReport" />
     </beans>

The bean declaration is clear by itself.

Step 04: Spring Web MVC

We have done the Model Layer (for simplicity in this case the EngineSomeEntitiesJasperReport class), now we must handle the controllers, so it’s onto Spring Web MVC.

For that the ReportSimplePdfJasperReportController is created, see the code below.

public class ReportSimplePdfJasperReportController extends AbstractController {
	
        private EngineSomeEntitiesJasperReport engineSomeEntitiesJasperReport;
        
        public void setEngineSomeEntitiesJasperReport(
                        EngineSomeEntitiesJasperReport engineSomeEntitiesJasperReport) {
                this.engineSomeEntitiesJasperReport = engineSomeEntitiesJasperReport;
        }
        
        protected ModelAndView handleRequestInternal(
                        HttpServletRequest request, HttpServletResponse res)throws Exception{
   
                ModelAndView mav = null;

                try{
                        JRBeanCollectionDataSource jRBeanCollectionDataSource =
                                this.engineSomeEntitiesJasperReport.engine();
                                
                        Map<String,Object> parameterMap = new HashMap<String,Object>();
                        parameterMap.put("datasource", jRBeanCollectionDataSource);
                        
                        mav = new ModelAndView("reportsimplepdfjasperreport",parameterMap);
                }
                catch(Exception e){		
                }
                return mav;
        }
}

We are using a setter method to inject our engine class; the handleRequestInternal method is calling the engine method and receives the JRBeanCollectionDataSource object. For this example no parameters are used (i.e request.getParameter(…)). An important part of the code is parameterMap.put("datasource", jRBeanCollectionDataSource);

Our ReportSimplePdfJasperReportController must be declared in our Spring context. For this bean, it is declared in applicationContext-jasperreports.xml

     <beans>
        <bean id="idReportSimplePdfJasperReportController"
              class="com.springjasperreports.controler.mvc.jasperreports.ReportSimplePdfJasperReportController" >
              <property name="engineSomeEntitiesJasperReport" >
                   <ref bean="idEngineSomeEntitiesJasperReport" />
              </property>
        </bean>
     </beans>

 

The bean declaration is clear by itself

Step 5: Spring Web Flow

To handle the actions we use Spring Web Flow. For that the ReportSimplePdfJasperReportAction is created, see the code below.

public class ReportSimplePdfJasperReportAction extends AbstractAction{

        private EngineSomeEntitiesJasperReport engineSomeEntitiesJasperReport;
                
        public void setEngineSomeEntitiesJasperReport(
                        EngineSomeEntitiesJasperReport engineSomeEntitiesJasperReport) {
                this.engineSomeEntitiesJasperReport = engineSomeEntitiesJasperReport;
        }
        
        protected Event doExecute(RequestContext context)throws Exception{
                
                try{
                        JRBeanCollectionDataSource jRBeanCollectionDataSource =
                                this.engineSomeEntitiesJasperReport.engine();
 
                        context.getRequestScope().put("datasource", jRBeanCollectionDataSource);
                }
                catch(Exception e){		
                }
                return success();
        }
}

Again we are using a setter method to inject our engine class; the doExecute method calls the engine method and recieve the JRBeanCollectionDataSource object. For this example, again no parameters are used (i.e context.getFlowScope().get(…)). An important part of the code is context.getRequestScope().put("datasource", jRBeanCollectionDataSource);

Our ReportSimplePdfJasperReportAction must be declared like a bean too; but we are working with SWF; then for this bean, it is declared in springwebflowjasperreports-beans.xml

     <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans.xsd">

          <bean id="reportSimplePdfJasperReportAction"
                class="com.springjasperreports.swf.actions.jasperreports.ReportSimplePdfJasperReportAction" >
                <property name="engineSomeEntitiesJasperReport" >
                      <ref bean="idEngineSomeEntitiesJasperReport" />
                </property>
          </bean>
          
     </beans>

 

Now we must declare the states of our flow. Then for this flow, we will create springwebflowjasperreports-flow.xml, and see the code below.

     <flow xmlns="http://www.springframework.org/schema/webflow"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="

http://www.springframework.org/schema/webflow


http://www.springframework.org/schema/webflow/spring-webflow-1.0.xsd">

             <start-state idref="springwebflowjasperreports-one" />

             <view-state id="springwebflowjasperreports-one"
                         view="springwebflowjasperreports-stepone">
                   <transition on="next" to="springwebflowjasperreports-two" />
             </view-state>
             
             <view-state id="springwebflowjasperreports-two"
                         view="springwebflowjasperreports-steptwo">
                   <transition on="next" to="springwebflowjasperreports-printpdf" />
                   <transition on="end"  to="springwebflowjasperreports-end" />
             </view-state>
             
             <view-state id="springwebflowjasperreports-printpdf"
                         view="reportsimplepdfjasperreport" >
                   <render-actions>
                      <action bean="reportSimplePdfJasperReportAction" />
                   </render-actions>
                   <!-- I am still using the last jsp of the flow -->
                   <transition on="next" to="springwebflowjasperreports-printpdf" />
                   <transition on="end"  to="springwebflowjasperreports-end" />
             </view-state> 
              
             <end-state id="springwebflowjasperreports-end" view="welcome" >
             </end-state>
              
             <import resource="springwebflowjasperreports-beans.xml"/>
     </flow> 

 

The states declaration is clear by itself. For a better idea, using Spring IDE we can see the flow in a graphical way.

The last part with SWF is to create the configuration. For that, we will create applicationContext-swf.xml

     <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:flow="http://www.springframework.org/schema/webflow-config"
            xsi:schemaLocation="

http://www.springframework.org/schema/beans


http://www.springframework.org/schema/beans/spring-beans-2.0.xsd


http://www.springframework.org/schema/webflow-config


http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd">

              <bean id="idSpringWebFlowJasperReportsFlowController"
                    class="org.springframework.webflow.executor.mvc.FlowController">
                    <property name="flowExecutor" ref="flowExecutorSpringWebFlowJasperReports" />
              </bean>
              
              <flow:executor id="flowExecutorSpringWebFlowJasperReports"
                     registry-ref="flowRegistrySpringWebFlowJasperReports"
                     repository-type="singlekey"/>
                             
              <flow:registry id="flowRegistrySpringWebFlowJasperReports">              
                    <flow:location path="/WEB-INF/flows/springwebflowjasperreports-flow.xml" />       
              </flow:registry>

     </beans>

Step 6: Other Beans Configuration

For this example we must configure the view resolver, url mapping, and some Web MVC beans. For that we must use springjasperreports-servlet.xml

     <beans>

         <bean id="viewResolver"
               class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
               <property name="basename" value="views"/>
         </bean>
         
         <bean id="messageSource" 
               class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
               <property name="cacheSeconds" value="5"/>
               <property name="basename" value="/WEB-INF/interproperties/messages"/>
         </bean>
         
         <bean id="urlMapping" 
               class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
               <property name="mappings">
                  <props>
                     <prop key="/welcome.htm">idWelcomeController</prop>
                     <prop key="/prereportsimplejasperreport.htm">idPreReportSimpleJasperReportController</prop>
                     <prop key="/reportsimplepdfjasperreport.htm">idReportSimplePdfJasperReportController</prop>
                     <prop key="/springwebflowjasperreports.htm">idSpringWebFlowJasperReportsFlowController</prop>
                  </props>
               </property>
         </bean>
         
         <bean id="idWelcomeController"
               class="com.springjasperreports.controler.mvc.welcome.WelcomeController" />
               
         <bean id="idPreReportSimpleJasperReportController"
               class="com.springjasperreports.controler.mvc.jasperreports.PreReportSimpleJasperReportController" />

     </beans>

The bean declarations are clear by themselves. You should notice the two last beans idWelcomeController and idPreReportSimpleJasperReportController. They are two simple controllers that extend the super class AbstractController, both used for forward purposes.

Step 7: views.properties

All our views (jsp/pdf) must be configured in views.properties, for this, see below.

     welcome.(class) = org.springframework.web.servlet.view.JstlView
     welcome.url = /WEB-INF/jsp/inicio.jsp

     prereportsimplejasperreport.(class) = org.springframework.web.servlet.view.JstlView
     prereportsimplejasperreport.url = /WEB-INF/jsp/mvc/reportSimpleJasperReport.jsp
     
     springwebflowjasperreports-stepone.(class) = org.springframework.web.servlet.view.JstlView
     springwebflowjasperreports-stepone.url = /WEB-INF/jsp/flow/swfjr-stepone.jsp
     
     springwebflowjasperreports-steptwo.(class) = org.springframework.web.servlet.view.JstlView
     springwebflowjasperreports-steptwo.url = /WEB-INF/jsp/flow/swfjr-steptwo.jsp

     reportsimplepdfjasperreport.(class) = org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
     reportsimplepdfjasperreport.reportDataKey = datasource
     reportsimplepdfjasperreport.url = /WEB-INF/reports/SpringJasperReportsspring.jasper

The most important part is for the Pdf View used by:

  • ReportSimplePdfJasperReportController class
  • springwebflowjasperreports-printpdf view-state

These consists of three parts:

  • The first our spring package to generate the pdf view
  • The second and in some way the trick part related with the jRBeanCollectionDataSource, the content of our report. If you see your Controller/Action classes the keyword datasource is used in both to assign the report:
    • parameterMap.put("datasource", jRBeanCollectionDataSource); in ReportSimplePdfJasperReportController class
    • context.getRequestScope().put("datasource", jRBeanCollectionDataSource); in ReportSimplePdfJasperReportAction class

    However "datasource" is sent to the SpringJasperReportsspring.jasper file.

  • The third part is the location of our SpringJasperReportsspring.jasper

 

Step 8: views files

We will see each resource file used by views.properties
First inicio.jsp code.

     <?xml version="1.0" encoding="ISO-8859-1" ?>
     <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
     <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
     <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
  
     <html>
       <head>
        <title>Welcome</title>
       </head>

       <body>
         <center>
           <p>	
               This Simple tutorial show you how use <i>Spring Web MVC</i> and <i>Spring Web Flow</i>
               working together with <i>JasperReports</i>.
           </p>

           <ul>
             <li>Spring Web MVC - JasperReports
                 <a href="/springjasperreports/prereportsimplejasperreport.htm">start</a></li>
             <li>Spring Web Flow - JasperReports
                 <a href="/springjasperreports/springwebflowjasperreports.htm?_flowId=springwebflowjasperreports-flow">
                 start</a>
             </li>
           </ul>           
         </center>
       </body>
     </html>

It only offers two links to start each type of example process, as illustrated here:

If we do click in the first link, it must show the reportSimpleJasperReport.jsp. Below is the code.

     <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
     <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
     <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>

     <html>
       <head>
         <title>Spring Web MVC - JasperReports</title>
       </head>

       <body>
         <center>
         <br><br>
           <p>
               Click in <i>Generate Pdf</i> button to open a Pdf Report
           </p>
          
           <form action="/springjasperreports/reportsimplepdfjasperreport.htm" method="post" >
               <input type="submit" class="button" name="next" value="Generate Pdf"/>
           </form>

           <p>
               Click in <i>Go To Welcome Page</i> button if you want to start again
           </p>
           
           <form action="/springjasperreports/welcome.htm" method="post" >
               <input type="submit" class="button" name="next" value="Go To Welcome Page"/>
           </form>
           
         </center>
       </body>
     </html>

 

It only consist in two forms, with an url action for each one, the purposes are obvious. Below the execution page

Note: I have configured my Web browser (Firefox/Opera) to open Adobe Reader to see the pdf report.

Now if we do click in the second link in inicio.jsp, it must start the flow process, then the first view-state use swfjr-stepone.jsp, below the code.

     <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
     <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
     <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
     
     <html>
       <head>
         <title>Spring Web Flow - JasperReports - Page One</title>
       </head>
     
       <body>
         <center>
           <p>
               It is the first page of N pages of a flow process, it would be a simple form.
               For simplicity it flow consist only in two pages.
           </p>

           <form action=""  method="post" >
               <input type="hidden" name="_flowExecutionKey" value="${flowExecutionkey}"/>
               <input type="submit" class="button" name="_eventId_next" value="Go To Page Two"/>
           </form>
         
         </center>
       </body>
     </html>

It consists of one submit button, below the jsp execution

 

If we do click in the button Go To Page Two we go to the next view-state and it use swfjr-steptwo.jsp, below the code.

     <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
     <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
     <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>

     <html>
       <head>
         <title>Spring Web Flow - JasperReports - Page Two</title>
       </head>
  
       <body>
         <p align="justify">
             It is the last page of N pages of a flow process.
             For simplicity according to the explanation it gives the options to
         
         <ul>
           <li>Generate a pdf</li>
           <li>Finish the flow process</li>
         </ul>

             In the case that you decide to finish the process without generate the pdf
             and then after some time you want to see the report
             (obvious that the flow process is already terminated),
             then you should use Spring Web MVC for get the report like a simple search
        </p>
        
        <center>
          <form action=""  method="post" >
              <input type="hidden" name="_flowExecutionKey" value="${flowExecutionkey}"/>
              <input type="submit" class="button" name="_eventId_next" value="Generate Pdf"/>
              <br><br>
              <input type="submit" class="button" name="_eventId_end" value="End Process"/>
          </form>
        </center>
       </body>
     </html>

 

The code is clear by itself, below is the jsp execution

Then the result desired is a pdf file, below the pdf generated.

Here part of the SpringJasperReportsspring.jrxml, the "<field…" declarations.

 
     <property name="ireport.scriptlethandling" value="0" />
     <property name="ireport.encoding" value="UTF-8" />

     <import value="java.util.*" />
     <import value="net.sf.jasperreports.engine.*" />
     <import value="net.sf.jasperreports.engine.data.*" />

     <field name="idCustomer" class="java.lang.String"/>
     <field name="nameCustomer" class="java.lang.String"/>
     <field name="phoneCustomer" class="java.lang.String"/>
     <field name="idProvider" class="java.lang.String"/>
     <field name="phoneProvider" class="java.lang.String"/>
     <field name="isbn" class="java.lang.String"/>
     <field name="titleBook" class="java.lang.String"/>
     <field name="priceBook" class="java.math.BigDecimal"/>

 

You would create your own war file and compile your SpringJasperReportsspring.jrxml with Ant tasks. (In a few days I will attach the source code to share)

I hope that this tutorial help you, if you have some doubt or recommendation, please let me know and sorry about my grammar.

In another Article, i will share perhaps the most important tutorial; the cumbersome case about Spring/JasperReports integration but related with Sub-Reports.

Home

About these ads

9 thoughts on “Spring Web MVC – Spring Web Flow Working With JasperReports

    • Hello Christian

      Really Thanks a lot for your kinds of words, I appreciate.

      I am just replying your comments, because I just see the comments that I should approve in my Blog account, wondered why WordPress not sent me before a notification about the new comments in my email. I apologize about that.

      Soon I should post the source code, I already found it, after my hard disk crashed =(

      My Best Regards

      -Manuel

  1. Just want to say what a great blog you got here!
    I’ve been around for quite a lot of time, but finally decided to show my appreciation of your work!

    Thumbs up, and keep it going!

    Cheers
    Christian, iwspo.net

    • Hello Kartik

      >>I get Failed to load PDF exception.
      could you post the error?, I am not sure what you are receiving

      Be sure you are downloading the file and not opening in the same web browser

      Few minutes ago I used other web browser and the file download fine

  2. Hola Manuel, tu ejemplo es fabuloso, pero soy nuevo en esto, quisiera saber si me puede proporcionar el proyecto completo, es que tengo que desarrollar un proyecto para la universidad y en todos los lugares que he buscado lo ponen incompleto, como para personas con mas experiencia, te agradezco de antemano me puedas ayudar.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s