Sunday, 19 August 2012

Spring REST Tutorial

In this post I’ll show you how to implement a RESTful web service using the Spring framework. REST support was introduced as part of Spring 3 and is built on top of the existing Spring MVC stack, so anyone that has worked with Spring MVC in the past should find the REST support very easy to grasp. I’ll provide a step by step guide so even if you’re not familiar with Spring MVC you should be able get a RESTful service up and running quickly.

What is REST?
REST is an architectural style that has evolved from existing web technologies to allow client applications to communicate with a server using a simple and familiar approach. The approach is familiar because REST is built on top of the HTTP protocol, a protocol that has formed the backbone of the web for years. REST leverages the existing capabilities of HTTP and uses them to provide an architectural approach for implementing a simple and effective client server model.
This formal definition of REST and what constitutes pure RESTful behaviour is well beyond the scope of this blog. It’s definitely something that’s worth discussing so I’ll be posting on this topic pretty soon. For now though I’ll take you through the process of building, deploying and testing a simple RESTful service using the Spring framework.

Technologies Used
Our REST example will be built using the following technology stack.
  • Spring MVC 3.1
  • Maven
  • Tomcat
It is assumed that readers have a basic knowledge of the technologies listed above. To help you get up and running as quick as possible I’ll attach the full project so that you can download and import it into STS (or Eclipse). 

Creating a Project Skeleton.
We'll start off by creating a basic Spring web project. You can either create it by hand or use something like Maven or Spring Roo to generate the basic project structure for you. Given the simplicity of our sample application you will probably create it by hand. Figure 1.0 below shows what the project structure should look like.

The main project components are described below.
  • src/main/java folder – this folder structure will contain all java source required by your project. This includes the MVC controller that will process REST requests and a few other bits and pieces we'll cover later.
  • src/main/resources folder – this folder structure will contain all resources required by your project. This could contain any number of configuration files in an enterprise application but given the simplicity of our demo app we’ll just have the log4j configuration here.
  • META-INF/context.xml – this file contains resources that can be used by Tomcat at runtime. This file is empty in our sample application but in an enterprise application would contain JNDI references to any number of enterprise resources (data sources, MQ managers etc) 
  • rest-services-config.xml - this file contains definitions for the various Spring beans required by our application. This will be covered in detail later.
  • Web.xml – This file is the base configuration for our java web application and is required as part of the standard Java Servlet specification. It is used to bootstrap the application on server start up and contains references to any Servlets or filters required by the application. This will be covered in detail later.
  • POM.xml – this is our Maven configuration and contains information necessary to resolve dependencies (external libraries) and build a deployable WAR file.
Figure 1.0 - Project Structure
Once you have the above project skeleton in place you’ll need to start configuring the application. We’ll start off with Web.xml which is shown below. I’ve commented each section to explain exactly what is being configured and how it impacts the application.
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:             xmlns="http://java.sun.com/xml/ns/javaee"  
4:             xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
5:             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
6:             id="WebApp_ID"  
7:             version="2.5">  
8:       <display-name>REST Sample</display-name>  
9:       <!--  
10:            Main configuration file for this Spring web application.  
11:       -->  
12:       <context-param>  
13:            <param-name>contextConfigLocation</param-name>  
14:            <param-value>  
15:                 /WEB-INF/config/rest-services-config.xml  
16:            </param-value>  
17:       </context-param>  
18:       <!--  
19:            Loads the Spring web application context using the config file defined above.  
20:       -->  
21:       <listener>  
22:            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
23:       </listener>  
24:       <!--  
25:            Define the Spring Dispatcher Servlet for the REST services.  
26:            The 'contextConfiguration' param with an empty value means that the  
27:            Spring Context won't try to load a default file called restservices-servlet.xml  
28:        -->  
29:       <servlet>  
30:            <servlet-name>restservices</servlet-name>  
31:            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
32:            <init-param>  
33:                 <param-name>contextConfigLocation</param-name>  
34:                 <param-value></param-value>  
35:            </init-param>  
36:            <load-on-startup>1</load-on-startup>  
37:       </servlet>  
38:       <!--  
39:            This Servlet mapping means that this Servlet will handle all incoming requests  
40:        -->  
41:       <servlet-mapping>  
42:            <servlet-name>restservices</servlet-name>  
43:            <url-pattern>/</url-pattern>  
44:       </servlet-mapping>  
45:  </web-app>  
Next we’ll set up rest-services-config.xml the content of which is shown below. I’ve commented each section to explain exactly what is being configured and how it impacts the application.
 <?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:oxm="http://www.springframework.org/schema/oxm"  
           xmlns:util="http://www.springframework.org/schema/util"  
           xmlns:mvc="http://www.springframework.org/schema/mvc"  
           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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd  
                                    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
                                    http://www.springframework.org/schema/oxm  
                                    http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd  
                                    http://www.springframework.org/schema/util  
                                     http://www.springframework.org/schema/util/spring-util-3.0.xsd">  
      <!--  
            Enables automatic mapping of fund objects to and from JSON  
      -->  
      <mvc:annotation-driven/>  
      <!--  
            Setup spring to pull in @Controller, @RequestMapping, etc Configuration scans specified packages  
            for classes configured as Spring managed beans and automatically sets up objects annotated with  
            @Controller, @Service etc.  
      -->  
      <context:component-scan base-package="com.blog.samples.webservices.rest" />  
      <context:component-scan base-package="com.blog.samples.services" />  
      <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />  
      <!--  
            Configures view for returning JSON to the client  
      -->  
      <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">  
           <property name="contentType" value="text/plain"/>  
      </bean>  
      <!--  
            maps handler methods based on HTTP paths
      -->  
      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
           <property name="messageConverters">  
                <util:list id="beanList">  
                     <ref bean="jsonMessageConverter"/>  
                </util:list>  
           </property>  
      </bean>  
      <!--  
            Converts JSON to POJO and vice versa  
      -->  
      <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>  
 </beans>  
The final part of the project skeleton is our maven POM file. This contains build and dependency details for our application and is defined below.
1:  <?xml version="1.0"?>  
2:  <project xmlns="http://maven.apache.org/POM/4.0.0"  
3:             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0  
5:                                      http://maven.apache.org/maven-v4_0_0.xsd">  
6:       <artifactId>rest-sample</artifactId>  
7:       <modelVersion>4.0.0</modelVersion>  
8:       <inceptionYear>2011</inceptionYear>  
9:       <packaging>war</packaging>  
10:       <groupId>com.blog.rest</groupId>  
11:       <version>1.0</version>  
12:        <properties>  
13:      <releaseCandidate>1</releaseCandidate>  
14:      <spring.version>3.1.1.RELEASE</spring.version>  
15:      <java.version>1.5</java.version>  
16:      <jackson.mapper.version>1.5.6</jackson.mapper.version>  
17:      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
18:      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>  
19:      <maven.javadoc.reporting.version>2.7</maven.javadoc.reporting.version>  
20:      <commons.logging.version>1.1.1</commons.logging.version>  
21:      <log4j.version>1.2.16</log4j.version>  
22:      <context.path>rest-sample</context.path>  
23:      <jackson.mapper.version>1.5.6</jackson.mapper.version>  
24:    </properties>  
25:       <build>  
26:            <resources>  
27:                 <resource>  
28:                      <directory>src/main/resources</directory>  
29:                      <filtering>true</filtering>  
30:                 </resource>  
31:            </resources>  
32:            <plugins>  
33:                 <plugin>  
34:                      <groupId>org.apache.maven.plugins</groupId>  
35:                      <artifactId>maven-war-plugin</artifactId>  
36:                      <configuration>  
37:                           <warName>${context.path}</warName>  
38:                      </configuration>  
39:                 </plugin>  
40:            </plugins>  
41:       </build>  
42:       <dependencies>  
43:       <dependency>  
44:        <groupId>log4j</groupId>  
45:        <artifactId>log4j</artifactId>  
46:        <version>${log4j.version}</version>  
47:      </dependency>  
48:      <dependency>  
49:        <groupId>org.springframework</groupId>  
50:        <artifactId>spring-core</artifactId>  
51:        <version>${spring.version}</version>  
52:      </dependency>  
53:      <dependency>  
54:        <groupId>org.springframework</groupId>  
55:        <artifactId>spring-context</artifactId>  
56:        <version>${spring.version}</version>  
57:      </dependency>  
58:      <dependency>  
59:        <groupId>org.springframework</groupId>  
60:        <artifactId>spring-beans</artifactId>  
61:        <version>${spring.version}</version>  
62:      </dependency>  
63:      <dependency>  
64:        <groupId>org.springframework</groupId>  
65:        <artifactId>spring-aop</artifactId>  
66:        <version>${spring.version}</version>  
67:      </dependency>  
68:      <dependency>  
69:        <groupId>org.springframework</groupId>  
70:        <artifactId>spring-aspects</artifactId>  
71:        <version>${spring.version}</version>  
72:      </dependency>  
73:      <dependency>  
74:        <groupId>org.springframework</groupId>  
75:        <artifactId>spring-asm</artifactId>  
76:        <version>${spring.version}</version>  
77:      </dependency>  
78:      <dependency>  
79:        <groupId>org.springframework</groupId>  
80:        <artifactId>spring-expression</artifactId>  
81:        <version>${spring.version}</version>  
82:      </dependency>  
83:      <dependency>  
84:                 <groupId>org.springframework</groupId>  
85:                 <artifactId>spring-web</artifactId>  
86:                 <version>${spring.version}</version>  
87:            </dependency>  
88:            <dependency>  
89:                 <groupId>org.springframework</groupId>  
90:                 <artifactId>spring-webmvc</artifactId>  
91:                 <version>${spring.version}</version>  
92:            </dependency>  
93:            <dependency>  
94:                 <groupId>org.codehaus.jackson</groupId>  
95:                 <artifactId>jackson-mapper-asl</artifactId>  
96:                 <version>${jackson.mapper.version}</version>  
97:            </dependency>  
98:            <dependency>  
99:                 <groupId>javax.servlet</groupId>  
100:                 <artifactId>servlet-api</artifactId>  
101:                 <version>2.4</version>  
102:            </dependency>  
103:       </dependencies>  
104:  </project>  
At this point you should have a project skeleton in place and it should look like the workspace in figure 1.0.

Defining a Domain Object
This sample application will expose a series of RESTful methods that allow client applications to perform operations on Funds. Our fund object is a very simple POJO and is defined as follows.
1:  package com.blog.samples.domain;  
2:    
3:  import java.util.Date;  
4:  import org.codehaus.jackson.map.annotate.JsonSerialize;  
5:  import com.blog.samples.web.utils.DateSerializer;  
6:    
7:  /**  
8:   * The Class Fund.  
9:   */  
10:  public class Fund{  
11:    
12:       private String fundId;  
13:       private String fundDescription;  
14:       private double bidPrice;  
15:       private double offerPrice;  
16:       private Date lastUpdated;  
17:    
18:       public Fund()  
19:       { }  
20:    
21:       /**  
22:        * Gets the fund id.  
23:        *  
24:        * @return the fund id  
25:        */  
26:       public String getFundId() {  
27:            return fundId;  
28:       }  
29:    
30:       /**  
31:        * Sets the fund id.  
32:        *  
33:        * @param fundId the new fund id  
34:        */  
35:       public void setFundId(String fundId) {  
36:            this.fundId = fundId;  
37:       }  
38:    
39:       /**  
40:        * Gets the fund description.  
41:        *  
42:        * @return the fund description  
43:        */  
44:       public Object getFundDescription() {  
45:            return fundDescription;  
46:       }  
47:    
48:       /**  
49:        * Sets the fund description.  
50:        *  
51:        * @param fundDescription the new fund description  
52:        */  
53:       public void setFundDescription(String fundDescription) {  
54:            this.fundDescription = fundDescription;  
55:       }  
56:    
57:       /**  
58:        * Gets the bid price.  
59:        *  
60:        * @return the bid price  
61:        */  
62:       public double getBidPrice() {  
63:            return bidPrice;  
64:       }  
65:    
66:       /**  
67:        * Sets the bid price.  
68:        *  
69:        * @param bidPrice the new bid price  
70:        */  
71:       public void setBidPrice(double bidPrice) {  
72:            this.bidPrice = bidPrice;  
73:       }  
74:    
75:       /**  
76:        * Gets the offer price.  
77:        *  
78:        * @return the offer price  
79:        */  
80:       public double getOfferPrice() {  
81:            return offerPrice;  
82:       }  
83:    
84:       /**  
85:        * Sets the offer price.  
86:        *  
87:        * @param offerPrice the new offer price  
88:        */  
89:       public void setOfferPrice(double offerPrice) {  
90:            this.offerPrice = offerPrice;  
91:       }  
92:    
93:       /**  
94:        * Gets the last updated.  
95:        *  
96:        * @return the last updated  
97:        */  
98:       @JsonSerialize(using=DateSerializer.class)  
99:       public Date getLastUpdated() {  
100:            return lastUpdated;  
101:       }  
102:    
103:       /**  
104:        * Sets the last updated.  
105:        *  
106:        * @param lastUpdated the new last updated  
107:        */  
108:       public void setLastUpdated(Date lastUpdated) {  
109:            this.lastUpdated = lastUpdated;  
110:       }  
111:    
112:       @Override  
113:       public String toString() {  
114:            return "Fund [fundId=" + fundId + ", fundDescription="  
115:                      + fundDescription + ", bidPrice=" + bidPrice + ", offerPrice="  
116:                      + offerPrice + ", lastUpdated=" + lastUpdated + "]";  
117:       }  
118:  }  
This is a very simple class and the only part worth mentioning is the @JsonSerialize(using=DateSerializer.class) annotation on the getLastUpdated method. This annotation ensures that Java dates are converted into a suitable JSON date format before being returned to the client. The DateDeserializer class referenced in the annotation handles this conversion and is defined as follows.
1:  package com.blog.samples.web.utils;  
2:    
3:  import java.io.IOException;  
4:  import java.text.SimpleDateFormat;  
5:  import java.util.Date;  
6:  import org.codehaus.jackson.JsonGenerator;  
7:  import org.codehaus.jackson.JsonProcessingException;  
8:  import org.codehaus.jackson.map.JsonSerializer;  
9:  import org.codehaus.jackson.map.SerializerProvider;  
10:    
11:  /**  
12:   * The Class DateSerializer.  
13:   */  
14:  public class DateSerializer extends JsonSerializer<Date> {  
15:    
16:    /* (non-Javadoc)  
17:     * @see org.codehaus.jackson.map.JsonSerializer#serialize(java.lang.Object, org.codehaus.jackson.JsonGenerator, org.codehaus.jackson.map.SerializerProvider)  
18:     */  
19:    @Override  
20:    public void serialize(Date value_p, JsonGenerator gen, SerializerProvider prov_p)  
21:      throws IOException, JsonProcessingException  
22:    {  
23:      SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");  
24:      String formattedDate = formatter.format(value_p);  
25:      gen.writeString(formattedDate);  
26:    }  
27:  }  
Defining REST Operations
Next we’ll create a controller and define the RESTful methods that we want to expose to client applications. Following a standard REST approach we’ll define methods to handle the following different types of request.

HTTP Action
URL
Purpose
GET
http://localhost:8080/rest-sample/rest/funds/12345
Returns fund information to the client for fund Id  specified in URL (in this case 1235)
GET
http://localhost:8080/rest-sample/rest/funds/
Returns fund information to the client for all available funds
POST
http://localhost:8080/rest-sample/rest/funds/
Create a new fund on the sever using the fund data in the HTTP request body. The path to the newly created resource is returned in a HTTP header as follows Location: /rest-sample/rest/funds/12345 
PUT
http://localhost:8080/rest-sample/rest/funds/123456
Update existing fund resource with fund data in HTTP request body. The updated fund data is returned to the client. Returning the updated fund data is not absolutely necessary but can be convenient where the server sets object values like ids or timestamps. By returning the updated entity to the client we can guarantee that the client has the exact current state of that entity.
DELETE
http://localhost:8080/rest-sample/rest/funds/123456
Deletes the fund specified by the fund Id in the URL.

Get a Fund - HTTP GET
1:       @RequestMapping(value = "/rest/funds/{fundId}", method = RequestMethod.GET)  
2:       public ModelAndView getFund(@PathVariable("fundId") String fundId_p) {  
3:            Fund fund = null;  
4:    
5:            /* validate fund Id parameter */  
6:            if (isEmpty(fundId_p) || fundId_p.length() < 5) {  
7:                 String sMessage = "Error invoking getFund - Invalid fund Id parameter";  
8:                 return createErrorResponse(sMessage);  
9:            }  
10:    
11:            try {  
12:                 fund = fundService_i.getFundById(fundId_p);  
13:            } catch (Exception e) {  
14:                 String sMessage = "Error invoking getFund. [%1$s]";  
15:                 return createErrorResponse(String.format(sMessage, e.toString()));  
16:            }  
17:    
18:            logger_c.debug("Returing Fund: " + fund.toString());  
19:            return new ModelAndView(jsonView_i, DATA_FIELD, fund);  
20:       }  
This method handles HTTP GET requests for a specified fund id.
Line 1 - RequestMapping annotation ensures that incoming HTTP requests to /rest/funds/{fundId} will be routed toward this method for processing. method = RequestMethod.GET ensures that only HTTP GET requests are processed by this method.
Line 2 - @PathVariable is a convenient way of stripping a value out of the URL (fund id in this case) and assigning it to a method parameter so that it can be used in the function.
Lines 6 to 9 - Some validation to ensure the fund id specified in the URL is valid. If it isn't valid we return an error response.
Lines 11 to 16 - this is simply a call to our dummy service to get fund information for specified fund.
Line 19 - Creates a ModelAndView object using the JSON view and our fund object. We defined a MappingJacksonJsonView in rest-services-config earlier. This is used to convert the Fund object to JSON before it is returned to the client.

Get all Funds - HTTP GET
1:       @RequestMapping(value = "/rest/funds/", method = RequestMethod.GET)  
2:       public ModelAndView getFunds() {  
3:            List<Fund> funds = null;  
4:    
5:            try {  
6:                 funds = fundService_i.getAllFunds();  
7:            } catch (Exception e) {  
8:                 String sMessage = "Error getting all funds. [%1$s]";  
9:                 return createErrorResponse(String.format(sMessage, e.toString()));  
10:            }  
11:    
12:            logger_c.debug("Returing Funds: " + funds.toString());  
13:            return new ModelAndView(jsonView_i, DATA_FIELD, funds);  
14:       }  
This method handles HTTP GET requests where a fund id is not specified in the URL. In this instance all available funds are returned.
Line 1 - RequestMapping annotation ensures that incoming HTTP requests to /rest/funds/ will be routed toward this method for processing. Note how it differs to the method above in that a specific fund id is not specified. method = RequestMethod.GET ensures that only HTTP GET requests are processed by this method. 
Lines 5 to 10 - this is simply a call to our dummy service to get all funds.
Line 13 - Creates a ModelAndView object using the JSON view and our list of fund objects. We defined a MappingJacksonJsonView in rest-services-config earlier. This is used to convert the Fund list to JSON before it is returned to the client.

Create a Fund - HTTP POST
1:       @RequestMapping(value = { "/rest/funds/" }, method = { RequestMethod.POST })  
2:       public ModelAndView createFund(@RequestBody Fund fund_p,  
3:                                      HttpServletResponse httpResponse_p, WebRequest request_p) {  
4:    
5:            Fund createdFund;  
6:            logger_c.debug("Creating Fund: " + fund_p.toString());  
7:    
8:            try {  
9:                 createdFund = fundService_i.createFund(fund_p);  
10:            } catch (Exception e) {  
11:                 String sMessage = "Error creating new fund. [%1$s]";  
12:                 return createErrorResponse(String.format(sMessage, e.toString()));  
13:            }  
14:    
15:            /* set HTTP response code */  
16:            httpResponse_p.setStatus(HttpStatus.CREATED.value());  
17:    
18:            /* set location of created resource */  
19:            httpResponse_p.setHeader("Location", request_p.getContextPath() + "/rest/funds/" + fund_p.getFundId());  
20:    
21:            /**  
22:             * Return the view  
23:             */  
24:            return new ModelAndView(jsonView_i, DATA_FIELD, createdFund);  
25:       }  

This method handles HTTP POST requests and is used to create a new fund entity.
Line 1 - RequestMapping annotation ensures that incoming HTTP requests to /rest/funds/ will be routed toward this method for processing. Note how it differs to the first two methods in that this method will process HTTP POST requests  as specified with method = RequestMethod.POST.
Line 2 - The @RequestBody annotation takes the contents of the HTTP request body and converts it to a Fund object. The HTTP request body must contain Fund data in the form of valid JSON data. You'll see how the request is set up on the client side later on. The HTTPResponse object is exposed by Spring MVC as a convenient way of setting values on the response - this is described below. Lines 8 to 13 - here we call our dummy service to to create a new fund. In our application nothing really happens here but in a real application this is where you'd probably persist the new Fund.
Line 16 - Here we set a HTTP header indicating a response status of CREATED. The client may then use this as confirmation that the request was successful and that the fund data posted to the server resulted in a new Fund entity being created.
Line 19 - We set a HTTP Location header that contains the URL to the newly created resource (our Fund). This is standard REST practice and enables the client to retrieve the created resource if required.
Line 24 - Creates a ModelAndView object using the JSON view and our newly created fund object. There are various opinions on what a POST request should actually return - in this instance I return the newly created Fund object. You might ask why you would want to return an object that the client just posted to the server? It is possible that the server manipulated the entity posted by the client in some way, perhaps added a server generated id. In this instance you'd want to return the newly created object so that the client has the latest representation of the resource. 

Update a Fund - HTTP PUT
1:       @RequestMapping(value = { "/rest/funds/{fundId}" }, method = { RequestMethod.PUT })  
2:       public ModelAndView updateFund(@RequestBody Fund fund_p, @PathVariable("fundId") String fundId_p,  
3:                                            HttpServletResponse httpResponse_p) {  
4:    
5:            logger_c.debug("Updating Fund: " + fund_p.toString());  
6:    
7:            /* validate fund Id parameter */  
8:            if (isEmpty(fundId_p) || fundId_p.length() < 5) {  
9:                 String sMessage = "Error updating fund - Invalid fund Id parameter";  
10:                 return createErrorResponse(sMessage);  
11:            }  
12:    
13:            Fund fund = null;  
14:    
15:            try {  
16:                 fund = fundService_i.updateFund(fund_p);  
17:            } catch (Exception e) {  
18:                 String sMessage = "Error updating fund. [%1$s]";  
19:                 return createErrorResponse(String.format(sMessage, e.toString()));  
20:            }  
21:    
22:            httpResponse_p.setStatus(HttpStatus.OK.value());  
23:            return new ModelAndView(jsonView_i, DATA_FIELD, fund);  
24:       }  

This method handles HTTP PUT requests and is typically used to update an existing resource, in our case an existing fund.
Line 1 - RequestMapping annotation ensures that incoming HTTP PUT requests to /rest/funds/ will be routed toward this method for processing. 
Line 2 and 3 - The @RequestBody annotation takes the contents of the HTTP request body and converts it to a Fund object. The HTTP request body must contain Fund data in the form of valid JSON data.  The HTTPResponse object is exposed by Spring MVC as a convenient way of setting values on the response - this is described below. The @PathVariable strips the fund id out of the URL and sets it as a parameter variable for convenience. Line 8 to 11 - Perform some simple validation to ensure that the fund id specified in the URL is valid.
Lines 15 to 20 - here we call the dummy service to update our fund. In our application nothing really happens here but in a real application this is where you'd probably update the fund in the database.
Line 22 - Here we set a HTTP header indicating a response status of OK. The client may then use this as confirmation that the request was successful and that the fund data sent to the server resulted in the specified fund being updated.
Line 23 - Creates a ModelAndView object using the JSON view and our updated fund object. There are various opinions on what a PUT request should actually return - in this instance I return the updated Fund object. The reasoning is the same as that explain for the POST request above - it is possible that the server manipulated the entity sent by the client in some way, perhaps added a server generated time stamp. In this instance you'd want to return the updated object so that the client has the latest representation of the resource. 

Delete a Fund - HTTP DELETE
1:       @RequestMapping(value = "/rest/funds/{fundId}", method = RequestMethod.DELETE)  
2:       public ModelAndView removeFund(@PathVariable("fundId") String fundId_p,  
3:                                            HttpServletResponse httpResponse_p) {  
4:    
5:            logger_c.debug("Deleting Fund Id: " + fundId_p.toString());  
6:    
7:            /* validate fund Id parameter */  
8:            if (isEmpty(fundId_p) || fundId_p.length() < 5) {  
9:                 String sMessage = "Error deleting fund - Invalid fund Id parameter";  
10:                 return createErrorResponse(sMessage);  
11:            }  
12:    
13:            try {  
14:                 fundService_i.deleteFund(fundId_p);  
15:            } catch (Exception e) {  
16:                 String sMessage = "Error invoking getFunds. [%1$s]";  
17:                 return createErrorResponse(String.format(sMessage, e.toString()));  
18:            }  
19:    
20:            httpResponse_p.setStatus(HttpStatus.OK.value());  
21:            return new ModelAndView(jsonView_i, DATA_FIELD, null);  
22:       }  

This method handles HTTP DELETE requests and is typically used to delete a resource, in our case a fund.
Line 1 - RequestMapping annotation ensures that incoming HTTP DELETE requests to /rest/funds/ will be routed toward this method for processing. 
Line 2 and 3 - The HTTPResponse object is exposed by Spring MVC as a convenient way of setting values on the response - this is described below. The @PathVariable strips the fund id out of the URL and sets it as a parameter variable for convenience.

Line 8 to 11 - Perform some simple validation to ensure that the fund id specified in the URL is valid.
Lines 13 to 18 - here we call our dummy service to delete the specified fund. In our application nothing really happens here but in a real application this is where you'd probably delete the fund from the database.
Line 20 - Here we set a HTTP header indicating a response status of OK. The client may then use this as confirmation that the request was successful and that the specified fund was deleted.
Line 23 - Creates a ModelAndView object using the JSON view that returns no data. The only thing the client is interested in, is know whether the request was processed successfully or not, and this will be apparent from the OK response status. 

Fund Service
Below is our fund service which exposes a number of methods that were called from the REST methods defined above. This is a dummy service and doesn't really do anything aside form return some hard coded fund information. In a real application, a service like this would use DAOs to perform  database operations corresponding to the requests being handled.  
1:  package com.blog.samples.services;  
2:    
3:  import java.util.ArrayList;  
4:  import java.util.Date;  
5:  import java.util.List;  
6:  import org.apache.log4j.Logger;  
7:  import org.springframework.stereotype.Service;  
8:    
9:  import com.blog.samples.domain.Fund;  
10:    
11:  /**  
12:   * The Class FundService.  
13:   */  
14:  @Service  
15:  public class FundService {  
16:    
17:       private static final Logger logger_c = Logger.getLogger(FundService.class);  
18:    
19:       /**  
20:        * Get the fund by id.  
21:        *  
22:        * @param fundId_p  
23:        *      the fund id_p  
24:        * @return the fund by id  
25:        */  
26:       public Fund getFundById(String fundId_p) {  
27:            Fund fund = new Fund();  
28:    
29:            fund.setFundId(fundId_p);  
30:            fund.setFundDescription("High Risk Equity Fund");  
31:            fund.setBidPrice(26.80);  
32:            fund.setOfferPrice(27.40);  
33:            fund.setLastUpdated(new Date());  
34:    
35:            return fund;  
36:       }  
37:    
38:       /**  
39:        * Gets all funds.  
40:        *  
41:        * @return the all funds  
42:        */  
43:       public List<Fund> getAllFunds() {  
44:            List<Fund> funds = new ArrayList<Fund>();  
45:    
46:            for (int i = 0; i < 10; i++) {  
47:                 Fund fund = new Fund();  
48:    
49:                 fund.setFundId("12345" + i);  
50:                 fund.setFundDescription("High Risk Equity Fund " + (i + 1));  
51:                 fund.setBidPrice(26.80 + (Math.random() * 10));  
52:                 fund.setOfferPrice(27.40 + (Math.random() * 10));  
53:                 fund.setLastUpdated(new Date());  
54:    
55:                 funds.add(fund);  
56:            }  
57:            return funds;  
58:       }  
59:    
60:       /**  
61:        * Creates the fund.  
62:        *  
63:        * @param fund_p  
64:        *      the fund_p  
65:        * @return the fund  
66:        */  
67:       public Fund createFund(Fund fund_p) {  
68:    
69:            logger_c.debug("Persisting fund in database: " + fund_p.toString());  
70:    
71:            /* set id and timestamp */  
72:            fund_p.setFundId("12345");  
73:            fund_p.setLastUpdated(new Date());  
74:    
75:            return fund_p;  
76:       }  
77:    
78:       /**  
79:        * Update fund.  
80:        *  
81:        * @param fund_p  
82:        *      the fund_p  
83:        * @return the fund  
84:        */  
85:       public Fund updateFund(Fund fund_p) {  
86:    
87:            logger_c.debug("Updating fund in database: " + fund_p.toString());  
88:    
89:            /* set timestamp */  
90:            fund_p.setLastUpdated(new Date());  
91:    
92:            return fund_p;  
93:       }  
94:    
95:       /**  
96:        * Delete fund.  
97:        *  
98:        * @param fundId_p  
99:        *      the fund id_p  
100:        */  
101:       public void deleteFund(String fundId_p) {  
102:            logger_c.debug("Deleting fund from database: " + fundId_p.toString());  
103:       }  
104:  }  

Running the Application
Our application is complete so we can now build and deploy. I've run this application on Tomcat but you can run it in any Servlet container. If you don't want to build the application step by step as described in this tutorial you can simply download the complete project (attached to this post), build the WAR file and drop it onto your server.

Testing the Application
There are a number of frameworks available that allow you to quickly and easily build RESTful clients. To keep this tutorial simple I've opted to test the application using a REST plugin for Chrome. REST plugins are available for most browsers so if you don't want to use Chrome you don't have to. I chose the Advanced REST Client plugin for chrome which can be downloaded here (https://chrome.google.com/webstore/detail/hgmloofddffdnphfgcellkdfbfbjeloo). Once installed the plugin can be used to test each of the RESTful endpoints we defined earlier.

Test GET fund
We'll start off by getting a specified fund. Figure 2.0 below shows us sending a GET request to http://localhost:8080/rest-sample/rest/funds for fund id 12345.  The HTTP response code (200 OK) and the JSON response returned from the server are shown below the request.  
Figure 2.0 - Testing HTTP GET
Test GET funds
Figure 3.0 below shows us sending a GET request to http://localhost:8080/rest-sample/rest/funds. This time we don't specify a fund id so the request is handled by the getFunds method and returns all funds to the client.  The HTTP response code (200 OK) and the JSON response returned from the server are shown below the request. Note that the JSON response now contains a list of funds.
Figure 3.0 - Testing HTTP GET
Test POST fund
Figure 4.0 below shows us sending a POST request to http://localhost:8080/rest-sample/rest/funds. This time we populate the HTTP body with JSON describing the fund we want to create. The HTTP response code (200 OK) and the JSON response returned from the server is shown below the request.
Figure 4.0 - Testing HTTP POST
Test PUT fund
Figure 5.0 below shows us sending a PUT request to http://localhost:8080/rest-sample/rest/funds. This time we populate the HTTP body with JSON describing the fund we want to update and identify the fund by putting the fund id in the URL.  The HTTP response code (200 OK) and the JSON response returned from the server are shown below the request.  
Figure 5.0 - Testing HTTP PUT
Test DELETE fund
Figure 6.0 below shows us sending a DELETE request to http://localhost:8080/rest-sample/rest/funds. We identify the fund we want to delete by putting the fund id in the URL.  The HTTP response code (200 OK) tells us that the operation succeeded.
Figure 6.0 - Testing HTTP DELETE
Summary
This tutorial has shown you how to build a simple set of RESTful services using the Spring framework. While the examples are trivial they should provide enough detail to get you up and running. Don't forget you can download the attached code and modify it as you see fit. Enjoy!

Update
I've just uploaded the full source code for this tutorial - you can grab it here https://docs.google.com/folder/d/0B_SZOyniHfc1dlJDS0Z3SWJwYTg/edit