Spring 4 – ContentNegotiatingViewResolver Sample

Hello every one, recently in one of my project i had a requirement that i need to generate multiple views based on the request type, this has lead me to write this post. In this article i will be explaining about “ContentNegotiatingViewResolver” class.

ContentNegotiatingViewResolver class is a implementation of ViewResolver. The ContentNegotiatingViewResolver does not resolve the view by itself unlike the ViewResolver based on the request type or the Header type in fact this delegates the request to other ViewResolvers that are configured explicitly.

This view resolver uses the requested media type to select a suitable View for a request. The requested media type is determined through the configured ContentNegotiationManager. Once the requested media type has been determined, this resolver queries each delegate view resolver for a View and determines if the requested media type is compatible with the view’s content type). The most compatible view is returned.

Below is the Project Structure.
ProjStruct-1

the project pom.xml is as follows

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.spring.ContentNegotiatingViewResolver</groupId>
	<artifactId>ContentNegotiatingViewResolver</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>ContentNegotiatingViewResolver</name>
	<description>ContentNegotiatingViewResolver</description>

	<properties>
		<springframework.version>4.0.6.RELEASE</springframework.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${springframework.version}</version>
		</dependency>

		<!-- Needed for XML View (with JAXB2) -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-oxm</artifactId>
			<version>${springframework.version}</version>
		</dependency>

		<!-- Needed for JSON View -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.4.1.3</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.4.1</version>
		</dependency>

		<!-- Needed for PDF View -->
		<dependency>
			<groupId>com.lowagie</groupId>
			<artifactId>itext</artifactId>
			<version>4.2.1</version>
		</dependency>

		<!-- Needed for XLS View -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.10-beta2</version>
		</dependency>

		<!-- Servlet dependencies -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
		</dependency>

	</dependencies>


	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-war-plugin</artifactId>
					<version>2.4</version>
					<configuration>
						<warSourceDirectory>src/main/webapp</warSourceDirectory>
						<warName>Spring4MVCContentNegotiatingViewResolverExample</warName>
						<failOnMissingWebXml>false</failOnMissingWebXml>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<configuration>
						<source>1.7</source>
						<target>1.7</target>
					</configuration>
				</plugin>
				<plugin>
					<groupId>org.codehaus.mojo</groupId>
					<artifactId>tomcat-maven-plugin</artifactId>
					<version>1.0-beta-1</version>
				</plugin>
			</plugins>
		</pluginManagement>

		<finalName>Spring4MVCContentNegotiatingViewResolverExample</finalName>
	</build>
</project>

Now let us create the configuration classes as this article is based on no-xml.
AppConfig.java

package com.spark.spring.config;

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

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import com.spark.spring.domain.objects.Pizza;
import com.spark.spring.view.resolvers.ExcelViewResolver;
import com.spark.spring.view.resolvers.Jaxb2MarshallingXmlViewResolver;
import com.spark.spring.view.resolvers.JsonViewResolver;
import com.spark.spring.view.resolvers.PdfViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.spark.spring")
public class AppConfig extends WebMvcConfigurerAdapter {

	public void configureContentNegotiation(
			ContentNegotiationConfigurer contentNegotiationConfigurer) {
		contentNegotiationConfigurer.ignoreAcceptHeader(true)
				.defaultContentType(MediaType.TEXT_HTML);
	}

	@Bean
	public ViewResolver contentNegotiationViewResolver(
			ContentNegotiationManager contentNegotiationManager) {
		ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
		contentNegotiatingViewResolver
				.setContentNegotiationManager(contentNegotiationManager);

		List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
		resolvers.add(jaxb2MarshallingXmlViewResolver());
		resolvers.add(jsonViewResolver());
		resolvers.add(excelViewResolver());
		resolvers.add(pdfViewResolver());

		contentNegotiatingViewResolver.setViewResolvers(resolvers);
		return contentNegotiatingViewResolver;
	}

	/*
	 * Configure View resolver to provide XML output Uses JAXB2 marshaller to
	 * marshall/unmarshall POJO's (with JAXB annotations) to XML
	 */
	@Bean
	public ViewResolver jaxb2MarshallingXmlViewResolver() {
		Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
		marshaller.setClassesToBeBound(Pizza.class);
		return new Jaxb2MarshallingXmlViewResolver(marshaller);
	}

	/*
	 * Configure View resolver to provide JSON output using JACKSON library to
	 * convert object in JSON format.
	 */
	@Bean
	public ViewResolver jsonViewResolver() {
		return new JsonViewResolver();
	}
	
	/*
	 * Configure View resolver to provide PDF output using lowagie pdf library to
	 * generate PDF output for an object content
	 */
	@Bean
	public ViewResolver pdfViewResolver() {
		return new PdfViewResolver();
	}

	/*
	 * Configure View resolver to provide XLS output using Apache POI library to
	 * generate XLS output for an object content
	 */
	@Bean
	public ViewResolver excelViewResolver() {
		return new ExcelViewResolver();
	}
	
	/*
	 * Configure View resolver to provide HTML output This is the default format
	 * in absence of any type suffix.
	 */
	@Bean
	public ViewResolver jspViewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		return viewResolver;
	}
}

Let us look into the above class little deep, First we have created ContentNegotiationManager which is used to determine the requested media types of a request by delegating to a list of ContentNegotiationStrategy instances. By default PathExtensionContentNegotiationStrategy is consulted (which uses the URL extension e.g. .xls, .pdf,.json..) , followed by ParameterContentNegotiationStrategy (which uses the request parameter ‘format=xls’ e.g.), followed by HeaderContentNegotiationStrategy (which uses HTTP Accept Headers) the ContentNegotiationManager instance is injected by Spring Container.
Snippet

@Bean
	public ViewResolver contentNegotiationViewResolver(
			ContentNegotiationManager contentNegotiationManager) {
		ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
		contentNegotiatingViewResolver
				.setContentNegotiationManager(contentNegotiationManager);

once the ContentNegotiationManager instance is ready then we need to pass different implementations of view resolvers in order to resolve dynamically in the runtime.
Snippet:

List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
		resolvers.add(jaxb2MarshallingXmlViewResolver());
		resolvers.add(jsonViewResolver());
		resolvers.add(excelViewResolver());
		resolvers.add(pdfViewResolver());

		contentNegotiatingViewResolver.setViewResolvers(resolvers);
		return contentNegotiatingViewResolver;

There is one more configuration class required inorder to map the request using the front control called “DispatcherServlet”

/**
 * 
 */
package com.spark.spring.config;

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

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

/**
 * @author Sony
 * 
 */
public class AppInitializer implements WebApplicationInitializer {

	public void onStartup(ServletContext container) throws ServletException {

		AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
		ctx.register(AppConfig.class);
		ctx.setServletContext(container);

		ServletRegistration.Dynamic servlet = container.addServlet(
				"dispatcher", new DispatcherServlet(ctx));

		servlet.setLoadOnStartup(1);
		servlet.addMapping("/");
	}

}

Now let us create different ViewResolver Classes inorder to inject them into ContentNegotiatingViewResolver Object.
Jaxb2MarshallingXmlViewResolver.java

/**
 * 
 */
package com.spark.spring.view.resolvers;

import java.util.Locale;

import org.springframework.oxm.Marshaller;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.xml.MarshallingView;

/**
 * @author Sony
 * 
 */
public class Jaxb2MarshallingXmlViewResolver implements ViewResolver {

	private Marshaller marshaller;

	public Jaxb2MarshallingXmlViewResolver(Marshaller marshaller) {
		this.marshaller = marshaller;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.web.servlet.ViewResolver#resolveViewName(java.lang
	 * .String, java.util.Locale)
	 */
	public View resolveViewName(String viewName, Locale locale)
			throws Exception {
		MarshallingView view = new MarshallingView();
		view.setMarshaller(marshaller);
		return view;
	}

}

JsonViewResolver.java

/**
 * 
 */
package com.spark.spring.view.resolvers;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

/**
 * @author Sony
 * 
 */
public class JsonViewResolver implements ViewResolver {

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.web.servlet.ViewResolver#resolveViewName(java.lang
	 * .String, java.util.Locale)
	 */
	public View resolveViewName(String viewName, Locale locale)
			throws Exception {
		MappingJackson2JsonView view = new MappingJackson2JsonView();
		view.setPrettyPrint(true);
		return view;
	}

}

ExcelViewResolver.java

/**
 * 
 */
package com.spark.spring.view.resolvers;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

import com.spark.spring.views.GenerateExcel;

/**
 * @author Sony
 *
 */
public class ExcelViewResolver implements ViewResolver {

	/* (non-Javadoc)
	 * @see org.springframework.web.servlet.ViewResolver#resolveViewName(java.lang.String, java.util.Locale)
	 */
	@Override
	public View resolveViewName(String viewname, Locale locale) throws Exception {
		GenerateExcel generateExcel = new GenerateExcel();
		return generateExcel;
	}

}

PdfViewResolver.java

/**
 * 
 */
package com.spark.spring.view.resolvers;

import java.util.Locale;

import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

import com.spark.spring.views.GeneratePdf;

/**
 * @author Sony
 *
 */
public class PdfViewResolver implements ViewResolver{

	@Override
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		GeneratePdf generatePdf = new GeneratePdf();
		return generatePdf;
	}

}

Below are utility classes for generating Excel content and PDF Content.

GenerateExcel.java

/**
 * 
 */
package com.spark.spring.views;

import java.util.Map;

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

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.web.servlet.view.document.AbstractExcelView;

import com.spark.spring.domain.objects.Pizza;

/**
 * @author Sony
 * 
 */
public class GenerateExcel extends AbstractExcelView {

	@Override
	protected void buildExcelDocument(Map<String, Object> model,
			HSSFWorkbook workbook, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		Pizza pizza = (Pizza) model.get("pizza");

		Sheet sheet = workbook.createSheet("sheet 1");
		CellStyle style = workbook.createCellStyle();
		style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
		style.setFillPattern(CellStyle.SOLID_FOREGROUND);
		style.setAlignment(CellStyle.ALIGN_CENTER);
		Row row = null;
		Cell cell = null;
		int rowCount = 0;
		int colCount = 0;

		// Create header cells
		row = sheet.createRow(rowCount++);

		cell = row.createCell(colCount++);
		cell.setCellStyle(style);
		cell.setCellValue("Name");

		cell = row.createCell(colCount++);
		cell.setCellStyle(style);
		cell.setCellValue("Flavor");

		cell = row.createCell(colCount++);
		cell.setCellStyle(style);
		cell.setCellValue("Toppings");

		// Create data cells
		row = sheet.createRow(rowCount++);
		colCount = 0;
		row.createCell(colCount++).setCellValue(pizza.getName());
		row.createCell(colCount++).setCellValue(pizza.getFlavor());

		StringBuffer toppings = new StringBuffer("");
		for (String topping : pizza.getToppings()) {
			toppings.append(topping);
			toppings.append(" ");
		}
		row.createCell(colCount++).setCellValue(toppings.toString());

		for (int i = 0; i < 3; i++)
			sheet.autoSizeColumn(i, true);
	}

}

GeneratePdf.java

/**
 * 
 */
package com.spark.spring.views;

import java.awt.Color;
import java.util.Map;

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

import org.springframework.web.servlet.view.document.AbstractPdfView;

import com.lowagie.text.Document;
import com.lowagie.text.Element;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.spark.spring.domain.objects.Pizza;

/**
 * @author Sony
 *
 */
public class GeneratePdf extends AbstractPdfView{

	@Override
	protected void buildPdfDocument(Map<String, Object> model,
			Document document, PdfWriter writer, HttpServletRequest request,
			HttpServletResponse response) throws Exception {

		Pizza pizza = (Pizza) model.get("pizza");

		PdfPTable table = new PdfPTable(3);
		table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
		table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
		table.getDefaultCell().setBackgroundColor(Color.lightGray);

		table.addCell("Name");
		table.addCell("Flavor");
		table.addCell("Toppings");

		table.addCell(pizza.getName());
		table.addCell(pizza.getFlavor());

		StringBuffer toppings = new StringBuffer("");
		for (String topping : pizza.getToppings()) {
			toppings.append(topping);
			toppings.append(" ");
		}
		table.addCell(toppings.toString());
		document.add(table);

	}
}

Now its time for us to write the .JSP file

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"  pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
	<title>Pizza order JSP View</title>
</head>
<body>
	<table border="1">
		<tr>
		<td>NAME</td>
		<td>Flavor</td>
		<td>Toppings</td>
		</tr>
		<tr>
			<td>${pizza.name}</td>
			<td>${pizza.flavor}</td>
			<td>
				<c:forEach var="item" items="${pizza.toppings}">
					<c:out value="${item}"/>&nbsp; 
				</c:forEach>
			</td>
		</tr>
	</table>
</body>
</html>

That is it we have done with our coding, now its time for us to deploy and test.

resp-json

resp-pdf

resp-xml

credits to : http://websystique.com/springmvc/spring-4-mvc-contentnegotiatingviewresolver-example/

Happy Coding With Spring 🙂

Advertisements

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