Monday, February 28, 2011

Spring 3: REST Web Service Provider and Client Tutorial (Part 2)

In this tutorial we'll create a Spring MVC REST client that uses Spring 3's RestTemplate. Our client is able to communicate via XML and JSON. It also has basic support for retrieving images. This tutorial is Part 2 of our Spring 3: REST Web Service Provider and Client Tutorial series. If you haven't read Part 1, please read it first (See here).

What is REST in Spring 3?
For a great introduction, please see the following links:
- REST in Spring 3: @MVC
- REST in Spring 3: RestTemplate

What is RestTemplate?
The central class for client-side HTTP access. It simplifies communication with HTTP servers, and enforces RESTful principles. It handles HTTP connections, leaving application code to provide URLs (with possible template variables) and extract results.

Source: Spring 3 Reference: RestTemplate

Development

Our client application is unique because it doesn't need a Service layer. We only need the Domain and the Controller layers. We also need a couple of JSP pages to display the records.

Domain Layer

We'll be declaring two domain objects: Person and PersonList. These are the same class declarations we had in the REST provider application.

Person.java
package org.krams.tutorial.domain;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="person")
public class Person {

 private Long id;
 private String firstName;
 private String lastName;
 private Double money;
 
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public Double getMoney() {
  return money;
 }
 public void setMoney(Double money) {
  this.money = money;
 }
}
Person is a simple POJO consisting of three properties. Notice we've annotated the class name with @XmlRootElement(name="person"). Its purpose is to help JAXB (the one responsible for marshalling/unmarshalling to XML) determine the root of our POJO.

PersonList.java
package org.krams.tutorial.domain;

import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="persons")
public class PersonList {

 @XmlElement(required = true)
 public List<person> data;

 @XmlElement(required = false)
 public List<person> getData() {
  return data;
 }

 public void setData(List<person> data) {
  this.data = data;
 }
}
PersonList is another simple POJO. It's purpose is to wrap a list of Person objects.

Ideally we should be able to return a List instead of a PersonList. However, JAXB has difficulties processing java.util.List. A simple Google search verifies this unwanted behavior, for example, http://stackoverflow.com/questions/298733/java-util-list-is-an-interface-and-jaxb-cant-handle-interfaces

To resolve this issue with JAXB, we wrap our List with another object. To learn more about JAXB please visit the following link: http://download.oracle.com/javaee/5/tutorial/doc/bnazf.html

Controller Layer

The controller layer is the most important section of the client application because here's where we communicate with the REST provider. It's also responsible for displaying the JSP pages.

ProviderController.java
package org.krams.tutorial.controller;

import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.krams.tutorial.domain.Person;
import org.krams.tutorial.domain.PersonList;
import org.krams.tutorial.util.Writer;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;

/**
 * REST service client
 */
@Controller
public class RestClientController {

 protected static Logger logger = Logger.getLogger("controller");
 
 private RestTemplate restTemplate = new RestTemplate();
 
 /**
  * Retrieves an image from the REST provider
  * and writes the response to an output stream.
  */
 @RequestMapping(value = "/getphoto", method = RequestMethod.GET)
 public void getPhoto(@RequestParam("id") Long id, HttpServletResponse response) {
  logger.debug("Retrieve photo with id: " + id);
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.IMAGE_JPEG);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<String> entity = new HttpEntity<String>(headers); 
  
  // Send the request as GET
  ResponseEntity<byte[]> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, byte[].class, id);
  
  // Display the image
  Writer.write(response, result.getBody());
 }
 
 /**
  * Retrieves all records from the REST provider
  * and displays the records in a JSP page
  */
 @RequestMapping(value = "/getall", method = RequestMethod.GET)
 public String getAll(Model model) {
  logger.debug("Retrieve all persons");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<PersonList> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/persons", HttpMethod.GET, entity, PersonList.class);
   // Add to model
   model.addAttribute("persons", result.getBody().getData());
   
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/personspage.jsp
  return "personspage";
 }
 
 /**
  * Retrieves a single record from the REST provider
  * and displays the result in a JSP page
  */
 @RequestMapping(value = "/get", method = RequestMethod.GET)
 public String getPerson(@RequestParam("id") Long id, Model model) {
  logger.debug("Retrieve person with id: " + id);
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id);
   // Add to model
   model.addAttribute("person", result.getBody());
   
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/getpage.jsp
  return "getpage";
 }
 
 /**
     * Retrieves the JSP add page
     */
 @RequestMapping(value = "/add", method = RequestMethod.GET)
 public String getAddPage(Model model) {
  logger.debug("Received request to show add page");

  // Create new Person and add to model
  // This is the formBackingOBject
  model.addAttribute("personAttribute", new Person());

  // This will resolve to /WEB-INF/jsp/addpage.jsp
  return "addpage";
 }
 
 /**
  * Sends a new record to the REST provider
  * based on the information submitted from the JSP add page.
  */
 @RequestMapping(value = "/add", method = RequestMethod.POST)
 public String addPerson(@ModelAttribute("personAttribute") Person person,
       Model model) {
  logger.debug("Add new person");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);

  // Send the request as POST
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person", HttpMethod.POST, entity, Person.class);
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
 }
 
 /**
     * Retrieves the JSP update page
     */
    @RequestMapping(value = "/update", method = RequestMethod.GET)
    public String getUpdatePage(@RequestParam(value="id", required=true) Integer id,  
              Model model) {
     logger.debug("Received request to show edit page");
    
     // Retrieve existing Person and add to model
     // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id);
   // Add to model
   model.addAttribute("personAttribute", result.getBody());
   
  } catch (Exception e) {
   logger.error(e);
  }
     
     // This will resolve to /WEB-INF/jsp/updatepage.jsp
     return "updatepage";
 }
 
 /**
  * Sends an update request to the REST provider
  * based on the information submitted from the JSP update page.
  */
    @RequestMapping(value = "/update", method = RequestMethod.POST)
 public String updatePerson(@ModelAttribute("personAttribute") Person person,
      @RequestParam(value="id",  required=true) Long id,
      Model model) {
  logger.debug("Update existing person");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);
  
  // Send the request as PUT
  ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.PUT, entity, String.class, id);

  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
    }
    
    /**
  * Sends a delete request to the REST provider
  */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
 public String deletePerson(@RequestParam("id") Long id,
          Model model) {
     logger.debug("Delete existing person");
     
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
     HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<String> entity = new HttpEntity<String>(headers); 
  
  // Send the request as DELETE
  ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.DELETE, entity, String.class, id);
  
  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
    }
}
Notice our controller is our typical Spring MVC 3 controller.

Let's dissect our controller and analyze each methods.

getPhoto() method

The getPhoto() method retrieves an image as a byte array from the REST provider. It displays the image by writing it to the output stream using a helper class: Writer
/**
  * Retrieves an image from the REST provider
  * and writes the response to an output stream.
  */
 @RequestMapping(value = "/getphoto", method = RequestMethod.GET)
 public void getPhoto(@RequestParam("id") Long id, HttpServletResponse response) {
  logger.debug("Retrieve photo with id: " + id);
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.IMAGE_JPEG);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<String> entity = new HttpEntity<String>(headers); 
  
  // Send the request as GET
  ResponseEntity<byte[]> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, byte[].class, id);
  
  // Display the image
  Writer.write(response, result.getBody());
 }

Writer.java
package org.krams.tutorial.util;

import java.io.ByteArrayOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;

/**
 * Writes the report to the output stream
 * 
 * @author Krams at {@link http://krams915@blogspot.com}
 */
public class Writer {

 private static Logger logger = Logger.getLogger("service");
 /**
  * Writes the report to the output stream
  */
 public static void write(HttpServletResponse response, ByteArrayOutputStream bao) {
  
  logger.debug("Writing report to the stream");
  try {
   // Retrieve the output stream
   ServletOutputStream outputStream = response.getOutputStream();
   // Write to the output stream
   bao.writeTo(outputStream);
   // Flush the stream
   outputStream.flush();
   // Close the stream
   outputStream.close();

  } catch (Exception e) {
   logger.error("Unable to write report to the output stream");
  }
 }
 
 /**
  * Writes the report to the output stream
  */
 public static void write(HttpServletResponse response, byte[] byteArray) {
  
  logger.debug("Writing report to the stream");
  try {
   // Retrieve the output stream
   ServletOutputStream outputStream = response.getOutputStream();
   // Write to the output stream
   outputStream.write(byteArray);
   // Flush the stream
   outputStream.flush();
   // Close the stream
   outputStream.close();
   
  } catch (Exception e) {
   logger.error("Unable to write report to the output stream");
  }
 } 
}

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/getphoto?id=1
Notice the port number is 8081. That's because I'm using two versions of Tomcat. For the REST provider, I use port 8080. For the REST client, I use port 8081. Depending on how you setup your server, please modify the URL accordingly.

Running the URL displays the following image:

I think you know who this person is (and it just so happen this is the photo I have in my desktop). Notice whatever id you use the application will show the same image. That's because the provider uses a fix image from the classpath. Anyway the point of this exercise is how to retrieve a byte array using RestTemplate.

getAll() method

The getAll() method sends a request to retrieve all records from the REST provider. Once the result has arrived, it is added to the model to be displayed in a JSP page.

/**
  * Retrieves all records from the REST provider
  * and displays the records in a JSP page
  */
 @RequestMapping(value = "/getall", method = RequestMethod.GET)
 public String getAll(Model model) {
  logger.debug("Retrieve all persons");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<PersonList> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/persons", HttpMethod.GET, entity, PersonList.class);
   // Add to model
   model.addAttribute("persons", result.getBody().getData());
   
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/personspage.jsp
  return "personspage";
 }

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/getall

Running the URL shows the following page:

getPerson() method

The getPerson() method retrieves a single record based on the id from the REST provider. Once the result has arrived, it is added to the model to be displayed in a JSP page.

/**
  * Retrieves a single record from the REST provider
  * and displays the result in a JSP page
  */
 @RequestMapping(value = "/get", method = RequestMethod.GET)
 public String getPerson(@RequestParam("id") Long id, Model model) {
  logger.debug("Retrieve person with id: " + id);
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id);
   // Add to model
   model.addAttribute("person", result.getBody());
   
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/getpage.jsp
  return "getpage";
 }

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/get?id=1
Alternatively we can click the Get link from the getAll page.

Running the URL shows the following page:

getAddPage() and addPerson() methods

The getAddPage() method returns a JSP form where the user can add a new record.

     /**
     * Retrieves the JSP add page
     */
 @RequestMapping(value = "/add", method = RequestMethod.GET)
 public String getAddPage(Model model) {
  logger.debug("Received request to show add page");

  // Create new Person and add to model
  // This is the formBackingOBject
  model.addAttribute("personAttribute", new Person());

  // This will resolve to /WEB-INF/jsp/addpage.jsp
  return "addpage";
 }

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/add?id=1
Alternatively we can click the Add link from the getAll page

Running the URL shows the following page:

When the form is submitted, the addPerson() method takes control. Its purpose is to send a new record by sending a POST request to the REST provider.

/**
  * Sends a new record to the REST provider
  * based on the information submitted from the JSP add page.
  */
 @RequestMapping(value = "/add", method = RequestMethod.POST)
 public String addPerson(@ModelAttribute("personAttribute") Person person,
       Model model) {
  logger.debug("Add new person");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);

  // Send the request as POST
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person", HttpMethod.POST, entity, Person.class);
  } catch (Exception e) {
   logger.error(e);
  }
  
  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
 }
To see the new record, please refresh the getAll page.

getUpdatePage() and updatePerson() methods

The getUpdatePage() method returns a JSP form where the user can edit an existing record.

 /**
     * Retrieves the JSP update page
     */
    @RequestMapping(value = "/update", method = RequestMethod.GET)
    public String getUpdatePage(@RequestParam(value="id", required=true) Integer id,  
              Model model) {
     logger.debug("Received request to show edit page");
    
     // Retrieve existing Person and add to model
     // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<Person> entity = new HttpEntity<Person>(headers);

  // Send the request as GET
  try {
   ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id);
   // Add to model
   model.addAttribute("personAttribute", result.getBody());
   
  } catch (Exception e) {
   logger.error(e);
  }
     
     // This will resolve to /WEB-INF/jsp/updatepage.jsp
     return "updatepage";
 }

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/update?id=1
Alternatively we can click the Edit link from the getAll page

Running the URL shows the following page:

When the form is submitted, the updatePerson() method takes control. Its purpose is to send an updated record by sending a PUT request to the REST provider.

/**
  * Sends an update request to the REST provider
  * based on the information submitted from the JSP update page.
  */
    @RequestMapping(value = "/update", method = RequestMethod.POST)
 public String updatePerson(@ModelAttribute("personAttribute") Person person,
      @RequestParam(value="id",  required=true) Long id,
      Model model) {
  logger.debug("Update existing person");
  
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);
  
  // Send the request as PUT
  ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.PUT, entity, String.class, id);

  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
    }
To see updated record, please refresh the getAll page.

deletePerson() method

The deletePerson() method sends a DELETE request to delete an existing record based on the submitted id.

    /**
  * Sends a delete request to the REST provider
  */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
 public String deletePerson(@RequestParam("id") Long id,
          Model model) {
     logger.debug("Delete existing person");
     
  // Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
     HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  HttpEntity<String> entity = new HttpEntity<String>(headers); 
  
  // Send the request as DELETE
  ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.DELETE, entity, String.class, id);
  
  // This will resolve to /WEB-INF/jsp/resultpage.jsp
  return "resultpage";
    }

To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/delete?id=1
Alternatively we can click the Delete link from the getAll page

When you run the URL it will automatically send a request. To see the results, please refresh the getAll page.

Configuration

The application's configuration is quite trivial. It's just a basic Spring MVC configuration. To spare some space, I will not post the XML configurations here. Instead, you can look at them by checking the source code at the end of this tutorial.

Considerations

Notice in the header of each request we set the media type to APPLICATION_XML. We can equally change it to APPLICATION_JSON to accept JSON objects.

APPLICATION_XML
// Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_XML);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);

APPLICATION_JSON
// Prepare acceptable media type
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
  
  // Prepare header
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(acceptableMediaTypes);
  // Pass the new person and header
  HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);

For JSON to work, you need to have the Jackson library in your classpath. Make sure to check the pom.xml!

Conclusion

That's it. We've managed to create a REST client using Spring 3's RestTemplate. We've exchanged information with the provider via XML and displayed the result in a JSP page using Spring MVC.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-rest-guide/

You can download the project as a Maven build. Look for the spring-rest-client.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring 3: REST Web Service Provider and Client Tutorial (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

33 comments:

  1. My REST Service client is a PHP application. How to support images to the PHP client?

    ReplyDelete
  2. @Srinivas, check the following article: http://stackoverflow.com/questions/1170006/php-get-image-from-byte-array

    ReplyDelete
  3. Thanks.The issue is I was reading the image from a oracle blob column and sending as a byte[] in Response Body. IE 7 is working fine. But Firefox is not working.

    ReplyDelete
  4. Do we need to encode image data as a base64 string? or byte array is OK? As I mentioned earlier my client is a PHP

    ReplyDelete
  5. Best tutorial on Spring Restful services. Very clear. Thanks lot hope to few articles on Spring.

    Thanks
    Grabczewski

    ReplyDelete
  6. Hello Thanks for you example. I am using Rest template but i have problems in the getForObject. How i convert messages xml and json to objects? thanks

    ReplyDelete
  7. Have you tried using the exchange method instead?

    // Send the request as GET
    try {
    ResponseEntity result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id);
    // Add to model
    model.addAttribute("person", result.getBody());

    } catch (Exception e) {
    logger.error(e);
    }

    ReplyDelete
  8. Don't work. I have this:

    @Controller
    public class ControllerClient {

    private RestTemplate restTemplate = new RestTemplate();

    @RequestMapping("/p")
    public String getPersonClient() {

    /***************************************/
    // Prepare acceptable media type
    List acceptableMediaTypes = new ArrayList();
    acceptableMediaTypes.add(MediaType.APPLICATION_XML);

    // Prepare header
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(acceptableMediaTypes);
    HttpEntity entity = new HttpEntity(headers);

    try {
    ResponseEntity result = restTemplate.exchange("http://localhost:8080/RestWebService/rest/person/getperson/{id}", HttpMethod.GET, entity, Person.class, 4);

    System.out.println("Resultado: " + result.getBody().getSbName());

    } catch (Exception e) {
    System.out.println("No funciono");
    return "error";
    }
    /***************************************/

    return "p";
    }

    ReplyDelete
  9. This is my problem:

    org.springframework.http.converter.HttpMessageNotReadableException: Could not unmarshal to [class com.jossimardeleon.example.layer.data.entities.User]: unexpected element (uri:"", local:"user"). Expected elements are <{}users>; nested exception is javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"user"). Expected elements are <{}users>

    ReplyDelete
  10. Hello Krams
    Can you pls provide a pointer for a simple java REST Client that passes in BASIC authentication credentials for a Spring Secured App (@Secured and MD5 Password Encoder).....i tried using simple commons HttpClient but not sure for the encoded string to pass in

    it should be base64(username:password) or
    base64(username:MD5(password)) ? both doesn't seem to work...getting 401 error

    Can you pls suggest in this regard ?

    ReplyDelete
  11. Hello Krams
    i have account rest web service witch return list of accounts type JSON how i can get this list at client side
    List acceptableMediaTypes = new ArrayList();
    acceptableMediaTypes.add(MediaType.APPLICATION_JSON);

    // Prepare header
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(acceptableMediaTypes);
    HttpEntity entity = new HttpEntity(headers);
    List accList = new ArrayList();
    // Send the request as GET
    try {
    ResponseEntity result = restTemplate.exchange("http://localhost:9380/direct/accounts", HttpMethod.GET, entity, Account.class);


    i got Error
    Can not deserialize instance of com.dd.Account out of START_ARRAY token

    Please help me... web server works good i check it in SoapUi Sory i'm new in web services..

    ReplyDelete
  12. please tell me how i will generate WADL from this service

    ReplyDelete
  13. Hi Krams, i need send a multipart File or Photo using postForObject, how many that?

    ReplyDelete
  14. HI,

    I am getting the following error related to jackson library while runing the RestClient.

    Would you please tell me the exact version of jar file that I should use for this ??

    Error Details are:

    [12/12/11 15:16:00:940 IST] 00000021 ServletWrappe E SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: restclient. Exception thrown : org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoSuchMethodError: org/codehaus/jackson/map/type/TypeFactory.type(Ljava/lang/reflect/Type;)Lorg/codehaus/jackson/type/JavaType;
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:839)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)

    ReplyDelete
  15. Great tutorial - simple and works great!!!! Thank you much for this. Saved me a lot of work.

    Ade.

    ReplyDelete
  16. Excellent article for Rest support with Spring which I have seen so for.

    ReplyDelete
  17. Very good article. I was struggling in the same area and your post helped a lot.

    ReplyDelete
  18. This is very good article,Thank you very much, your application solved my problem...

    ReplyDelete
  19. Can someone help me solve this error

    service provider:

    //*
    @RequestMapping(value = "/ucpeople/{id}",
    method = RequestMethod.GET
    )
    public PeopleList getUcPeople(@PathVariable("id") Long id, Model model) {

    PeopleList result = new PeopleList();
    String methodName = new Exception().getStackTrace()[0].getMethodName();
    String className = new Exception().getStackTrace()[0].getClassName();
    Util.log(className, methodName, "entering ...");
    Util.log(className, methodName, "Provider has received request to get person with id: " + id);
    result.setStatus(RmpServiceCommon.STATUS_SUCCESSFUL);
    result.setMessage("OKOK");
    return result;
    }
    //*/

    Service client :

    List acceptableMediaTypes = new ArrayList();
    acceptableMediaTypes.add(MediaType.APPLICATION_XML);

    // Prepare headervoid
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(acceptableMediaTypes);
    HttpEntity entity = new HttpEntity(headers);


    ResponseEntity result = restTemplate.exchange(REQUEST_URI, HttpMethod.GET, entity, Person.class, personId);

    the server actually received/acknowledged the request and then response to the client, then the client throws this exception

    log4j:WARN Please initialize the log4j system properly.
    org.springframework.web.client.HttpClientErrorException: 404 Not Found
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:76)
    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:486)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:443)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:206)
    at edu.ucsd.rmp.client.PeopleClient.getUcPeopleByPersonId(PeopleClient.java:151)
    at edu.ucsd.rmp.client.PeopleClient.main(PeopleClient.java:213)

    ReplyDelete
  20. If you're interested in generating documentation for a standard Spring 3 RESTful service, you should check out RESTdoclet (recently outsourced by IG Group). More info and links here: http://soaprobe.blogspot.co.uk/2012/07/rest-service-documentation-using.html

    Robert

    ReplyDelete
  21. I have a ProductData POJO which contains a collection of ImageData class like
    public class ProductData
    {
    @XStreamImplicit
    Collection images;
    //not-including the set/get methods
    }

    I have a problem unmarshalling ProductData as it tries to find "image" field inside the ImageData class.

    public class ImageData
    {
    private String url;
    private String imageType;
    private String altText;
    }

    Do I have to write a custom converter for it??
    Please suggest if there is any other way out?

    ReplyDelete
  22. Thank you for posting this tutorial! Helped me a lot.
    Regards,
    Marcos

    ReplyDelete
  23. Hi krams how can i validate that the URL is given in correct format ,if url is not in correct form we need to pass message and we need to check consumes and produces media type ,if the media type is other than json we need to pass a message in rest client

    ReplyDelete
  24. Hi Krams,

    This example you are using the static data. Can you tell me how to do restful webservices using database. In your previous spring hibernate mysql example

    ReplyDelete
  25. Hi,

    How to do database configuration for the same project. I am using Mysql.Can u please tell me how to do why because i want do rest webservices dynamicly

    ReplyDelete
  26. can you provide the Jersey example

    ReplyDelete
  27. Hi Karam,

    How to integrate the
    i) Spring 3: REST Web Service Provider and Client Tutorial (Part 1)
    with
    ii) Spring 3: REST Web Service Provider and Client Tutorial (Part 2)

    Whether to run on different port nos on tomcat ?

    ReplyDelete
  28. Hi Karam,

    Want to know how to consume the services exposed with the help of client code which is thier in client tutorial. To put in other words how do we integrate client and service code.

    ReplyDelete
  29. Application Service Provider-A Application Service Provider (ASP) helps companies and their customers to stay in touch via mobile technology. What makes these ASP providers, like Integrate in South Africa, so special is the fact that they are highly specialized and you would not simply find one around every corner.

    ReplyDelete
  30. Really impressive.
    http://www.indiatravelsonline.in/
    http://www.indiatravelsonline.com/

    ReplyDelete
  31. It's the best time to make a few plans for the long run and it is time to be happy. I have learn this publish and if I may I wish to recommend you few fascinating things or suggestions. Maybe you could write next articles regarding this article. I wish to read even more issues about it!

    ReplyDelete