Skip to content

Posts tagged ‘Spring’

17
Jan

Spring 3 and RESTful Services

Introduction

This article describes the process for supporting REST (Representational State Transfer) in your application using Spring 3. For a variety of reasons including scalability, performance, and simplification, REST has gained critical mass as an alternative to SOAP-based Web Services and is now offered as part of JEE 6 in accordance with JSR-311. REST facilitates access to resources in a uniform way independent of the context or caller to promote simplified integration and facilitate re-use across architectural layers.

SpringMVC enhancements

Central to supporting REST in Spring 3 were enhancements made to SpringMVC and annotation driven controllers. In this article I present the hypothetical example of a motor vehicle distributor listing the types of vehicles that they sell. The HTTP GET method will be implemented in the controller for obtaining a list of vehicles or details of a specific vehicle and the output will be presented in the form of an object graph transformed to XML using XStream. XStream has been chosen as it is supported by the org.springframework.oxm.xstream.XStreamMarshaller class in the Spring Object/XML Mapping (OXM) package an does not require custom classes to marshall/unmarshall a payload.

Getting started

The quickest way to create the application structure for this example is with Maven. Here, we will use the web archetype to create an artifact deployable to our Tomcat servlet container.

mvn archetype:generate -DgroupId=net.comdynamics.springrestexample -DartifactId=springrestexample-web -DpackageName=net.comdynamics.springrestexample -Dversion=0.1-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp

The directory structure will be similar to the following. You will need to create the Java classes and resources listed in this article as part of the steps listed.

Sample project layout

Maven project

I have specified a property in the Maven POM file to indicate the use of Spring 3.0 Release 3.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.comdynamics.springrestexample</groupId>
    <artifactId>springrestexample-web</artifactId>
    <packaging>war</packaging>
    <version>0.1-SNAPSHOT</version>
    <name>springrestexample-web Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
			<id>maven2-repository.dev.java.net</id>
			<name>Java.net Repository for Maven</name>
			<url>http://download.java.net/maven/2</url>
			<layout>default</layout>
		</repository>
        <repository>
            <id>spring-milestone</id>
            <url>http://maven.springframework.org/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <properties>
        <spring.version>3.0.3.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.2.2</version>
            <optional>false</optional>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
        <finalName>springrestexample</finalName>
    </build>
</project>

JEE deployment descriptor

In the web.xml, the Spring bean configuration is referenced from applicationContext.xml. The Spring MVC DispatcherServlet is responsible for dispatching HTTP requests to our handler which in this scenario is the VehicleController class. Specifying /* with an asterisk ensures all incoming HTTP requests are processed by the Dispatcher.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">

    <display-name>springrestexample</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:/applicationContext.xml
        </param-value>
    </context-param>

    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>/WEB-INF/classes/log4j.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>springrestexample</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
	        <param-name>contextConfigLocation</param-name>
	        <param-value></param-value>
	    </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>springrestexample</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

Spring Application Context

The Spring application context wires the OXM XStream marshaller and handler. The context:component-scan element triggers annotation-based autowiring against the package specified.

<?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"
        xmlns:util="http://www.springframework.org/schema/util"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        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.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <context:component-scan base-package="net.comdynamics.springrestexample"/>

    <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>

    <bean id="marshallingHttpMessageConverter"
          class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
        <property name="marshaller" ref="xstreamMarshaller"/>
        <property name="unmarshaller" ref="xstreamMarshaller"/>
    </bean>

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <util:list id="beanList">
                <ref bean="marshallingHttpMessageConverter"/>
            </util:list>
        </property>
    </bean>

    <mvc:annotation-driven/>

</beans>

Domain Object

The Vehicle class is an immutable domain object that encapsulates id, make, and model fields.

package net.comdynamics.springrestexample.domain;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Vehicle {

    private static final Log log = LogFactory.getLog(Vehicle.class);

    private Long id;
    private String make;
    private String model;

    public Vehicle(Long id, String make, String model) {
        this.id = id;
        this.make = make;
        this.model = model;
    }

    public Long getId() {
        return id;
    }

    public String getMake() {
        return make;
    }

    public String getModel() {
        return model;
    }

	@Override
	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (this.id == null || !(obj instanceof Vehicle))
			return false;
		Vehicle vehicle = (Vehicle) obj;
		return this.id.equals(vehicle.getId());
	}

	@Override
	public int hashCode() {
		return this.id == null ? 1 : this.id.hashCode();
	}

}

Service Component

There is no reliance on a data tier in this example therefore I have created a service component which uses an in-memory collection of vehicles. The service exposes two methods: getVehicleById() and getAllVehicles(). These methods are called by the Controller class when incoming HTTP GET requests are processed by the Dispatcher.

package net.comdynamics.springrestexample.service;

import net.comdynamics.springrestexample.domain.Vehicle;

import java.util.*;

import javax.inject.Named;

@Named("vehicleService")
public class VehicleService {

    // Dummy list of vehicles
    static final Map<Long, Vehicle> vehicles = new HashMap<Long, Vehicle>();

    // Dummy entry to be returned if not found
    static final Vehicle notFound = new Vehicle(0L, "Vehicle", "Not Found");

    static {
        vehicles.put(1L, new Vehicle(1L, "Audi", "A1"));
        vehicles.put(2L, new Vehicle(2L, "Audi", "S4"));
        vehicles.put(3L, new Vehicle(3L, "Audi", "RS5"));
        vehicles.put(4L, new Vehicle(4L, "BMW", "120i"));
        vehicles.put(5L, new Vehicle(5L, "BMW", "330d"));
        vehicles.put(6L, new Vehicle(6L, "BMW", "M3"));
        vehicles.put(7L, new Vehicle(7L, "Ford", "Focus"));
        vehicles.put(8L, new Vehicle(8L, "Ford", "Mondeo"));
        vehicles.put(9L, new Vehicle(9L, "Volkswagen", "Polo TSI"));
        vehicles.put(10L,new Vehicle(10L, "Volkswagen", "Golf GTI"));
        vehicles.put(11L,new Vehicle(11L, "Volkswagen", "Tiguan"));
    }

    public Vehicle getVehicleById(Long vehicleId) {
        if (vehicleId < vehicles.size() + 1) {
            return vehicles.get(vehicleId);
        } else {
            // Return not found
            return notFound;
        }
    }

    /**
     * Return all vehicles as a List.
     */
    public List<Vehicle> getAllVehicles() {
        return new ArrayList<Vehicle>(vehicles.values());
    }

}

The Controller

The use of the JSR 330 annotation @Named facilitates auto-wiring of dependencies as this controller requires the VehicleService. Here, the annotation @RequestMapping is used to map a URI for a resource and the HTTP method supported which is this instance is GET. An HTTP request to http://server:port/springrestexample/vehicles/ will list all vehicles, and http://server:port/springrestexample/vehicles/3 will return the third vehicle in the motor vehicle distributors inventory. I have chosen to use the plural name (“vehicles”) and not the singular name (“vehicle”) in mappings as this approach is widely adopted for resources accessed using REST.

The @ResponseBody annotation ensures that the return type is written to the response HTTP body which in our case is handled by the XStream OXM class.

package net.comdynamics.springrestexample.rest;

import net.comdynamics.springrestexample.domain.Vehicle;
import net.comdynamics.springrestexample.service.VehicleService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
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 javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

@Controller
public class VehicleController {

    private static final Log log = LogFactory.getLog(VehicleController.class);

    @Inject
    @Named("vehicleService")
    private VehicleService vehicleService;

    /**
     * Get all vehicles.
     */
    @RequestMapping(value = "/vehicles", method = RequestMethod.GET)
    @ResponseBody
    public List<Vehicle> getAllVehicles() {
        return vehicleService.getAllVehicles();
    }

    /**
     * Get the vehicle matching a specific id.
     *
     * @param vehicleId the identifier of the requested vehicle
     */
    @RequestMapping(value = "/vehicles/{vehicleId}", method = RequestMethod.GET)
    @ResponseBody
    public Vehicle getVehicleById(@PathVariable Long vehicleId) {
        Vehicle vehicle = vehicleService.getVehicleById(vehicleId);
        log.debug("Got request for vehicle " + vehicleId + ", returning " + vehicle);
        return vehicle;
    }

}

Accessing resources via a web brower

Using a browser to get details for vehicle with an identifier of 10 at http://localhost:8080/springrestexample/vehicles/10 will produce the following:

HTTP GET for a single vehicle

When requesting all vehicles at http://localhost:8080/springrestexample/vehicles/ you should see the following:

HTTP GET for all vehicles

Conclusion

Support for REST in Spring 3 is provided using a SpringMVC controller that maps a URI to a resource and HTTP request method type. Future articles will examine the use of Spring URI REST templates and the Content Negotiating View Resolver support for different response types based on the user-agent accessing the resource.

19
Dec

Java Bean mapping with Dozer

Introduction

This article describes how to use Dozer to copy data between different Java class types. Dozer is a mapping framework that recursively copies data from one object to another with typical use cases such as converting between domain objects and data transfer objects (DTOs) or mapping classes from an external library to your application objects.

Add dependency references to your project

The Dozer dependencies can be referenced within a Maven project POM file as follows:

<dependency>
	<groupId>net.sf.dozer</groupId>
  	<artifactId>dozer</artifactId>
  	<version>5.3.1</version>
</dependency>
<dependency>
	<groupId>org.apache.xmlbeans</groupId>
	<artifactId>xmlbeans</artifactId>
	<version>2.4.0</version>
	<scope>runtime</scope>
</dependency>

Specify source classes

In this scenario the source classes contain collection objects that must also be mapped. These collection objects are non-primitive data types requiring that sub-mappings are also specified.

The containing object Basket for the source object.

package net.comdynamics.myapp;

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

public class Basket {

	private String customerName;
	private List<Product> products;

	public Basket() {
		products = new ArrayList<Product>();
	}

	public Basket(String customerName, List<Product> products) {
		this.customerName = customerName;
		this.products = products;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public List<Product> getProducts() {
		return products;
	}

	public void setProducts(List<Product> products) {
		this.products = products;
	}
}

The collection object Product for the source object.

package net.comdynamics.myapp;

import java.math.BigDecimal;

public class Product {

	private String name;
	private String description;
	private BigDecimal price;

	public Product() {
	}

	public Product(String name, String description, BigDecimal price) {
		this.name = name;
		this.description = description;
		this.price = price;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public BigDecimal getPrice() {
		return price;
	}

	public void setPrice(BigDecimal price) {
		this.price = price;
	}

}

Specify target classes

The target classes should contain fields that you need to copy data into as these will be mapped in the Dozer configuration file.

The containing object BasketCase for the target object.

package net.comdynamics.myapp;

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

public class BasketCase {

	private String customerNameOutput;
	private List<ProductOffering> productListOutput;

	public BasketCase() {
		productListOutput = new ArrayList<ProductOffering>();
	}

	public BasketCase(String customerName, List<ProductOffering> products) {
		this.customerNameOutput = customerName;
		this.productListOutput = products;
	}

	public String getCustomerNameOutput() {
		return customerNameOutput;
	}

	public void setCustomerNameOutput(String customerNameOutput) {
		this.customerNameOutput = customerNameOutput;
	}

	public List<ProductOffering> getProductListOutput() {
		return productListOutput;
	}

	public void setProductListOutput(List<ProductOffering> productListOutput) {
		this.productListOutput = productListOutput;
	}

}

The collection object ProductOffering for the target object.

package net.comdynamics.myapp;

import java.math.BigDecimal;

public class ProductOffering {

	private String nameOutput;
	private String descriptionOutput;
	private BigDecimal priceOnline;

	public ProductOffering() {
	}

	public ProductOffering(String nameOutput, String descriptionOutput,
			BigDecimal priceOnline) {
		this.nameOutput = nameOutput;
		this.descriptionOutput = descriptionOutput;
		this.priceOnline = priceOnline;
	}

	public String getNameOutput() {
		return nameOutput;
	}

	public void setNameOutput(String nameOutput) {
		this.nameOutput = nameOutput;
	}

	public String getDescriptionOutput() {
		return descriptionOutput;
	}

	public void setDescriptionOutput(String descriptionOutput) {
		this.descriptionOutput = descriptionOutput;
	}

	public BigDecimal getPriceOnline() {
		return priceOnline;
	}

	public void setPriceOnline(BigDecimal priceOnline) {
		this.priceOnline = priceOnline;
	}

}

Create Dozer XML mapping file

Create an XML file called dozerBeanMapping.xml and ensure it is accessible from your classpath at runtime. This file should include mapping for the containing Basket object and the collection Product object. When mapping the collection object you should specify the fully-qualified class name for the destination object using the b-hint element.


<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://dozer.sourceforge.net

http://dozer.sourceforge.net/schema/beanmapping.xsd">

	<mapping>
		<class-a>net.comdynamics.myapp.Basket</class-a>
		<class-b>net.comdynamics.myapp.BasketCase</class-b>
		<field>
			<a>customerName</a>
			<b>customerNameOutput</b>
		</field>
		<field>
		  	<a>products</a>
  			<b>productListOuput</b>
		  	<b-hint>net.comdynamics.myapp.ProductOffering</b-hint>
  		</field>
	</mapping>

	<mapping>
		<class-a>net.comdynamics.myapp.Product</class-a>
		<class-b>net.comdynamics.myapp.ProductOffering</class-b>
		<field>
			<a>name</a>
			<b>nameOutput</b>
		</field>
		<field>
			<a>description</a>
			<b>descriptionOutput</b>
		</field>
		<field>
			<a>price</a>
			<b>priceOnline</b>
		</field>
	</mapping>

</mappings>

Converting object types

Using the Dozer singleton mapper instance it is then possible to convert from the source object to the destination object. Using the above example classes:

// Create instance of Basket
Basket sourceObject = new Basket();
// Object populated with some values...
// etc
// Get mapping instance and convert
Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
// Convert source to destination object
BasketCase destObject = mapper.map(sourceObject, BasketCase.class);

Spring Integration

If you need to load custom mapping files in your Spring application context, add an entry similar to the following whereby each mapping file is listed separately:

<bean id="mapper" class="org.dozer.DozerBeanMapper">
    <property name="mappingFiles">
        <list>
            <value>xml-bean-mappings.xml</value>			   
            <value>dto-bean-mappings.xml</value>
        </list>
    </property>
</bean>