This tutorial is going to show you how to leverage Spring MVC’s view technology to build a sample application that generates a PDF document dynamically in order to be downloaded/opened by the user. For PDF generation, we will use the popular, open source PDF library called iText.

The application will provide a download link as follows:

 Spring MVC PDF View Demo

Clicking on the “Download PDF Document” link will cause the browser to download or open the generated PDF document. The following screenshot shows Chrome browser opens the document using its built-in PDF viewer:

Viewing PDF document in Chrome browser

 

About iText library

The iText PDF library was originally written by Bruno Lowagie, then later is developed and managed by iText Software Corp. The project is hosted on SourceForge.net and can be downloaded from the following link:

Download iText

As of this writing, the latest version of iText is 5.4.2. So after extracting the distribution archive, put the itextpdf-5.4.2.jar file to the project’s classpath.

 

Recommended Book: Getting started with Spring Framework

 

1. Subclassing AbstractView Class to Work with iText 5.x

Spring 3.x provides an AbstractPdfView abstract class which can be subclassed to create a helper class for generating PDF documents. However, it has a big drawback which the AbstractPdfView class only supports old API version of iText i.e. it is using the package com.lowagie.* (iText version <= 2.1.7) while the recent iText’s package changes to com.itextpdf.* (iText version >= 5.x)

The old iText version is no longer available nor supported, so subclassing AbstractPdfView class (as of Spring 3.x) is discouraged. Instead, we recommend to subclass the AbstractView class to create an iText 5.x-compatible version. Here is code of the subclass (AbstractITextPdfView.java):

package net.codejava.spring;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Map;

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

import org.springframework.web.servlet.view.AbstractView;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * This class is a work around for working with iText 5.x in Spring.
 * The code here is almost identical to the AbstractPdfView class. 
 *
 */
public abstract class AbstractITextPdfView extends AbstractView {

	public AbstractITextPdfView() {
		setContentType("application/pdf");
	}

	@Override
	protected boolean generatesDownloadContent() {
		return true;
	}
		
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		// IE workaround: write into byte array first.
		ByteArrayOutputStream baos = createTemporaryOutputStream();

		// Apply preferences and build metadata.
		Document document = newDocument();
		PdfWriter writer = newWriter(document, baos);
		prepareWriter(model, writer, request);
		buildPdfMetadata(model, document, request);

		// Build PDF document.
		document.open();
		buildPdfDocument(model, document, writer, request, response);
		document.close();

		// Flush to HTTP response.
		writeToResponse(response, baos);
	}

	protected Document newDocument() {
		return new Document(PageSize.A4);
	}
	
	protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException {
		return PdfWriter.getInstance(document, os);
	}
	
	protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request)
			throws DocumentException {

		writer.setViewerPreferences(getViewerPreferences());
	}
	
	protected int getViewerPreferences() {
		return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
	}
	
	protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) {
	}
	
	protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
			HttpServletRequest request, HttpServletResponse response) throws Exception;	
}

 

Note that code of this class is almost identical to the AbstractPdfView class, except it uses the package com.itextpdf.* instead of the com.lowagie.*. If curious, you can look at source code of the AbstractPdfView class which can be found inside the spring-webmvc-VERSION-sources.jar file.

 

Recommended Book: Spring in Action


2. Coding Model Class

Create a model class (Book.java) as follows:

package net.codejava.spring;

public class Book {
	private String title;
	private String author;
	private String isbn;
	private String publishedDate;
	private float price;

	public Book(String title, String author, String isbn, String publishedDate,
			float price) {
		this.title = title;
		this.author = author;
		this.isbn = isbn;
		this.publishedDate = publishedDate;
		this.price = price;
	}

	// getters and setters

}

The application will generate a PDF document that contains a list of books, thus this model class is needed.

 


3. Coding Entry JSP Page

Create home.jsp file under WEB-INF\jsp directory (you have to create the jsp directory first) with the following HTML code:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
	"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring MVC PDF View Demo (iText)</title>
</head>
<body>
	<div align="center">
		<h1>Spring MVC PDF View Demo (using iText library)</h1>
		<h3><a href="/downloadPDF">Download PDF Document</a></h3>
	</div>
</body>
</html>

As you see, this page simply displays a hyper link “Download PDF Document” that points to a relative URL which will be handled by a Spring controller class described below.


4. Coding Spring Controller Class

Create MainController class that acts as the Spring controller class as follows:

package net.codejava.spring;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

/**
 * A Spring controller that allows the users to download a PDF document
 * generated by the iText library.
 * 
 * @author www.codejava.net
 * 
 */
@Controller
public class MainController {

	/**
	 * Handle request to the default page
	 */
	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String viewHome() {
		return "home";
	}

	/**
	 * Handle request to download a PDF document 
	 */
	@RequestMapping(value = "/downloadPDF", method = RequestMethod.GET)
	public ModelAndView downloadExcel() {
		// create some sample data
		List<Book> listBooks = new ArrayList<Book>();
		listBooks.add(new Book("Spring in Action", "Craig Walls", "1935182358",
				"June 29th 2011", 31.98F));
		listBooks.add(new Book("Spring in Practice", "Willie Wheeler, Joshua White",
				"1935182056", "May 16th 2013", 31.95F));
		listBooks.add(new Book("Pro Spring 3",
				"Clarence Ho, Rob Harrop", "1430241071", "April 18th 2012", 31.85F));
		listBooks.add(new Book("Spring Integration in Action", "Mark Fisher", "1935182439",
				"September 26th 2012", 28.73F));

		// return a view which will be resolved by an excel view resolver
		return new ModelAndView("pdfView", "listBooks", listBooks);
	}
}

Banner will redirect to landing page with sports courses.This controller handles two URLs by two methods:

    • viewHome(): handles the URL request “/” which will redirect the user to the home.jsp page when the view name “home” is resolved by Spring’s InternalResourceViewResolver.
    • downloadPDF(): handles the URL request “/downloadPDF” which will be sent to the server when the user clicks on the hyperlink in the home.jsp page. This method creates some dummy data, e.g. a list of Spring framework books which will be passed to the model associates with the view name “pdfView”.

 

Recommended Book: Spring in Practice


5. Coding PDF View Class

Create PDFBuilder class that is a subclass of the AbstractITextPdfView class which we have created previously, with the following code:

package net.codejava.spring;

import java.util.List;
import java.util.Map;

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

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * This view class generates a PDF document 'on the fly' based on the data
 * contained in the model.
 * @author www.codejava.net
 *
 */
public class PDFBuilder extends AbstractITextPdfView {

	@Override
	protected void buildPdfDocument(Map<String, Object> model, Document doc,
			PdfWriter writer, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		// get data model which is passed by the Spring container
		List<Book> listBooks = (List<Book>) model.get("listBooks");
		
		doc.add(new Paragraph("Recommended books for Spring framework"));
		
		PdfPTable table = new PdfPTable(5);
		table.setWidthPercentage(100.0f);
		table.setWidths(new float[] {3.0f, 2.0f, 2.0f, 2.0f, 1.0f});
		table.setSpacingBefore(10);
		
		// define font for table header row
		Font font = FontFactory.getFont(FontFactory.HELVETICA);
		font.setColor(BaseColor.WHITE);
		
		// define table header cell
		PdfPCell cell = new PdfPCell();
		cell.setBackgroundColor(BaseColor.BLUE);
		cell.setPadding(5);
		
		// write table header 
		cell.setPhrase(new Phrase("Book Title", font));
		table.addCell(cell);
		
		cell.setPhrase(new Phrase("Author", font));
		table.addCell(cell);

		cell.setPhrase(new Phrase("ISBN", font));
		table.addCell(cell);
		
		cell.setPhrase(new Phrase("Published Date", font));
		table.addCell(cell);
		
		cell.setPhrase(new Phrase("Price", font));
		table.addCell(cell);
		
		// write table row data
		for (Book aBook : listBooks) {
			table.addCell(aBook.getTitle());
			table.addCell(aBook.getAuthor());
			table.addCell(aBook.getIsbn());
			table.addCell(aBook.getPublishedDate());
			table.addCell(String.valueOf(aBook.getPrice()));
		}
		
		doc.add(table);
		
	}

}

The method buildPdfDocument() uses the iText API to generate a simple PDF document that contains a list of books in tabular format. We will configure Spring to pick up this view class as described below.

 


6. Configuring PDF View Class

Create views.properties file under the project’s classpath (which is under src directory in the Eclipse project), with the following line:

pdfView.(class)=net.codejava.spring.PDFBuilder

That tells the Spring’s view resolver to pick the net.codejava.spring.PDFBuilder class to render response for the view name “pdfView”.


7. Writing Spring Configuration File

Configure Spring MVC and view resolvers in spring-mvc.xml file under WEB-INF directory as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<context:component-scan base-package="net.codejava.spring" />

   <bean id="viewResolver1" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
  		<property name="order" value="1"/>
  		<property name="basename" value="views"/>
	</bean>
	
	<bean id="viewResolver2"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  		<property name="order" value="2"/>
		<property name="prefix" value="/WEB-INF/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
</beans>

Here we configure two view resolvers:

    • ResourceBundleViewResolver: to resolve view names specified in the views.properties file.
    • InternalResourceViewResolver: to resolve view names to JSP pages.

Note that the ResourceBundleViewResolver has higher priority (order=”1”) than the InternalResourceViewResolver (order=”2”) so the view names specified in the views.properties are processed first.

Finally we would have the following project structure in Eclipse IDE:

Spring MVC PDF View Project Structure

 

Recommended Book: Pro Spring 3


8. Testing the Application

To test this sample application, deploy the project on Tomcat server under a context named SpringMvcPdfViewDemo. Type the following URL in browser:

http://localhost:8080/SpringMvcPdfViewDemo

The default page (home.jsp) gets displayed as follows:

Spring MVC PDF View Demo

Click on the “Download PDF Document” hyperlink, depending on the browser type and setup, it will opens the document inside the browser or ask for downloading. Here the document is opened by Chrome’s built-in PDF viewer:

Viewing PDF document in Chrome browser

 

Recommended Book: Spring Integration in Action

Related Course: The Java Spring Tutorial

Attachments:
Download this file (SpringMvcPdfViewDemo.zip)SpringMvcPdfViewDemo.zip[Eclipse project]5247 kB
Start learning on Udemy today!