Sunday, February 10, 2013

Export to PDF using spring 3.2 & itextpdf-5.3.4


We used to extends AbstractPdfView an override the method protected void buildPdfDocument(Map model, Document document, PdfWriter writer, HttpServletRequest   request, HttpServletResponse response), But not anymore if we are using latest itextpdf Library. So in this class there is nothing spring specific, the spring in title is misnomer.

Controller class:

GeneratePdf.java :

package com.spring.package.controller;

import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Controller;
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.web.bind.annotation.ResponseBody;

import com.itextpdf.text.Document;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

@Controller
public class GeneratePdf{
      
       @RequestMapping(value = "/report.htm")
       public void pdfReport(ModelMap model, HttpServletResponse response, HttpServletRequest     request, OutputStream outputStream) throws Exception
       {
              response.setContentType("application/pdf");
              response.setHeader("Content-Disposition", "attachment; filename=file.pdf");
             
              Document document = new Document();
              PdfWriter.getInstance(document, outputStream);
              document.open();

              document.addTitle("Bank Statement");
              document.addSubject("Bank Statement");
              document.addKeywords("Report, PDF, iText");
              document.addAuthor("Satyam");
              document.addCreator("Satyam");

              document.add(new Paragraph(" "));
              document.add(new Paragraph(" "));
              document.add(new Paragraph(" "));

              Map<String,String> data = new HashMap<String,String>();
              data.put("1/20/2010", "$100,000");
              data.put("1/21/2010", "$200,000");
              data.put("1/22/2010", "$300,000");
              data.put("1/23/2010", "$400,000");
              data.put("1/24/2010", "$500,000");
             
              PdfPTable  table = new PdfPTable(2);
              table.addCell("Month");
              table.addCell("Amount");
             
              for (Map.Entry<String, String> entry : data.entrySet()) {

                     table.addCell(entry.getKey());
                     table.addCell(entry.getValue());
                    
        }
             
              document.add(table);
              document.close();
             
       }
}

In this method we can modularize the creation of pdf in some other utility class and will call it in our controller.

Call the above controller from your jsp by three ways:

1.  Form Submit
<form:form method="POST" commandName="user" action="report.htm">
       <table>
              <tr><td colspan="2"><input type="submit" value="show pdf"></div></td></tr>
       </table>
</form:form>

2. Ajax Call
<input type="button" value="Generate Pdf" onclick="doAjaxPost()">

Script Entry :

function doAjaxPost() { 
         // get the form values 
         var name = $('#name').val();
         var data;
         var url = "/SpringExportPdf/report.htm";
         $.ajax({

                type: "POST", url: url,
                data: "name=" + name, success: function(response, status, xhr){
                  var ct = xhr.getResponseHeader("content-type") || "";
                  if (ct.indexOf('xml') > -1) {
                         alert("xml");
                    // handle xml here
                  }
                  if (ct.indexOf('pdf') > -1) {
                     window.open(url);
                     //alert("pdf");
                  }   
                },
                   error: function(error, status){
                     window.alert("Problem retrieving PDF.\nThe error status is: " + status);
                 }
              });
       } 

For those who are trying to generate or retrieve the pdf file from ajax call must know that in the end you will have to hit the GET request only because ajax response only gives you text.

3.  Simple Get Call

<a href="/SpringExportPdf/report.htm">Show Pdf</a>


And we are done.


Saturday, February 9, 2013

Eliminate XML configuration using Java Based Configuration in Spring 3.2


We will required to set the home page as minimal configuration in web.xml otherwise we can remove the WEB-INF itself.

So web.xml entry:

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

Other configurations which were in web.xml earlier are put in configuration java file:
DeploymentDescripterConfig.java:

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class DeploymentDescripterConfig implements WebApplicationInitializer {
        
       @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
              // Create the 'root' Spring application context
           AnnotationConfigWebApplicationContext rootContext  = new AnnotationConfigWebApplicationContext();
           rootContext.scan("com.spring.package.*");
           servletContext.addListener(new ContextLoaderListener(rootContext));
          
          
           ServletRegistration.Dynamic appServlet = servletContext.addServlet("dispatcher", new DispatcherServlet(new GenericWebApplicationContext()));
           appServlet.setLoadOnStartup(1);

           appServlet.addMapping("*.htm");
          

    }

}


We also removed application-servlet.xml configuration, So its alternate java configuration file :
WebConfig.java:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;


@EnableWebMvc
@Configuration
@ComponentScan("com.spring.package.*")
public class WebConfig extends WebMvcConfigurerAdapter{
          
          
    /**
     * ViewResolver configuration required to work with resource-based views.
     */
    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
       InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
       internalResourceViewResolver.setPrefix("/WEB-INF/jsp/");
       internalResourceViewResolver.setSuffix(".jsp");
       internalResourceViewResolver.setOrder(0);
        return internalResourceViewResolver;
    }

}

Declaring our bean definitions:

UserConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.spring.package.domain.User;

@Configuration
@ComponentScan("com.spring.package.*")
public class UserConfig {
   
       @Bean
       public User userA() {
              User user = new User();
              user.setName("userA");
              return user;
       }
       @Bean
       public User userB() {
              User user = new User();
              user.setName("userB");
              return user;
       }
}

Create the controller:

UserListController.java :

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.spring.package.domain.User;

@Controller
public class UserListController {
       private List<User> userList = new ArrayList<User>();
      
       @RequestMapping(value="/User.htm",method=RequestMethod.GET)
       public String showForm(ModelMap model){
              User user = new User();
              model.addAttribute("user", user);
              return "User";
       }
      
       @RequestMapping(value="/ShowUsers.htm",method=RequestMethod.POST)
       public String showUsers(ModelMap model, @ModelAttribute("user") User user, BindingResult result ){
              if(!result.hasErrors()){
                     userList.add(user);
              }else{
                     System.out.println("User has not been added to list.");;
              }
              model.addAttribute("Users", userList);
              return "ShowUsers";
       }
}

Domain User class:

public class User {
      
       private String name;
       private int age;
      
       public String getName() {
              return name;
       }
       public void setName(String name) {
              this.name = name;
       }
       public int getAge() {
              return age;
       }
       public void setAge(int age) {
              this.age = age;
       }

}


Index.jsp containing:

<jsp:forward page="/User.htm"></jsp:forward>

User.jsp containing:

<form:form method="POST" commandName="user" action="ShowUsers.htm">
       <table>
              <tr><td>Enter your name : </td><td> <form:input path="name" /><br/></td></tr>
              <tr><td>Age : </td><td> <form:input path="age" /><br/></td></tr>
              <tr><td colspan="2"><input type="submit" value="show user"></div></td></tr>
       </table>
</form:form>


showUsers.jsp containing :

<body style="color: green;">
       The following are the user is logged in :<br>
       <ul>
       <c:forEach items="${Users}" var="user">
              <li>Name : <c:out value="${user.name}" />; Age : <c:out value="${user.age}"/>
       </c:forEach>
       </ul>
</body>

And we are good to go. Bye bye xml.

We can also add test class to the above sample application:

UserConfigTest.java :


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import com.spring.export.configuration.UserConfig;
import com.spring.export.domain.User;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration // defaults to "file:src/main/webapp"
@ContextConfiguration(classes={UserConfig.class})
public class UserConfigTest{

       @Autowired
       @Qualifier("userA")
    private User user = null;

    /**
     * Tests message.
     */
    @Test
    public void testMessage() {  
        assertNotNull("Constructor message instance is null.", user);
       
        String msg = user.getName();
       
        assertNotNull("Message is null.", msg);
       
        String expectedMessage = "userA";
       
        assertEquals("Message should be '" + expectedMessage + "'.", expectedMessage, msg);

        System.out.println("message='{}'"+ msg);
    }
   
}