http://www.developer.com/

Back to article

Get RESTful with Spring 3.0


December 23, 2009

Service oriented architectures and simple service-based mashups have become very popular in the past few years as businesses became more agile and Web 2.0 sites began to integrate more data from more places. Frameworks such as Axis and Apache CFX, as well as Java standards such as JAX-WS and JAX-RS, have sprung up to support these types of applications by exposing services through various lightweight client/server protocols. In August of 2007, the Spring community added Spring Web Services (Spring-WS) 1.0 to the mix, which added Simple Object Access Protocol (SOAP)-based web service integrations to Spring Framework applications.

Fast forward to today, and Web 2.0 mashups are more popular than ever, increasing the demand for easier ways to integrate web applications with dynamic backend data. All of the specifications and promises of SOAP-based web services haven't fully panned out, and developers are looking for better ways to consume services in JavaScript- and Flash-based applications through the browser-supported XmlHttpRequest object.

Unfortunately, your organization more than likely spent the past few years building out a SOAP API that you don't want to (or can't afford to) abandon. This article walks through the implementation of a REST-like API (see Sidebar 1. 'REST-like' vs. REST Service) that fully leverages your existing SOAP-based services using the new features of Spring 3.0 in order to get a more Ajax-friendly API quickly. You should have a basic understanding of Spring-WS and Spring's MVC pattern, as well as the Java API for XML Binding (JAXB), before continuing. However, in most cases, you probably already have these technologies deployed in your system.

Background for the Example

SOAP is an XML-based protocol normally based on HTTP as the transport mechanism. SOAP web services can be very powerful when combined with good language support, as you find in Java (through various third-party libraries) and .NET. However, if a language doesn't provide support, exposing or consuming a SOAP service can be a slow, painful, and error-prone experience for the developer. This is commonly the case with scripting languages such as JavaScript and Python. While you can do it, it isn't an ideal approach.

As a good web services developer, you most likely defined your existing SOAP services using XML schema and the web service definition language (WSDL). This contract-first approach can be very valuable in clearly defining your API and providing documentation to end users. This article will show you how to use these API definitions to expose a REST-like API that is easier to consume from a JavaScript application, while not abandoning the benefits offered by the XML schema and WSDL. Figure 1 shows the final set of APIs that will expose the demo business service.


Figure 1. Final Set of APIs that Will Expose the Demo Business Service:
These are the APIs that will expose the internal weather service business logic to clients. The same XML Schema is used to define the messages to all three APIs.

The example in this article will use only the HTTP POST operation for requests. It will also use the existing WSDL-defined operation names rather than introduce new resource names. To make the services easier for clients to consume, the SOAP API will be complimented with both the JavaScript object notation (JSON) and simple XML APIs.

That's enough background; it's time to write some services.

The Existing Web Service Endpoint

To begin, you need an existing SOAP-based web service that you will ultimately expose as a REST-like service that supports both the JSON and basic XML formats. With Spring-WS, the demo weather service would be defined in a WSDL with a supporting XML schema as in Listing 1 and Listing 2. You can see that the service supports three operations:
  • convertTemperature
  • saveWeatherUpdate
  • getWeatherReport

Listing 3 shows how the web service operations are then implemented in an endpoint, which uses the Spring-WS annotations @EndPoint and @PayloadRoot to connect the WSDL messages to specific operations. The parameter objects to each implemented operation are generated using the XJC command of JAXB from the XML Schema of domain objects for this service, which allows Spring-WS to automatically unmarshal the SOAP request into a Java object and pass it to the endpoint's operation. Automatic marshalling and unmarshalling greatly reduce the amount of code you need to write and maintain.

The web service endpoint delegates the actual business logic of each operation out to the common business tier WeatherService shown in Listing 4. If your application design doesn't cleanly separate the web service tier from the business tier, you may need to do a little refactoring before trying to support an additional API.

Finally, you tie all of these pieces together through the web.xml servlet definition in Listing 5 and the WeatherSoapService-servlet.xml Spring bean configuration file in Listing 6.

Fully defining and explaining Spring-WS and Spring MVC is beyond the scope of this article. If you're confused at this point, you may need to brush up on those frameworks first and then come back. The focus now is to take this existing, three-operation service and make it available as a REST-like service that supports JSON and simple XML messages.

The Simple XML Endpoint

The first logical step in the transition from SOAP web services to REST-like services is to introduce a simple XML endpoint that can leverage the same XML messages and domain objects defined for the web services, but without the SOAP envelope, fault complexity, or WSDL. A new MVC annotation in Spring 3.0, @RequestBody, allows a controller (the C in MVC) to receive the body of a request as a parameter. The best part is that Spring will perform the same JAXB unmarshalling you saw previously with Spring-WS, making it simple to reuse the same domain objects generated from the web service's XML schema.

To use the new annotations, introduce a WeatherRestEndpoint and annotate it with the @Controller annotation, which tells Spring MVC that the DispatcherServlet should use this class as a controller to handle requests. You will implement the same three methods implemented in the web service endpoint in this endpoint to support the three API methods discussed previously. However, this time, you will map each method to a specific request URI using the @RequestMapping annotation. To keep things simple and consistent, the requests map to a URI with the same name as the original web service operation name, while in a pure REST endpoint, the mapping may point to a specific resource. For example, you map the convertTemperature method with this annotation:

@RequestMapping(value = "/convertTemperature", method = RequestMethod.POST)
public ModelAndView convertTemperature(@RequestBody ConvertTemperature request)
{	
 ...
}

This code listing also demonstrates how you use the new @RequestBody annotation to get the actual content of the request. Again, using reflection and JAXB, Spring can automatically unmarshal the request XML into the generated domain object. Listing 7 shows the complete WeatherRestEndpoint, which again delegates to the same WeatherService business-tier object to perform all the business logic. Figure 2 shows the implemented flow at this point.


Figure 2. The Message Flow for an XML Message Through Spring, JAXB, and the Service Endpoint:
The endpoint simply delegates all requests to the common business logic backend services.

Now, the endpoint needs to return the business service response value to the caller in an XML response message. To accomplish this, you use a MarshallingView (the view in MVC), which delegates to JAXB to marshal the response object (the model in MVC) into XML and returns it to the caller. The MarshallingView is another Spring 3.0 feature that uses the new Spring object to XML mapping (OXM) support to simply invoke JAXB and makes for one fewer class the developer needs to worry about.

Now that the implementation of the endpoint is complete, everything needs to be linked together in the web.xml servlet definition and the WeatherRestXmlService-servlet.xml Spring bean configuration file. The servlet definition uses the classic Spring MVC DispatcherServlet, so there isn't much new to discuss (see Listing 8). The REST-like functionality comes mostly from the configuration of the new Spring 3.0 MarshallingHttpMessageConverter as it is added to the AnnotationMethodHandlerAdapter to perform the JAXB unmarshalling of HTTP requests, as well as the MarshallingView to marshal the HTTP responses.

<!--
    Maps incoming requests to the appropriate controller based on the annotations on
    the controller methods.
  -->
  <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
  <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
      <list>
        <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
          <property name="unmarshaller" ref="restXmlMarshaller"/>
        </bean>
      </list>
    </property>
  </bean>
  
  <!--
    View for rendering the XML for result objects using JAXB annotations.
  -->
  <bean id="restXmlMarshalView" class="org.springframework.web.servlet.view.xml.MarshallingView">
    <property name="marshaller" ref="restXmlMarshaller"/>
  </bean>

You can see the full Spring servlet bean configuration in Listing 9. To test the new functionally, deploy the application and post an XML message to the configured endpoint using a testing tool such as SoapUI or through a test case with the new Spring 3.0 RestTemplate:

RestTemplate restOps = new RestTemplate();
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.parseMediaType("application/xml"));
stringConverter.setSupportedMediaTypes(mediaTypes);
restOps
	.setMessageConverters(new HttpMessageConverter[] { stringConverter });

String xmlRequest = "<weat:ConvertTemperature xmlns:weat=\"http://mpilone.org/weather\">\r\n"
	+ "   <weat:temperature>38</weat:temperature>\r\n"
	+ "   <weat:targetScale>celcius</weat:targetScale>\r\n"
	+ "</weat:ConvertTemperature>";

String xmlResponse = restOps.postForObject(
	"http://localhost:8080/weather/xml/convertTemperature", xmlRequest,
	String.class);
System.out.println(xmlResponse);

The JSON Endpoint

While simpler than the full SOAP web service implementation, the simple XML endpoint still forces clients to produce and consume XML messages, which aren't always easy to processes in some web 2.0 languages like JavaScript. To make things even easier, you can introduce another endpoint that uses the REST-like HTTP POST pattern introduced with the simple XML endpoint, but have the endpoint consume and produce the JSON, making it very Ajax friendly. Keeping with the design goals for these new APIs, the JSON endpoint should use the same domain objects described in the web service XML schema, which requires the use of the Jackson JSON Processor (see Sidebar 2. What's the Jackson JSON Processor?).

The flexible Spring MVC design allows you to reuse the exact same WeatherRestEndpoint that you implemented to support the simple XML endpoint. If you think about the process described with the XML endpoint, you can see that the only part of the message flow that needs to change is the marshalling of JSON into the XML schema-defined domain objects and the marshalling of the response objects back into JSON. Therefore, you can reuse the implementation of the endpoint and configure it as another controller accessed by the DispatcherServlet defined in the web.xml servlet definition (see Listing 10).

Again, the Spring bean configuration file needs to link the request unmarshaller, the controller, and the view. The addition of JSON support comes mostly from the configuration of the new Spring 3.0 MappingJacksonHttpMessageConverter, as it is added to the AnnotationMethodHandlerAdapter to perform the Jackson unmarshalling of HTTP requests, as well as the MappingJacksonView to marshal the HTTP responses:

<!--
    Maps incoming requests to the appropriate controller based on the annotations on
    the controller methods.
  -->
  <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
  <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
      <list>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
          <property name="objectMapper" ref="jacksonObjectMapper"/>
        </bean>
      </list>
    </property>
  </bean>
  
  <!-- The Jackson view that uses the Jackson object mapper for serialization. -->
  <bean id="restJsonMarshalView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
    <property name="objectMapper" ref="jacksonObjectMapper"/>
    <property name="renderedAttributes">
      <list>
        <value>response</value>
      </list>
    </property>
  </bean>

You can see how similar the definition of the Jackson converter and view is to the XML converter and view, which brings a simple consistency to the implementation and deployment. However, unlike the XML endpoint, Spring's OXM functionality is replaced with Jackson's ObjectMapper and the built-in JaxBAnnotationIntrospector:

<!-- Jackson ObjectMapper that can serialize and deserialize to and from JSON.
     The ObjectMapper is similar to JAXB marshallers. -->
<bean class="org.mpilone.spring.web.rest.MapUnwrappingObjectMapper" id="jacksonObjectMapper"/>

Jackson uses the JAXB introspector, configured as part of the custom object mapper to be discussed later, to identify fields of JAXB-generated objects to be marshaled and unmarshalled, allowing Jackson to bridge from JSON- to JAXB-annotated Java objects. Listing 11 contains the full Spring configuration file for the Jackson endpoint.

You must handle two special cases explicitly with the Jackson endpoints:

  1. The MappingJacksonView allows you to specify which properties in Spring's model map to serialize (in this case, only the "response" object). However, Jackson serializes the map itself, which can introduce an undesirable top-level map object in the response. To avoid this, a custom implementation of the Jackson ObjectMapper was implemented to "unwrap" single item model maps and to configure the JAXB annotation introspector (see Listing 12).
  2. Jackson currently does not support serializing an empty object (i.e., an object with no properties) through the default BeanSerializer. Therefore, if your current XML response objects are empty (as is commonly the case with a "void" SOAP operation), you must implement a custom serializer to simply generate an empty JSON object (e.g., "{ }"). The Jackson developers have indicated that this feature may be supported natively in the future.

To test the new functionally, deploy the application and post an XML message to the configured endpoint using a testing tool such as SoapUI or through a test case using the new Spring 3.0 RestTemplate:

RestTemplate restOps = new RestTemplate();
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaType.parseMediaType("application/json"));
stringConverter.setSupportedMediaTypes(mediaTypes);
restOps
	.setMessageConverters(new HttpMessageConverter[] { stringConverter });

String jsonRequest = "{ \"temperature\": 38, \"targetScale\": \"celcius\" }";
String jsonResponse = restOps.postForObject(
	"http://localhost:8080/weather/json/convertTemperature", jsonRequest,
	String.class);
System.out.println(jsonResponse);

HTTP Client Example

The full source code for this article includes a simple HTTP and JavaScript example. The example simply uses the XmlHttpRequest object that is built into the browser to allow you to post a message payload to any of the endpoints implemented here. You can use the JavaScript as a launching pad to integrate real Ajax support into your web applications using the new REST-like APIs you just added. You also can deploy the services and client demos directly in the Tomcat container as a web application archive (WAR).

Quick REST Services Implementations

Spring 3.0 brings many new simple yet powerful features to the Spring MVC framework. These features allow for the quick implementation of REST services. In this article, you saw how to leverage your existing service API (defined with XML schema and WSDL) to expose a SOAP-based web service as a REST-like service. The REST-like service supports both XML and JSON, making it more flexible and easier for clients to use, while maintaining consistency across all three APIs. You also can use Spring's ContentNegotiatingViewResolver to select the appropriate response format based on the HTTP Accept header, which would allow a single endpoint to serve multiple formats—beyond just XML and JSON—to meet clients' needs.

Only time will tell which service approach wins out, but you can rest assured that with the proper tools, your application will be agile enough to meet the demands of client developers. At the same time, it will successfully reduce time and costs by reusing existing API definitions and infrastructure, as well as leveraging common backend business logic.

The Spring Framework suite, as well as great third-party libraries such as JAXB and Jackson, make Java all the more powerful in a fast moving Web 2.0 world.

Code Download

  • Spring-WS-Rest_src
  • For Further Reading

  • Apache CXF
  • Apache Axis2
  • Spring Web Services
  • Jackson JSON Processor
  • JAXB
  • Spring Framework
  • SoapUI
  • About the Author

    Michael Pilone is a senior software engineer for Vangent, Inc., where he is currently working on Java based web service technologies and large scale geographical data indexing and searching. Michael holds a master's degree in Information Technology Project Management.

    Sitemap | Contact Us

    Thanks for your registration, follow us on our social networks to keep up-to-date