Wednesday, February 20, 2013

Accessing Restful services. HTTP Message converters

Registered converters

When accessing to Restful services, the Spring class RestTemplate maintains a list of message converters that will use to marshal objects into the request body, or unmarshalling them from the response. When instantiating the RestTemplate class, it automatically fills a list with several converters:
  • ByteArrayHttpMessageConverter
  • StringHttpMessageConverter
  • ResourceHttpMessageConverter
  • SourceHttpMessageConverter
  • AllEncompassingFormHttpMessageConverter

The RestTemplate class may register additional converters if it finds the required classes on the classpath:
  •  JAXB converter: Converts xml using JAXB2. The Binder class must be found on the classpath. If you are using Java 6, it is no longer necessary as it already comes with the JDK. If you are using Java 5, add the following dependencies:
<dependency>
             <groupId>javax.xml.bind</groupId>
             <artifactId>jaxb-api</artifactId>
             <version>2.2</version>
         </dependency>
         <dependency>
             <groupId>com.sun.xml.bind</groupId>
             <artifactId>jaxb-impl</artifactId>
             <version>2.2</version>
         </dependency>

  • Jackson converter: Converts Objects from/to JSON. ObjectMapper and JsonGenerator must exist on the classpath.
<dependency>
                  <groupId>org.codehaus.jackson</groupId>
                  <artifactId>jackson-mapper-asl</artifactId>
                  <version>1.4.2</version>
            </dependency>

  •  Atom and RSS feeds converters. The WireFeed class must be present on the classpath.


Serving different content

The following controller will execute three different operations that will generate xml, String and json content:

@RequestMapping(value="/users/{userId}", method=RequestMethod.GET)
public @ResponseBody User getUser(@PathVariable("userId") long id) {
       return userRepository.getUser(id);
}

Result: JSON. The User class can be serialized/deserialized by the Jackson object mapper.
                


@RequestMapping(value="/usernames/{userId}", method=RequestMethod.GET)
public @ResponseBody String getUsername(@PathVariable("userId") long id) {
       StringBuilder result = new StringBuilder();
       User user = userRepository.getUser(id);
       return result.append(user.getName()).append(" ").append(user.getSurname()).toString();
}

Result: String. The returned object is a String.
               

@RequestMapping(value="/cars/{carId}", method=RequestMethod.GET)
public @ResponseBody Car getCar(@PathVariable("carId") long id) {
       return carRepository.getCar(id);
}

Result: XML. The Car class is annotated with @XmlRootElement
               

You can also specify the converters that will be used by the RestTemplate. You could, for example, only instantiate the needed converters or register your own message converter:

private RestTemplate restTemplate = new RestTemplate();

@Before
public void setup() {
List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
       converters.add(new StringHttpMessageConverter());
       converters.add(new Jaxb2RootElementHttpMessageConverter());
       converters.add(new MappingJacksonHttpMessageConverter());
       restTemplate.setMessageConverters(converters);
}

Just take into account that if you serve both json and xml content, you must add the jaxb2 converter before the json converter. Otherwise, regardless of the presence of xml annotations, the xml method response will be handled by the jackson converter and be converted to json. That happens because the HttpEntityRequestCallback inner class of the RestTemplate will use the first converter that can write/read the content.

You can get the source code from Github.

Monday, February 11, 2013

Spring property-placeholder: External properties configuration

The PopertyPlaceholderConfigurer

A common way of setting the configuration of the web application is by defining it in a properties file which is located in the classpath. The way of doing this is using the PropertyPlaceholderConfigurer class, or simplifying the configuration, using the context namespace.

That's ok but, what if you need a different configuration for different environments? Or, once the web application is deployed on the server, what happens if we need to change some value? What we will have to do is modify the application, redeploy the war file and restart the server. If there's only one application deployed it's not much of a problem, but imagine a properties file which contains generic configuration that is shared by all the applications on the server.


Externalizing the properties file

By putting the properties file in the server file system, you will have your configuration outside the web application. In case you need to change a value, you will only need to modify the file and restart the server, without needing to redeploy the application.

Let's assume that we put our properties file in the "D:\environment\config\" folder. What we need to do is add a new argument to the server start up script. If you use an IDE, for example Eclipse, double click the server name (servers tab) and click on the Open launch configuration link:


Then, select the arguments tab and add a new argument with the properties path as shown below:


Once done this, the Spring configuration with the property placeholder is as follows:

Tuesday, February 5, 2013

Create and test REST services with Spring MVC



Introduction

 The first part of this example shows how to create RESTful web services using Spring MVC. The controller contains CRUD operations on warehouses and its products. For this example, the repository is a stub that simulates access to the database.

The second part will access these services using the RestTemplate class and test them.

Source code available at github.


Configuration

 The context configuration is quite simple. It is split in two xml files. The parent context:

<?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: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/context
             http://www.springframework.org/schema/context/spring-context-3.0.xsd
             http://www.springframework.org/schema/mvc
             http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
 
       <!-- Detects annotations like @Component, @Service, @Controller... -->
       <context:component-scan base-package="xpadro.tutorial.rest"/>
      
       <!-- Detects MVC annotations like @RequestMapping -->
       <mvc:annotation-driven/>
</beans>

And the servlet context, which contains the stub repository:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
             http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
      
       <!-- The warehouse repository. Simulates the retrieval of data from the database -->
       <bean id="warehouseRepository" class="xpadro.tutorial.rest.repository.WarehouseRepositoryImpl"/>
</beans>

The web.xml file just contains basic Spring configuration:

<?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>SpringRestTest</display-name>
 
  <!-- Root context configuration -->
  <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:xpadro/tutorial/rest/configuration/root-context.xml</param-value>
  </context-param>
 
  <!-- Loads Spring root context, which will be the parent context -->
  <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
 
  <!-- Spring servlet -->
  <servlet>
       <servlet-name>springServlet</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath:xpadro/tutorial/rest/configuration/app-context.xml</param-value>
       </init-param>
  </servlet>
  <servlet-mapping>
       <servlet-name>springServlet</servlet-name>
       <url-pattern>/spring/*</url-pattern>
  </servlet-mapping>
</web-app>

And finally the pom.xml with all the dependencies, which can be found here.


Creating the RESTful services

 The controller has the following methods:

getWarehouse: Returns an existing warehouse.

@RequestMapping(value="/warehouses/{warehouseId}", method=RequestMethod.GET)
public @ResponseBody Warehouse getWarehouse(@PathVariable("warehouseId") int id) {
     return warehouseRepository.getWarehouse(id);
}

This method uses several MVC annotations, explained below:

  • @RequestMapping: This annotation maps requests based on method onto specific handlers, in this case, the getWarehouse method, but only if the HTTP request method is GET. Specifying the method, you can have multiple methods mapped to the same uri. For example, the following request will be handled by this method and return the warehouse identified by 1:

                http://localhost:8080/myApp/spring/warehouses/1

  • @PathVariable: Extract values from request URL. In the method above, it extracts the warehouseId value from the request URL and maps it to the id parameter.
  • @ResponseBody: Bounds the return value of the method to the response body. For this task it uses HTTP message converters. The function of these converters is to convert between HTTP request/response and object.


addProduct: Adds a new product to an existing warehouse.

@RequestMapping(value="/warehouses/{warehouseId}/products", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void addProduct(@PathVariable("warehouseId") int warehouseId, @RequestBody Product product, HttpServletRequest request, HttpServletResponse response) {
            
     warehouseRepository.addProduct(warehouseId, product);
     response.setHeader("Location", request.getRequestURL().append("/")
          .append(product.getId()).toString());
}


  • With the @ResponseStatus annotation, we are defining that there won’t be a view returned. Instead, we will return a response with an empty body.
  • Like @ResponseBody annotation, the @RequestBody annotation uses converters to transform request data into the object passed as a parameter.

 Other methods are defined in this controller but won’t put them all here. You can look up the source code linked above.


Setting the exception handler

 You can have multiple exception handlers, each one mapped to one or more exception types. Using the @ExceptionHandler annotation allows you to handle exceptions raised by methods annotated with @RequestMapping. Instead of forwarding to a view, it allows you to set a response status code. For example:

@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler({ProductNotFoundException.class})
public void handleProductNotFound(ProductNotFoundException pe) {
     logger.warn("Product not found. Code: "+pe.getMessage());
}


 Testing the services

 The test class is as follows:
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
       "classpath:xpadro/tutorial/rest/configuration/root-context.xml",
       "classpath:xpadro/tutorial/rest/configuration/app-context.xml"})
public class WarehouseTesting {
     private static final int WAREHOUSE_ID = 1;
     private static final int PRODUCT_ID = 4;
      
     private RestTemplate restTemplate = new RestTemplate();

     /**
      * Tests accessing to an existing warehouse
      */
     @Test
     public void getWarehouse() {
          String uri = "http://localhost:8081/rest_test/spring/warehouses/{warehouseId}";
          Warehouse warehouse = restTemplate.getForObject(uri, Warehouse.class, WAREHOUSE_ID);
          assertNotNull(warehouse);
          assertEquals("WAR_BCN_004", warehouse.getName());
     }
      
     /**
      * Tests the addition of a new product to an existing warehouse.
      */
     @Test
     public void addProduct() {
          //Adds the new product
          String uri = "http://localhost:8081/rest_test/spring/warehouses/{warehouseId}/products";
          Product product = new Product(PRODUCT_ID, "PROD_999");
          URI newProductLocation = restTemplate.postForLocation(uri, product, WAREHOUSE_ID);
            
          //Checks we can access to the created product
          Product createdProduct = restTemplate.getForObject(newProductLocation, Product.class);
          assertEquals(product, createdProduct);
          assertNotNull(createdProduct.getId());
     }

     /**
      * Tests the removal of an existing product
      */
     @Test
     public void removeProduct() {
          String uri = "http://localhost:8081/rest_test/spring/warehouses/{warehouseId}/products/{productId}";
          restTemplate.delete(uri, WAREHOUSE_ID, PRODUCT_ID);
            
          try {
               restTemplate.getForObject(uri, Product.class, WAREHOUSE_ID, PRODUCT_ID);
               throw new AssertionError("Should have returned an 404 error code");
          } catch (HttpClientErrorException e) {
               assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode());
          }
     }
}