CHAPTER 5

image

Spring RESTful Services

REST is an acronym for REpresentational State Transfer. It was introduced and defined in 2000 by Roy Fielding in his doctoral dissertation. REST is a lightweight alternative to mechanisms like RPC (Remote Procedure Calls) and web services (SOAP, WSDL, etc.). REST is an architecture style for designing networked (distributed) applications. The idea is that, rather than using complex mechanisms such as CORBA, RPC, or SOAP to connect machines, simple HTTP is used to make calls between machines. RESTful applications use HTTP requests to post data (create and/or update), read data (e.g., make queries), and delete data. Thus, REST uses HTTP for all four CRUD (create/read/update/delete) operations.

Core REST Concepts

The REST architectural style describes six constraints:

  • Uniform interface: Defines the interface between client and server. Rest uses HTTP as an application protocol, as a platform, not just a transport protocol. The following HTTP specifications are used:
    • HTTP verbs are used as actions to execute on the resources (GET, PUT, PATCH, POST, DELETE, HEAD, and OPTIONS)1
    • URIs are used to identify resource names. The resources are conceptually separate from representations. Representations of the resources are returned from the server to the client, after a client request (typically JSON or XML). Representations contain metadata that can be used by the client to modify or delete the resource on the server, provided it has permission to do so.
    • HTTP response: Response codes, the body, and headers are used to deliver state to clients. Clients deliver state using body content, query-string parameters, request headers, and the URI.
  • Statelessness: The server should contain no client state. Each request has enough context for the server to process the message. The URI uniquely identifies the resource, and the body contains the state (or state change) of that resource if the request is one that has a body (PUT, POST, PATCH). When working with a specific container, a session is used to preserve state across multiple HTTP requests. When using REST, there is no need for this, which increases the scalability because the server does not have to maintain, update, or communicate the session state.
  • Client-server: A RESTful architecture is a client-server architecture, so the system is disconnected. The server might not be available all the time, so operations are asynchronous.
  • Cacheable: Anything returned by the server can be cached explicitly (the server specifies conditions for caching), implicitly (the client uses its own caching conditions), or negotiated(the client and the server negotiate caching conditions)
  • Layered system: The client cannot assume direct connection to the server. Sometimes a requested resource can be cached, and some other unknown software and hardware layers are interposed between the client and the server. Intermediary servers may improve system scalability and/or security by enabling load balancing, providing shared caches, and enforcing security policies.
  • Code on demand: Executable code can be transferred as a representation to the client (usually JavaScript or compiled Java applications known as applets).

Image Note  Processes running on different hosts communicate over a layered set of network protocols defined by the OSI model. The uppermost level is the application layer and protocols specific to it are called application protocols. This is the layer that is closest to the user, which means the user interacts directly with the software application. Between the application layer and the transport layer are two more layers.

The transport layer provides the functional and procedural means of transferring variable-length data sequences from a source to a destination host via one or more networks, while maintaining the quality of service functions. The protocols specific to it are called transport protocols.

When using REST, data is not just sent and received via HTTP (transport), but data is actively manipulated by the user in the context of an application. More information about network layers and protocols can be found on the Internet; if you are interested in finding out more, you can check out Wikipedia at https://en.wikipedia.org/wiki/OSI_model. Advanced networking is not the object of this book or the certification exam.

Complying with the first five constraints ensures that a RESTful application will be scalable, simple, easy to modify, portable, and reliable. The last constraint is optional; a REST application can be built without code being transferred to clients, if there is no need for such operations. The main REST HTTP methods are presented in Table 5-1.

Table 5-1. Message Converters

HTTP Method

Purpose

Observation

GET

Read

Reads a resource; does not change it: therefore, it can be considered safe. Reading the same resource always returns the same result: therefore, it can be considered idempotent.

POST

Create

Used to create a new resource. Neither safe nor idempotent. Two identical POST requests will result in two identical resources being created or errors at application level.

PUT

Update

Most often used for update capabilities. It is not safe, because it modifies the state on the server, but is idempotent (unless subsequent calls of the same PUT request increments a counter within the resource, for example).

DELETE

Delete

Used to delete resources. Not safe, but can be considered idempotent because requests to delete a resource that no longer exists will always return a 404 (not found).

To analyze contents of the REST requests and responses handled by the browser, the Firebug plugin in Firefox can be used. Simply install it directly from the official site (http://getfirebug.com/) and enable it by clicking the little bug on the right corner of the page (1). To see the contents of a request, just click on the Net tab (2), as depicted in Figure 5-1.

9781484208090_Fig05-01.jpg

Figure 5-1. Using the Firebug plugin in Firefox to analyze REST requests and responses handled by the browser

The following describes the GET example shown in Figure 5-2:

  • It retrieves a representation of a resource.
  • It might have length restrictions.2
  • It is a safe operation; idempotent; repetitive execution that has no side effects.
  • It is cacheable and ETags are used to keep tags on resource versions.3
  • When a resource is not found, a 404 (Not Found) status code is returned; otherwise 200 (OK)

9781484208090_Fig05-02.jpg

Figure 5-2. GET Request and Response example; snippets form the Firebug console

The following describes the POST example shown in Figure 5-3:

  • It creates a new resource.
  • It is not idempotent; repetitive execution causes duplicate data and/or errors.
  • The response has the created resource location URI in the response header.
  • When the resource being created requires a parent that does not exist a 404 (Not Found) status code is returned. When an identical resource already exists a 409 (Conflict) status code is returned. When the resource was created correctly a 201 (Created) status code is returned.

9781484208090_Fig05-03.jpg

Figure 5-3. POST Request and Response example

The following describes the PUT example shown in Figure 5-4:

  • It updates an existing resource or creates it with a known destination URI. The URI of a resource contains an identifier for that resource. If that identifier is not generated by the application, but can be created by the client a behavior such as this can be implemented: when a PUT request refers to an existing resource, the resource is updated, otherwise a new resource with the identifier from the URI and the contents in the request body is created.
  • It is idempotent; repetitive execution has the same result.
  • It is not safe; repetitive updates could corrupt data.
  • When the resource being updated requires a parent that does not exist, or the resource requested to be updated does not exist, a 404 (Not Found), status code is returned. When the resource is updated correctly, a 200 (OK) (or a 204 (No Content) if not returning any content in the body) status code is returned.

9781484208090_Fig05-04.jpg

Figure 5-4. PUT Request and Response example

The following describes the DELETE example shown in Figure 5-5:

  • It deletes a resource.
  • It is idempotent; repetitive execution has the same result.
  • It is not safe; repetitive deletes could corrupt data.
  • When the resource being deleted does not exist, a 404 (Not Found), status code is returned. When the resource was deleted correctly, a 200 (OK) status code is returned.

9781484208090_Fig05-05.jpg

Figure 5-5. DELETE Request and Response example

When it comes to REST, everything is about resource states and transferring them between a client and a server, in different forms. The request specifies the representation type using the Accept HTTP header for GET and the Content-Type HTTP header for PUT and POST, as you have seen in the preceding images, because when the client is not a browser (remember Chapter 3), the Accept header is taken into consideration. The URI extension can be used as a representation type identifier too. The response reports the representation type returned using the Content-Type HTTP header. When using Spring, the representation type is specified using an attribute of the @RequestMapping annotation and well-known media types defined in the MediaType class:

@RestController
@RequestMapping(value = "/rest-persons")
public class PersonsRestController extends BaseController {
...
@ResponseStatus(HttpStatus.OK)
@RequestMapping(value = "id/{id}", method = RequestMethod.GET,
                 produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPersonById(@PathVariable Long id) throws NotFoundException {
    logger.info("-----> PERSON: " + id);
    Person person = personManager.findById(id);
      if (person == null) {
           throw new NotFoundException(Person.class, id.toString());
      }
      return person;
}
...
}

//Exception handler class for Rest errors.
@ControllerAdvice(basePackages = "com.pr.rest")
public class RestExceptionProcessor {
/

       Maps NotFoundException to a 404 Not Found HTTP status code.

     @ResponseStatus(value = HttpStatus.NOT_FOUND,
           reason = "This entity is not found in the system")
     @ExceptionHandler({NotFoundException.class})
     public void handleNotFound(NotFoundException nfe) {
         // just return empty 404
         logger.info("-----> Entity " + nfe.getObjType() +
           " with identifier" + nfe.getObjIdentifier() + "not found.");
     }
}

HATEOAS

On his public blog,4 Roy Fielding mentioned that most REST services are not really RESTful, because fully RESTful services should only return links. Basically, HATEOAS implies that when a client makes a REST request to a server, the server should return a response that informs the client of all possible REST operations using links. For example, a resource should contain links to related resources, including URIs for editing it and deleting it, and so forth. Following this idea, well-known author Leonard Richardson defined the Richardson Maturity Model,5 which describes four levels of REST compliance:

  • Level 0, also known as the Swamp of POX. Only HTTP is used as a transport method.
  • Level 1, also known as the Resource Level. HTTP is used as a transport method and URIs are used to identify resources.
  • Level 2, also known as the HTTP Verb Level. This is the level where HTTP headers, statuses, methods, distinct URIs and everything else HTTP has to offer to provide a REST service. At this level, HTTP is used the way it’s meant to be.
  • Level 3, also known as the Hypermedia Controls Level. This is the final level, where a fully complying REST service should be. HATEOAS, an abbreviation for Hypermedia As The Engine Of Application State, is a constraint of the REST application architecture that distinguishes it from most other network application architectures. The principle is that a client interacts with a network application entirely through hypermedia provided dynamically by application servers.

The Spring team has developed a separate project to make it easy to implement RESTful services that comply with the third level. Spring HATEOAS6 provides APIs to ease creating REST representations that follow the HATEOAS principle when working with Spring, and especially Spring MVC.

HATEOAS is a concept of application architecture, which defines the way clients interact with servers using hypermedia links they find inside representations returned by the server. To implement HATEOAS, resources representations must comply with a set of standards and contain hypermedia information. One of the most common standards used to hyperlink resources is HAL.7 A resource in HAL is just a plain-old JSON or XML object with whatever properties needed, but that provides the possibility to hyperlink resources. The following is a code snippet showing what a resource representation that complies to the HAL standard looks like:

//GET Request: /persons/5 using JSON format
// Response representation returned below:
{
"links": {
    "self": { "href": "/persons/5" },
    "parents": [
      { "href": "/persons/2", "title": "mother" },
      { "href": "/persons/3", "title": "father }
    ]
},
    "firstName" : "John",
    "middleName" : "Constantine",
    "lastName" : "Smith",
    "dateOfBirth" : "1935-10-01",
    "gender" : "MALE",
    "hospital" : {
    "code" : "134181",
    "name" : "General Hospital",
    "address" : "Sample address",
    "location" : "Constance, Romania"
},
"identityCard" : {
    "pnc" : "1351001134181",
    "series" : "CO",
    "number" : "205727",
    "emittedAt" : "1949-10-01",
    "expiresAt" : "1985-10-01",
    "address" : "34eeb1d5-0ff4-4d4a-b811-4ff32aa15ada"
  }

}

//GET Request: /persons/5 analogous example using XML
<?xml version="1.0" encoding="utf-8"?>
<person rel="self" href="/person/5">
      <linkList>
          <link rel="parent" title="mother" href="/persons/2"/>
          <link rel="parent" title="father" href="/persons/3"/>
      </linkList>

      <firstName>John</firstName>
      <lastName>Constantine</lastName>
      <!-- other simple properties-->
      ...
      <hospital>
               <code>134181</code>
               <!-- other simple properties-->
            ...
      </hospital>
      <identityCard>
              <pnc>1351001134181</pnc>
              <!-- other simple properties-->
            ...
      </identityCard>

</person>

Advantages of REST

The following list describes the advantages of REST.

  • REST is simple.
  • REST is widely supported.
  • Resources can be represented in a wide variety of data formats (JSON, XML, Atom, etc.).
  • You can make good use of HTTP cache and proxy servers to help you handle high loads and improve performance.
  • It reduces client/server coupling.
  • Browsers can interpret representations.
  • JavaScript can use representations.
  • A REST service can be consumed by applications written in different languages.
  • It is easy for new clients to use a RESTful application, even if the application was not designed specifically for a client.
  • Because of the statelessness of REST systems, multiple servers can be behind a load balancer and provide services transparently, which means increased scalability.
  • Because of the uniform interface, documentation of the resources and basic API operations are unnecessary.
  • The hypermedia constraint assures that application processing transitions are always navigable by clients, simply by following opaque server-provided links. Thus, the client does not need to understand anything more than the data format. (And when JSON is used, the data format is quite obvious.)
  • Using REST does not imply specific libraries at the client level in order to communicate with the server. With REST, all that is needed is a network connection.

REST services can be secured, but as the interaction between the client and server is stateless, credentials have to be embedded in every request header. Basic authentication is the easiest to implement without additional libraries (HTTP Basic, HTTP Digest, XML-DSIG, or XML-Encryption), but it guarantees the lowest level of security. Basic authentication should never be used without TLS (formerly known as SSL) encryption because the credentials can be easily decoded otherwise. In Figure 5-6, you can see how basic authentication is used when a client communicates with a RESTful application that requires basic authentication.

9781484208090_Fig05-06.jpg

Figure 5-6. Basic authentication when using RESTful systems

!  When a collection is expected, it is enough use: /persons (plural) and /hospitals (plural). The /all link was used here because the original web controllers implemented in Chapter 3 were kept separate, so you can access the interface and verify the changes you are doing via REST in the browser. Basically, the REST and web functionalities are fully decoupled. And because the PersonsController was already mapped to /persons and the HospitalController was already mapped to /hospitals, there was no other way to do this but to map the REST controllers to different URLs.

!  Snippets of code from the HospitalsRestController are not mentioned in the book, because the code is almost identical to the one for the REST methods in PersonsController; the only difference is the resource type. But the code is available for you to practice on in the book’s code samples.

Other common protocols used with RESTful systems are OAuth 1.0a and OAuth 2.0. Custom security implementation should be used only if necessary, because the skill to understand cryptographic digital signatures is quite difficult to master.

There may be a lot more to say about REST in general, but the introduction to REST must end here, as this chapter is about Spring and how Spring can be used to develop RESTful applications. And you will notice that providing and consuming REST services with Spring is so easy that a deep understanding of REST is not actually needed.

RESTful Applications Using Spring MVC

To learn how to implement and test RESTful services using MVC, module 09-pr-rest-practice was created. This module contains the implementation of the operations depicted in Figure 5-7.

9781484208090_Fig05-07.jpg

Figure 5-7. RESTful architecture for the practice section

RESTful Clients with Spring

A RESTful application can be accessed by any type of client that can create the type of request supported by the application. To test a Spring RESTful application, Spring provides two classes: RestTemplate and AsyncRestTemplate.

The RestTemplate is Spring’s central class for synchronous client-side HTTP access. This class provides a wide set of methods for each HTTP method, which can be used to access RESTful services and enforces REST principles.8 Figure 5-8 depicts a correspondence between HTTP methods and RestTemplate methods that can be used to access REST services.

9781484208090_Fig05-08.jpg

Figure 5-8. RestTemplate api to HTTP methods correspondence

As you can see, the execute and exchange methods can be used for any type of REST calls, as long as the HTTP method is given as a parameter for the methods. All methods are polymorphic,9 and using one or another depends on the requirements and the developer’s preferences. URI instances are returned to identify resources, and RestTemplate methods support URI templates. So, the following two calls are identical:

//using URI Template
String url = "http://localhost:8080/mvc-rest/rest-person/id/{id}";
Person person = restTemplate.getForObject(url, Person.class, "1");

// using URI
String url = "http://localhost:8080/mvc-rest/rest-personid/1";
Person person = restTemplate.getForObject(url, Person.class);

The execute method can also be given a RequestCallback implementation as a parameter, which tells the RestTemplate what to do with the request before sending it to the server. Considering this, a GET request for a Person instance with id=1 could be written with the exchange method like this:

String url ="http://localhost:8080/mvc-rest/rest-person/id/{id}";
       Person person = restTemplate.execute(url, HttpMethod.GET,
       new RequestCallback() {
             @Override
             public void doWithRequest(ClientHttpRequest request)
                     throws IOException {
                 HttpHeaders headers = request.getHeaders();
                 headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
                 System.out.println("Request headers = " + headers);
            }
      }, new HttpMessageConverterExtractor<Person>(Person.class,
          restTemplate.getMessageConverters())
               , new HashMap<String, Object>() {{
          put("id", "1");
      }});

Objects passed to and returned from the methods getForObject(), postForLocation(), and put() are converted to HTTP requests and from HTTP responses by HttpMessageConverters. Message converters are automatically detected and used by Spring in applications configured with <mvc:annotation-driven/> or @EnableWebMvc. In the code sample for this chapter, the representations are in JSON format, so MappingJackson2HttpMessageConverter is used. And because the message format is supported by default, the HttpMessageConverterExtractor<T> is not necessary in the previous example. Also, if no Accept header is specified, all formats supported by Spring are considered. So in this case, RequestCallback becomes unnecessary too, so you can stick to the simpler restTemplate.getForObject method that was mentioned in the previous code snippet.

Speaking of message converters, restTemplate deals only with objects, so it internally converts resources to representations, and vice-versa, using message converter implementations of the HttpMessageConverter<T> interface. Spring comes with a default long list of supported message converters, but if necessary, a developer can provide his own implementation of the HttpMessageConverter<T>. Table 5-2 provides a list of the most commonly used message converters and the datatype handled:

Table 5-2. Message Converters

Message Converter

Data Type

Observation

StringHttpMessageConverter

text/plain

 

MappingJackson2HttpMessageConverter

application/*+json

Only if Jackson 2 is present

on the classpath

AtomFeedHttpMessageConverter

application/atom+xml

Only if Rome is present on

the classpath

RssChannelHttpMessageConverter

application/rss+xml

Only if Rome is present on

the classpath

MappingJackson2XmlHttpMessageConverter

application/*+xml

Only if Jackson 2 is present

on the classpath

To use a restTemplate, you can define and initialize it directly where you need it, or declare a bean and inject it. restTemplate handles HTTP connections internally, so the developer does not have to write extra code with opening and closing connections. A different HTTP client can also be used, and Apache provides an implementation that can be injected into a RestTemplate bean. This is highly recommended for production applications when authentication and HTTP connection pooling are usually needed.

To use the Apache Commons HttpClient, Spring provides a factory class named HttpComponentsClientHttpRequestFactory, which provides an HttpClient instance that uses a default org.apache.http.impl.conn.PoolingClientConnectionManager10 that is able to service connection requests from multiple execution threads.

<bean id="restTemplate" class="o.s.web.client.RestTemplate">
    <property name="requestFactory">
        <bean class= "o.s.http.client.HttpComponentsClientHttpRequestFactory"/>
    </property>
</bean>

Configuring a RestTemplate bean using Java Configuration looks like this:

\in the @Configuration and @EnableWebMvc annotated class
@Bean
  public RestTemplate restTemplate() {
      RestTemplate restTemplate = new RestTemplate();
      restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
      return restTemplate;
}

Other examples of restTemplate usage are in the following code snippet:

// GET request to retrieve all persons born at a hospital with a specific code
String url = "http://localhost:8080/mvc-rest/rest-hospitals/{code}/persons";

Person[] persons = restTemplate.getForObject(url, Person[].class, "134181");

// POST request to create a person
Person person = buildPerson();
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

final HttpEntity<Person> personRequest = new HttpEntity<>(person, headers);
String url = "http://localhost:8080/mvc-rest/rest-persons/create";
// this method returns the created resource
Person newPerson = this.restTemplate.postForObject(url, personRequest, Person.class);
//this method returns the URI of the created resource
URI uri = this.restTemplate.postForLocation(url, personRequest, Person.class);

//DELETE request to delete a person by id
String url = "http://localhost:8080/mvc-rest/rest-persons/delete/23";
restTemplate.delete(url);

REST services are used most commonly by AJAX components in a web application, and currently all HTTP methods are supported in AJAX. But most browsers do not support any other methods besides GET and POST in HTML forms. To use them in a form, Spring has introduced hidden methods. Basically, a hidden input is added to a form with a regular POST method. If the POST request is to be treated as a PUT request, the value of the field will be equal to this method name, as shown in the code sample below. A filter interceptor intercepts the request, searches for that parameter, and modifies the request accordingly before sending it to the appropriate handler.

For this to work, the Spring form has the method attribute value set to the desired HTTP method, and the resulting HTML form has a hidden field added:

<!-- Spring form -->
<sf:form method="put" action=".." modelAttribute="..">
    ...
</sf:form>

<!-- HTML form -->
<form method="post" action="...">
     <input type="hidden" name="method" value="put" />
     ...
</form>

The filter interceptor that takes care of intercepting requests and modifying the methods is the HiddenHttpMethodFilter, which can be configured in a web.xml file or in a class implementing WebApplicationInitializer.

<!-- in web.xml -->
  <!-- Enables use of HTTP methods PUT and DELETE -->
      <filter>
           <filter-name>httpMethodFilter</filter-name>
           <filter-class>o.s.web.filter.HiddenHttpMethodFilter</filter-class>
      </filter>

      <filter-mapping>
          <filter-name>httpMethodFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>

\in class extending AbstractDispatcherServletInitializer
\ or AbstractAnnotationConfigDispatcherServletInitializer
@Override
protected Filter[] getServletFilters() {
      return new Filter[] { new HiddenHttpMethodFilter()};
}

Asynchronous REST Calls

At the beginning of this section, AsyncRestTemplate was mentioned. This class can be used to create Spring REST clients that make asynchronous calls to a REST service. The AsyncRestTemplate class is nothing other than a wrapper class for RestTemplate that provides the asynchronous behavior via a set of methods (analogous to the ones in RestTemplate) that return Future<T> wrappers (or ListenableFuture<F> that extends Future<T> when a callback method is needed) instead of concrete data. An example of an asynchronous GET request can be found in the AsyncRestTemplateTest class, in the 07-pr-rest-solution. In the same class, you can also find an example with a callback.

private static final String PERSON_BASE_URL =
         "http://localhost:8080/mvc-rest/rest-persons/id/{id}";
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
...

Future<ResponseEntity<Person>> futurePerson =
      asyncRestTemplate.exchange(url, HttpMethod.GET, entity, Person.class, "5");
  //waiting a little, to give time to the async call to complete
         Thread.sleep(1000L);

ResponseEntity<Person> result = futurePerson.get();
Person person = result.getBody();
assertNotNull(person);

//callback example
ListenableFuture<ResponseEntity<Person>> futurePerson =
       asyncRestTemplate.exchange(url, HttpMethod.GET, entity, Person.class, "5");
  futurePerson.addCallback(new ListenableFutureCallback<ResponseEntity<Person>>() {
               @Override
               public void onSuccess(ResponseEntity result) {
                Person person = (Person) result.getBody();
                assertNotNull(person);
                }

                @Override
                public void onFailure(Throwable t) {
                    logger.error("------> Async call failure!", t);
                }
});

Implementing REST with Spring MVC

There are multiple Java frameworks available for implementing RESTful applications: Spark, Restlet, JAX-RS(Java EE), and RESTEasy, but Spring MVC is the easiest to use. This section contains a lot of information and code snippets to convince you that this affirmation is true. REST support was added to Spring MVC in version 3.0, and although developing RESTful applications was always easy, in version 4.x things have become even more practical.

Among the aforementioned frameworks, JAX-RS is shipped with out-of-the-box Spring Integration. This framework encapsulates the Java API for RESTful web services (JAX-RS, defined in JSR 311). Jersey, the reference implementation of JAX-RS, implements support for the annotations defined in JSR 311, making it easy for developers to build RESTful web services by using the Java programming language. It is focused more on application-to-application communication, so the focus is not on browser clients. That’s the amazing thing about Spring MVC—a Spring RESTful application does not care about its client type at all.

Spring MVC provides the following resources to build RESTful applications:

  • The potential to declare status codes.
  • URI templates.
  • Content negotiation.
  • Many message converters offer out-of-the-box support.
  • RestTemplate and AsyncRestTemplate classes are used for easily creating client applications or for testing RESTful application services.
  • Browsers are supported as clients, although HTTP method conversion is necessary for PUT and DELETE methods. When making REST requests from a web page, jQuery can be used (this is covered in Chapter 6).

A few of these have already been mentioned in the previous section, as they were involved in creating REST clients; the others are covered in this section.

To develop a RESTful service class with Spring MVC, you have to do the most obvious thing: create a controller that contains handler methods that return resources representations instead of views, which are the actual response body. In Spring 3.0, we had to do the following:

@Controller
@RequestMapping(value = "/rest-persons")
public class PersonsRestController {

@Autowired
PersonManager personManager;

      @ResponseStatus(HttpStatus.OK)
      @RequestMapping(value = "/id/{id}", method = RequestMethod.GET)
       public @ResponseBody Person getPersonById(@PathVariable Long id)
            throws NotFoundException {
           Person person = personManager.findById(id);
           if (person == null) {
               throw new NotFoundException(Person.class, id.toString());
           }
           return person;
      }
}

Looks like any MVC controller, right? The only difference is the @ResponseBody that indicates a method return value should be bound to the web response body. The advantage here is that, in the same controller you can also have methods that are not used to provide REST representations, having all the people management data in one place. But, because it is a good practice to decouple code with different scopes, in Spring MVC 4.0 the @RestController was introduced. This annotation is conveniently annotated with @Controller and @ResponseBody, which practically means that if you annotate a class with it, all handler methods are transparently annotated with @ResponseBody. Also, the purpose of this controller becomes quite obvious—it handles only REST requests. Thus, the preceding code becomes the following:

@RestController
@RequestMapping(value = "/rest-persons")
public class PersonsRestController extends BaseController {

      @ResponseStatus(HttpStatus.OK)
      @RequestMapping(value = "/id/{id}", method = RequestMethod.GET)
       public Person getPersonById(@PathVariable Long id) throws NotFoundException {
       ... // identical content as above
      }
}

And this is all. All methods defined inside this class can then be called from REST clients, and they will receive the requested representations. What happens in the background—the way that the DispatcherServlet is involved—is depicted in Figure 5-9.

9781484208090_Fig05-09.jpg

Figure 5-9. Spring MVC RESTFul Container

So basically, the controller methods return data directly to the client—data that no longer needs to be processed in order to render a view. Every time a request is mapped to a handler method that has parameters annotated with @RequestBody, or the method is annotated with @ResponseBody, Spring loops over all HttpMessageConverters; it is seeking the first that fits the given MIME type and class, and then uses it for the actual conversion.

Mapping requests to methods is the same as with web controllers. All annotations applicable in web handler methods are applicable in REST handler methods too: @PathVariable, @Valid, and so forth. @RequestParam can be used too, but this would break the REST constraints mentioned at the beginning of the chapter.

HTTP Status Codes

When a web application returns a response, that response has a status code that describes a certain state of the returned resource or the result of the operation that the request triggered on the server. The most familiar is probably the 404 Not Found status code that is returned when a requested resource cannot be found. A full list of HTTP status codes can be found on Wikipedia, which you should look at if you are curious about and unfamiliar with HTTP status codes.11

RESTful applications use HTTP status codes to communicate with their clients. With Spring MVC, the status code of a response can be set easily using the @ResponseStatus annotation. This annotation can receive as a value any of the constants defined in Spring class HttpStatus. Table 5-3 contains the most common response statuses used in RESTful applications.

Here are some examples of @ResponseStatus annotated REST handlers that you will work with in the practice project for this chapter:

@ResponseStatus(HttpStatus.NO_CONTENT)
@RequestMapping(value = "/delete/{pnc}", method = RequestMethod.DELETE)
public void deletePerson(@PathVariable String pnc) throws NotFoundException {
  ...
}

@ResponseStatus(HttpStatus.CREATED)
@RequestMapping(value = "/create", method = RequestMethod.POST,
             produces = MediaType.APPLICATION_JSON_VALUE,
             consumes = MediaType.APPLICATION_JSON_VALUE)
public Person createPerson(@RequestBody @Valid Person newPerson) {
     ...
}

@ResponseStatus(HttpStatus.OK)
    @RequestMapping(value = "/all", method = RequestMethod.GET,
    produces = MediaType.APPLICATION_JSON_VALUE)
public List<Person> getAll() {
...
}

Table 5-3. HTTP Status Codes

HTTP Status

HttpStatus Constant

Observation

200

OK

Successful GET  with  returned content.

201

CREATED

Successful PUT or POST; location header should contain URI or new resource.

204

NO_CONTENT

Empty  response; after

successful PUT or DELETE.

404

NOT_FOUND

Resource was not found.

403

FORBIDDEN

Server is refusing to respond to the request, because the response is not authorized.

405

METHOD_NOT_ALLOWED

HTTP method is not supported for the resource identified by the Request-URI.

409

CONFLICT

Problems when making changes, when PUT or POST try  to  save  data that already exists and is marked as unique

415

UNSUPPORTED_MEDIA_TYPE

The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method.

!  The "produces" and "consumes" properties are covered later in the chapter.

!  Normally, void or null returning methods result in a default view name determined by the request’s path information (from @RequestMapping annotations on the class and method, as explained in Chapter 3).The @ResponseStatus overrides the default behavior, causing a null ModelAndView to be used, which indicates that the response has been handled by the controller method already. So, obviously the @ResponseStatus is mandatory for a RESTful handler method returning void or null.

Exception Handling

The status codes can be used for exception handlers too. Yes, RESTful handlers can also throw exceptions, and they have to be properly handled. Similar to Spring MVC web specific controllers, exception handlers can be defined either in the body of the REST controller, or they can be defined in class annotated with @ControllerAdvice. And the same ExceptionHandler annotation is used to annotate the exception handler methods. In the next code snippet, such a class was defined with two exception handlers for different types of exceptions, and the handlers were limited to the com.pr.rest package, using the basePackages attribute, in order to handle exceptions thrown only by controllers in that package.

  @ControllerAdvice(basePackages = "com.pr.rest")
public class RestExceptionProcessor {
     private Logger logger = LoggerFactory.getLogger(RestExceptionProcessor.class);

     //Maps IllegalArgumentExceptions to a 404 Not Found HTTP status code
    @ResponseStatus(value = HttpStatus.NOT_FOUND,
           reason = "This entity is not found in the system")
    @ExceptionHandler({NotFoundException.class})
    public void handleNotFound(NotFoundException nfe) {

      // just return empty 404
      logger.info("-----> Entity " + nfe.getObjType() + " with identifier"
          + nfe.getObjIdentifier() + "not found.");
}

     // Maps DataIntegrityViolationException to a 409 Conflict HTTP status code.
     @ResponseStatus(value = HttpStatus.CONFLICT,
            reason = "Another entity with the same identity exists")
     @ExceptionHandler({DataIntegrityViolationException.class})
     public void handleAlreadyExists() {
            // just return empty 409
            logger.info("-----> Entity save operation failure");
     }
}

Content can be returned using an exception handler, but in this case, the client must be implemented to handle the response.

@ExceptionHandler(NotFoundException.class)
    @ResponseStatus(value= HttpStatus.NOT_FOUND)
    @ResponseBody
    public JsonError personNotFound(HttpServletRequest req, NotFoundException ex) {
        Locale locale = LocaleContextHolder.getLocale();
        String errorMessage = messageSource.
                        getMessage("error.no.person.id", null, locale);

        errorMessage += ex.getObjIdentifier();
        String errorURL = req.getRequestURL().toString();

        return new JsonError(errorURL, errorMessage);
}

  ...
public class JsonError {
    private String url;
    private String message;

    public JsonError(String url, String message) {
       this.url = url;
       this.message = message;
    }

    // getters and setters
}

The “produces” and “consumes” Properties

In the previous examples, the consumes and produces annotation properties of the @RequestMapping were used. These two attributes are used to narrow the primary mapping for a request. The consumes attribute defines the consumable media types of the mapped request (defined on the server) and the value of the Content-Type header (defined on the client side) must match at least one of the values of this property in order for a method to handle a specific REST request. Let’s say, for example, that in the REST client, the following headers were set:

final HttpHeaders headers = new HttpHeaders();
final String url = "http://localhost:8080/mvc-rest/rest-persons/create";
\"application/json"
headers.setContentType(MediaType.APPLICATION_JSON);
final HttpEntity<Person> personRequest = new HttpEntity<>(person, headers);
        Person newPerson =
             restTemplate.postForObject(url, personRequest, Person.class);

On the server, the following REST handler would be mapped to process this request:

@ResponseStatus(HttpStatus.CREATED)
@RequestMapping(value = "/create", method = RequestMethod.POST,
  produces = MediaType.APPLICATION_JSON_VALUE,
  consumes = {MediaType.APPLICATION_JSON_VALUE,
  //Public constant media type for {@code application/octet-stream}.
    MediaType.APPLICATION_OCTET_STREAM})
public Person createPerson(@RequestBody @Valid Person newPerson) {
...
}

The produces attribute defines the producible media types of the mapped request, narrowing the primary mapping. The value of the Accept header (on the client side) must match at least one of the values of this property in order for a method to handle a specific REST request. Let’s say, for example, that in the REST client there is the following request:

final  String  url  =  "http://localhost:8080/mvc-rest/rest-persons/id/{id}";
Person  person   =  restTemplate.execute(url,  HttpMethod.GET,  request  ->  {
            HttpHeaders headers = request.getHeaders();
            headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
        }, new HttpMessageConverterExtractor<>(Person.class,
               restTemplate.getMessageConverters())
                 , new HashMap<String, Object>() {{
          put("id", "1");
        }});

!  As mentioned, the code for making a REST request for a person can be far simpler than what was depicted earlier. The execute method was used here to show how this method can be used.

On the server, the following REST handler would be mapped to process this request:

@ResponseStatus(HttpStatus.OK)
@RequestMapping(value = "/id/{id}", method = RequestMethod.GET,
    produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPersonById(@PathVariable Long id) throws NotFoundException {
        return personManager.findById(id)
}

Accessing Servlet Environment and Request Data

Because RESTful controllers are run in a servlet environment, and the interface is the DispatcherServlet, the servlet environment properties can be injected and accessed in the same manner presented in Chapter 3. The RESTful handler methods can have flexible signatures. HttpServletRequest or HttpServletResponse can be used as parameters, and Spring will take care of populating them for you. The @PathVariable and @RequestParam annotations can be used to tell Spring to inject request data automatically. @Valid can be used to validate resources submitted with POST or PUT. And so on. Even SpEL expressions are supported. The next example depicts a REST handler for a POST method, which creates a person and adds the URI of the new resource; this is built from the original request URL that is populated by Spring as a value for the Location header:

@ResponseStatus(HttpStatus.CREATED)
@RequestMapping(value = "/create2", method = RequestMethod.POST)
    public void createPerson2(@RequestBody @Valid Person newPerson,
          @Value("#{request.requestURL}")StringBuffer originalUrl,
          HttpServletResponse response) {

          Person person = personManager.save(newPerson);
          logger.info("-----> PERSON: " + person);
          response.setHeader("Location",
                 getLocationForPersonResource(originalUrl, person.getId()));
}

//Determines URL of person resource based on the full URL of the given request,
//appending the path info with the given childIdentifier using a UriTemplate.
protected static String getLocationForPersonResource
     (StringBuffer url, Object childIdentifier) {
     String newURL = url.toString();
     newURL = newURL.replace("create2", "id/{id}");
     UriTemplate template = new UriTemplate(newURL);
     return template.expand(childIdentifier).toASCIIString();
}

Another method for accessing request and response is the HttpEntity<T> class and its subclasses: RequestEntity<T> and ResponseEntity<T>. By using these classes, you can get access to the request and response body. RequestEntity<T> and ResponseEntity<T> can be used as follows:

  • In the REST client to encapsulate every detail about a REST request that is made by calling restTemplate.exchange.
                    final String url = "http://localhost:8080/mvc-rest/rest-persons/id/{id}";

                    final RequestEntity<Person> entity = RequestEntity.post(new URI(url))
                                   .accept(MediaType.APPLICATION_JSON)
                                   .contentType(MediaType.APPLICATION_JSON)
                    //setting a custom header that will be accessed in the handler method
                                   .header("custom", "true")
                                   .body(person);

                          ResponseEntity<Person> response = restTemplate.exchange(entity,Person.class);
                          Person newPerson = response.getBody();

                         //get URI location for the Person created
                         HttpHeaders headers = response.getHeaders();
                         URI uri = headers.getLocation();
  • In the RESTful handler method to access request headers, read the body of a request, and write headers to the response stream.
                    @ResponseStatus(HttpStatus.CREATED)
                    @RequestMapping(value = "/create3", method = RequestMethod.POST)
                    public ResponseEntity<Person> handle(HttpEntity<Person> requestEntity,
                               @Value("#{request.requestURL}") StringBuffer originalUrl)
                                  throws UnsupportedEncodingException {
                         // will return "true"
                          String requestHeader = requestEntity.getHeaders().getFirst("custom");
                        //we are just making sure the header is the one sent from the client
                         assertTrue(Boolean.parseBoolean(requestHeader));

                         Person person = requestEntity.getBody();
                         Hospital hospital = hospitalManager.
                              findByCode(person.getHospital().getCode());
                         person.setHospital(hospital);
                         Person newPerson = personManager.save(person);

                         HttpHeaders responseHeaders = new HttpHeaders();
                         responseHeaders.set("Location",
                                getLocationForPersonResource(originalUrl, person.getId()));
                         return new ResponseEntity<>(newPerson, responseHeaders,
                             HttpStatus.CREATED);
               }

!  As with @RequestBody and @ResponseBody, Spring uses HttpMessageConverter<T> to convert to and from the request and response streams. The HttpMessageConverter<T> and supported implementations were covered in the “RESTful Applications Using Spring MVC” section.

Asynchronous REST Services Using @Async Annotated Methods

The “Asynchronous REST Calls” section showed how to make an asynchronous REST call using the AsyncRestTemplate class. In that case, the client did the rest call and could then focus on other operations until the Future object returned the concrete data.

But asynchronous calls can be made in a different way using @Async annotated methods. This annotation marks a method as a candidate for asynchronous execution. It can also be used at type level; in this case, all methods in the class are considered asynchronous. Asynchronous methods can have any signature and any parameter types. There are absolutely no restrictions about this. However, the return type is restricted to void and Future (and implementations of this interface). Immediately after a client calls an asynchronous method, the invocation returns and the execution of the method is submitted to a Spring TaskExecutor12. Asynchronous methods that return void are used when the client does not expect a reply.

By default, to execute a method annotated with @Async, the executor that is used is the one supplied to the <task:annotation-driven/> element. (The Spring Task namespace was introduced in Spring 3.0 to help configure TaskExecutor and TaskScheduler instances.)

<task:annotation-driven executor="prExecutor"/>
<task:executor id="prExecutor" pool-size="100"/>

In Java Configuration, support for @Async can be enabled using @EnableAsync in one of the configuration classes of the application—those annotated with @Configuration. To provide a different executor, like in the preceding XML example, the class must implement org.springframework.scheduling.annotation.AsyncConfigurer and provide a concrete implementation for the getAsyncExecutor method.

@Configuration
@EnableAsync
 public class AppConfig implements AsyncConfigurer {

      @Override
      public Executor getAsyncExecutor() {
          ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
          executor.setCorePoolSize(100);
          executor.initialize();
          return executor;
      }
      ...
}

Also the @Async annotation has a value attribute to indicate that an executor other than the default should be used when the executor13 is defined as a bean:

@Async("otherExecutor")
public Future<Person> findPerson(Long id) throws InterruptedException {
    String url = "http://localhost:8080/mvc-rest/rest-persons/id/{id}";
    Person person = restTemplate.getForObject(url, Person.class, "1");
    Thread.sleep(1000L);
    return new AsyncResult<>(person);
}

<!-- in a spring configuration file we define an Executor bean -->
  <bean id="otherExecutor"
         class="o.s.scheduling.concurrent.ThreadPoolTaskExecutor"
         init-method="initialize" destroy-method="shutdown">
         <property name="corePoolSize" value="100"/>
  </bean>

// in a class annotated with @Configuration
@Bean(name="otherExecutor", destroyMethod = "shutdown",
      initMethod = "initialize")
     ThreadPoolTaskExecutor getExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         executor.setCorePoolSize(100);
         return executor;
     }

!  An example of an @Async annotated method and usage can be found in 07-pr-rest-solution. The example is covered in the “Practical Exercise” section.

Intercepting REST Calls

There is a section in Chapter 3 about handler interceptors for controller methods, which mentions that REST requests can be intercepted too, but the REST interceptors have to implement the ResponseBodyAdvice<T> or extend one of its subclasses and provide the proper implementation for the beforeBodyWrite and supports.

When extending JsonViewResponseBodyAdvice or AbstractMappingJacksonResponseBodyAdvice, the beforeBodyWriteInternal method must be implemented, because the AbstractMappingJacksonResponseBodyAdvice class provides a concrete implementation for beforeBodyWrite, which calls beforeBodyWriteInternal after creating a proper JSON body container. ResponseBodyAdvice<T> implementation allows you to customize the response after the execution of a @ResponseBody or a ResponseEntity<T> method, but before being passed for conversion to an HTTP message converter. These interceptors are annotated with @ControllerAdvice and are automatically picked up and used by Spring.

In the 07-pr-rest-solution module, such an interceptor is implemented for you:

@ControllerAdvice(basePackages = "com.pr.rest")
//this interceptor is retricted to the classes in package "com.pr.rest"
public class AuditRestInterceptor
    extends JsonViewResponseBodyAdvice {
    private Logger logger = LoggerFactory.getLogger(AuditRestInterceptor.class);

   @Override
   public boolean supports(MethodParameter returnType, Class converterType) {
        logger.info("-----> Audit REST interceptor supports(Person.class) ? "
              + Person.class.isAssignableFrom(returnType.getParameterType()));
        return (super.supports(returnType, converterType)
                && returnType.getMethodAnnotation(JsonView.class) != null);

   }

....
}

The supports method tests if the AuditRestInterceptor supports the given controller method return type and the selected HttpMessageConverter<T> type.

The value logged in the preceding supports method implementation is true if the controller method return type is assignable to a reference of type Person.

  @ControllerAdvice(basePackages = "com.pr.rest")
//this interceptor is retricted to the classes in package "com.pr.rest"
public class AuditRestInterceptor
    extends JsonViewResponseBodyAdvice {
    private Logger logger = LoggerFactory.getLogger(AuditRestInterceptor.class);
    ...

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer,
           MediaType contentType, MethodParameter returnType,
           ServerHttpRequest request, ServerHttpResponse response) {
        logger.info("-----> Audit REST interceptor beforeBodyWrite");
         response.getHeaders().add(HttpHeaders.CONTENT_ENCODING, "UTF-8");
       super.beforeBodyWriteInternal(bodyContainer, contentType, returnType,
             request, response);
    }
}

In the beforeBodyWriteInternal, the CONTENT_ENCODING header is added to the response, so the presence of this header can be tested in the client and you can make sure that the interceptor did its job. After that, the super.beforeBodyWriteInternal() is called to keep the original behavior of the extended class, which is to modify the response body before being converted and sent back to the client.

You see this interceptor in action when testing your REST services, because the log messages are printed in the log console.

INFO c.p.r.AuditRestInterceptor - -->
      Audit REST interceptor supportsPerson.class ? true
INFO c.p.r.AuditRestInterceptor - --> Audit REST interceptor beforeBodyWrite

Using Spring HATEOAS

HATEOAS and Spring HATEOAS project were mentioned at the beginning of the chapter. When the Hypermedia REST constrains are respected by a REST service, it is said that the service is a Hypermedia Driven REST web service. Hypermedia is quite important for REST, because it allows you to build services that are almost fully decoupled from their clients. The representations returned by the REST services contain links that indicate further locations of resources that the client needs access to.

To build a Hypermedia Driven REST web service with Spring, the spring-hateoas dependency must be added to the project. The current version of spring-hateoas is 0.17.0.RELEASE. This library (it is only one jar currently, but it will probably grow into a framework) provides a set of classes used to generate resource URIs. It also provides classes to decorate representations with links to return to the HATEOAS complying client.

In this chapter’s examples, the Person class is wrapped inside a PersonHateoas class that extends the core class of spring-hateoas: ResourceSupport. This class provides methods useful to add links to representations and to access representations links. The PersonHateoas looks like this:

...
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.hateoas.ResourceSupport;

public class PersonHateoas extends ResourceSupport {

     private Person person;

     @JsonCreator
     public PersonHateoas(@JsonProperty("person") Person person) {
         this.person = person;
     }

     public Person getPerson() {
     return person;
    }
}

The PersonHateoas class has a field of type Person. By extending class ResourceSupport, methods to generate HATEOAS links and references are inherited. When requesting a Person resource from a HATEOAS REST service, a PersonHateoas is serialized and sent to the client. When the serialization is done in JSON format, some specific JSON annotations are needed when declaring the PersonHateoas class. The @JsonProperty specifies that at serialization time, the resulted object will contain a property named person that will be mapped to a serialized version of the Person instance. Looks like a simple POJO, right? Well, that’s what it is.

A controller that returns an instance of PersonHateoas must define handler methods that populate the PersonHateoas instances with HAREOAS-specific links. In order to do, Spring offers utility methods that allow you to create links by pointing to controller classes, which are grouped under the ControllerLinkBuilder. The controller and the method do nothing special, except that before returning the response, the personHateoas object is populated with its own URI, using utility methods from the ControllerLinkBuilder class that link together in a very readable way. For example, the underlined code snippet in the previous example can be read like this: Add link to which the handler method getPersonHateoasById from the PersonHateoasController class is mapped, with PathVariable id equal to person.getId() to the personHateoas ph instance. The sources for spring-hateoas are available on GitHub at https://github.com/spring-projects/spring-hateoas.

In the following code snippet, the linkTo and methodOn methods from ControllerLinkBuilder are statically imported and used to generate the resource link for the Person instance with id=1.

...
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

@RestController
@RequestMapping(value = "/hateoas")
public class PersonHateoasController {

private Logger logger = LoggerFactory.getLogger(PersonHateoasController.class);

@ResponseStatus(HttpStatus.OK)
@RequestMapping(value = "/{id}", method = RequestMethod.GET,
      produces = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<PersonHateoas> getPersonHateoasById(
      @PathVariable Long id) throws NotFoundException {
     logger.info("-----> PERSON: " + id);
     Person person = personManager.findById(id);
     if (person == null) {
         throw new NotFoundException(Person.class, id.toString());
     }
     PersonHateoas ph = new PersonHateoas(person);
     ph.add(
            linkTo(
                 methodOn(PersonHateoasController.class)
                       .getPersonHateoasById(person.getId())
            ).withSelfRel()
       );
     return new ResponseEntity<>(ph, HttpStatus.OK);
    }
}

In the previous example, the controller class is the one that takes care of setting the links by inspecting the mappings. But Spring provides another way—by using EntityLinks implementations. To use them, the controller class must be annotated with @ExposesResourcesFor, which makes EntityLinks available by dependency injection. Also, the configuration class must be annotated with @EnableEntityLinks. The EntityLinks interface API exposes methods to access links pointing to controllers backing an entity type. So the controller becomes this:

...
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.ExposesResourceFor;
@Controller
@ExposesResourceFor(Person.class)
@RequestMapping("/hateoas")
public class PersonHateoasController extends BaseController {
    private Logger logger = LoggerFactory.getLogger(PersonHateoasController.class);

    @Autowired
    private EntityLinks entityLinks;

    @RequestMapping(value = "/{id}", method = RequestMethod.GET,
              produces = "application/hal+json")
    public HttpEntity<PersonHateoas> getPersonHateoasById
       (@PathVariable Long id) throws NotFoundException {
       logger.info("-----> PERSON: " + id);
       Person person = personManager.findById(id);
       if (person == null) {
           throw new NotFoundException(Person.class, id.toString());
       }
       PersonHateoas ph = new PersonHateoas(person);

       ph.add(entityLinks.linkForSingleResource(Person.class, id).withSelfRel());
       return new ResponseEntity<>(ph, HttpStatus.OK);
    }
}

And the @EnableEntityLinks annotation is added to the configuration class. Also, to enable HAL support, the EnableHypermediaSupport should be added to the configuration class too.

import org.springframework.hateoas.config.EnableEntityLinks;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
...
@EnableEntityLinks
@EnableHypermediaSupport(type= {HypermediaType.HAL})
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.pr, com.pr.web, com.pr.rest, com.pr.hateoas"})
@ImportResource({"classpath:spring/app-service-config.xml",
  "classpath:spring/db-config.xml"})
public class WebConfig extends WebMvcConfigurerAdapter {
....
}

The full documentation for spring-hateoas can be found at http://docs.spring.io/spring-hateoas/docs/current/reference/html/.

The Hypermedia Driven REST web service that was just created can be tested with restTemplate, just like any REST service. But to deserialize the HATEOAS links correctly, a custom MappingJackson2HttpMessageConverter must be set for the restTemplate. The ObjectMapper must also be customized to register the Jackson2HalModule implementation provided by Spring HATEOAS.

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.hal.Jackson2HalModule;
...
public class PersonHateoasControllerTest {

      @Test
      public void getHateoasPerson() throws Exception {
          ObjectMapper mapper = new ObjectMapper();
          mapper.configure
                   (DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
          mapper.registerModule(new Jackson2HalModule());

          MappingJackson2HttpMessageConverter
             converter = new MappingJackson2HttpMessageConverter();
          converter.setSupportedMediaTypes(
                  MediaType.parseMediaTypes("application/hal+json"));
          converter.setObjectMapper(mapper);

          RestTemplate restTemplate = new RestTemplate(
                  Collections.<HttpMessageConverter<?>> singletonList(converter));

          String url = "http://localhost:8080/mvc-rest/hateoas/{id}";

          ResponseEntity<PersonHateoas> responseEntity =
                  restTemplate.getForEntity(url, PersonHateoas.class, "1");
          PersonHateoas personHateoas = responseEntity.getBody();

          assertNotNull(personHateoas);
          assertTrue(personHateoas.hasLinks());
          assertEquals("http://localhost:8080/mvc-rest/hateoas/1",
                         ppersonHateoas.getLink("self").getHref());
          assertEquals("John", personHateoas.getPerson().getFirstName());
          assertEquals("Smith", personHateoas.getPerson().getLastName());
    }
}

And the response sent to the client will look like this:

{"person":
    {"firstName":"John",
      "middleName":null,
      "lastName":"Smith",
      "dateOfBirth":"1935-10-01",
      "gender":"MALE","
      "hospital":{...},
      "identityCard":{...},
      " links":{"self":{"href":"http://localhost:8080/mvc-rest/hateoas/1"}}
  }

!  The hospital and identityCard objects are not displayed in the previous example, as their contents are not relevant for it. The content of those properties represents the JSON serialization of the hospital and identityCard fields specific to the Person instance. Their contents are displayed in Figure 5-15.

The response body contains two properties: "person" and "links". The "person" property value is the JSON representation of the Person instance with id=1. The "links" property contains a link and its meaning. The "rel":"self" tells the client that the link points to the current resource. In this chapter, the fundamentals of creating and consuming RESTful services with Spring MVC were covered, which is enough for the certification exam.

Summary

After reading this chapter, you should have a proper understanding of how Spring can be used to provide and consume REST services. Here is a simple list of topics that you should keep handy when reviewing your acquired knowledge:

  • What is REST?
  • What type of clients can access a web application?
  • How are resources exposed to the client?
  • How many types of representations are supported?
  • What is the difference between @Controller and @RestController?
  • Make sure that you can describe Spring MVC support for RESTful applications.
  • Understand how to access request/response data.
  • Use message converters.
  • How is asynchronous REST supported?
  • What is HATEOAS?
  • How do you build a HATEOAS complying service with Spring HATEOAS and MVC?

Quick Quiz

Question 1: What is REST?

  1. a software design pattern
  2. a framework
  3. an architecture style

Question 2: Which of the following methods are HTTP methods?

  1. PUT
  2. GET
  3. SUBMIT
  4. OPTIONS

Question 3: What Spring class can be used to access and test REST services?

  1. RestTemplate
  2. AsyncRestTemplate
  3. Both
  4. None

Question 4: What does the RestTemplate handle?

  1. Resources
  2. Representations
  3. Both

Question 5: What can be said about the @RestController annotation?

  1. It is used to declare a controller providing REST services.
  2. Is annotated with @Controller and @ResponseBody.
  3. Controller methods annotated with @RequestMapping assume @ResponseStatus semantics by default when the controller is annotated with @RestController.

Question 6: What is the effect of annotating a method with @ResponseStatus?

  1. The default behavior for resolving to a view for methods returning void or null is overridden.
  2. The HTTP status code matching the @ResponseStatus is added to the response body.
  3. It forces usage of HTTP message converters.

Question 7: Which of the following HTTP message converters are supported by Spring MVC?

  1. StringHttpMessageConverter
  2. MappingJackson2HttpMessageConverter, but Jackson2 must be in the classpath
  3. YamlMessageConverter

Question 8: Which of the following RestTemplates can be used to make a GET REST call to a URL?

  1. restTemplate.getForObject(...)
  2. optionsForAllow(...)
  3. getForEntity(...)
  4. exchange(..., HttpMethod.GET,...)

Question 9: Does the following REST handler method comply with the HATEOAS constraint?

@ResponseStatus(HttpStatus.CREATED)
@RequestMapping(value = "/create", method = RequestMethod.POST,
    produces = MediaType.APPLICATION_JSON_VALUE,
    consumes = MediaType.APPLICATION_JSON_VALUE)
    public Person createPerson(@RequestBody @Valid Person newPerson) {
    logger.info("-----> CREATE");
    Hospital hospital = hospitalManager.findByCode(
          newPerson.getHospital().getCode());
    newPerson.setHospital(hospital);
    Person person = personManager.save(newPerson);
    logger.info("-----> PERSON: " + person);
    return person;
}
  1. Yes, because it returns a representation of the object that was created.
  2. No, because it does not set the location header to the URI of the created resource.
  3. This is not a REST handler method.
  4. No, because a Link object is not added to the returned resource.

Practical Exercise

The practical exercises for this chapter require you to develop some REST client test methods to check your understanding of implementing RESTful application with Spring MVC. The project module is named 07-pr-rest-practice. An analogous module with proposed solutions exists, which is named 07-pr-rest-solution. The projects and their TODOs are shown in Figure 5-10.

9781484208090_Fig05-10.jpg

Figure 5-10. Projects associated with this chapter

The project is split into packages that contain classes grouped by purpose:

  • com.pr.config contains the Java Configuration class used to configure the application.
  • com.pr.hateoas contains classes that describe a hypermedia-driven REST web service
  • com.pr.problem contains classes that handle the exceptions thrown in the application.

    –   GlobalExceptionHandler handles exceptions thrown by methods in the controllers under the com.pr.web package. The restriction is done using @ControllerAdvice(basePackages = "com.pr.web").

    –   NotFoundException is a type of exception thrown when a resource cannot be found.

    –   RestExceptionProcessor handles exceptions thrown by methods in the REST controllers under the com.pr.rest package.

  • com.pr.rest contains classes that implement REST services and interceptors.

    –   AuditRestInterceptor is an interceptor for REST services that prints simple messages and adds a header to the response before it is written.

    –   HospitalsRestController is a REST controller for managing Hospital resources.

    –   PersonsRestController is a REST controller for managing Person resources.

  • com.pr.web contains the web controllers that receive requests from a browser and return views. The structure of the project is depicted in Figure 5-11.

9781484208090_Fig05-11.jpg

Figure 5-11. Package organization of the 07-pr-rest-practice project module

The tests for the rest controllers are located under the same packages as the controllers being tested. The only exception is the com.pr.async that contains a configuration class, a service class, and a test class used to test an asynchronous REST method annotated with @Async. You have no TODO tasks in this package; the example is simply provided for you to run it and see how an REST asynchronous is made.

All the TODO tasks are in the RestPersonControllerTest class. They cover GET, POST, and DELETE operations.

The practical exercise for this chapter requires Gradle tasks to be run in parallel, because the REST tests require the web application to be started. To do this, you have to create an Intellij IDEA Gradle launcher to start the application, and another to stop it. The test cases are run by right-clicking the method you want to execute, and then selecting Run from the menu that appears.

To create a Gradle launcher, you have to do the following:

  1. In the Gradle Task view, right-click the appStart task. A menu is displayed. Select Create personal-records:07-pr-rest-practice.
  2. In the popup check the Single instance only check box. Modify the name to something more relevant, like mvc-rest-start.
  3. Click Apply, and then OK. Your launcher should be available in the Intellij IDEA launcher menu.

The flow for creating a Gradle launcher is depicted in Figure 5-12. Do the same to create a launcher for the appStop task.

9781484208090_Fig05-12.jpg

Figure 5-12. Creating a Gradle launcher

Then from the launcher menu, select the mvc-rest-start launcher and start the application. If the application starts correctly, you should see in the console the following log:

  INFO    Jetty 9.2.10.v20150310 started and listening on port 8080
  INFO    mvc-rest runs at:
  INFO     http://localhost:8080/mvc-rest
Run 'gradle appStop' to stop the server.

Open the link in a browser. You should see the page shown in Figure 5-13.

9781484208090_Fig05-13.jpg

Figure 5-13. The mvc-rest web application

The web application will help you verify that your REST requests have executed correctly.

Once you have the web application up, you can go ahead and try to solve the TODO tasks. There are eight TODOs, numbered from 15 to 22, that require you to perform certain types of REST requests.

Image !  GET requests can be done directly in the browser, so if you want to get creative with GET REST handler methods, you can test them in a browser. For example, try to access http://localhost:8080/mvc-rest/rest-persons/id/1. You should see a JSON reply like the one depicted in the Figure 5-14.

9781484208090_Fig05-14.jpg

Figure 5-14. JSON response for a GET REST request

To format JSON representations properly for display, a @MappingJackson2HttpMessageConverter bean has to be defined and configured accordingly. There are two ways of doing this:

  • Call setPrettyPrint on the @MappingJackson2HttpMessageConverter
    @Bean
    public MappingJackson2HttpMessageConverter
                mappingJackson2HttpMessageConverter() {
         MappingJackson2HttpMessageConverter converter
                          = new MappingJackson2HttpMessageConverter();
         converter.setObjectMapper(objectMapper());
         converter.setPrettyPrint(true);
         return converter;
    }

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
  • Enable the indentation of the serialization output by calling enable on the objectMapper set for the @MappingJackson2HttpMessageConverter bean
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objMapper = new ObjectMapper();
        objMapper.enable(SerializationFeature.INDENT_OUTPUT);
        return objMapper;
    }

Image !  POST and DELETE requests can be tested using a Firefox plugin called Poster.14 Figure 5-15 shows a REST POST request and response done with Poster.

9781484208090_Fig05-15.jpg

Figure 5-15. POST REST request and response done with Poster. You have to copy and paste the RequestBody into the Poster content text area

1Although REST seems strongly connected to HTTP, REST principles can be followed using other protocols too, for example: POP, IMAP, and any protocol that uses URL-like paths and supports GET and POST methods.

2Servers should be cautious about depending on URI lengths above 255 bytes, because some older client or proxy implementations may not properly support these lengths. When a browser does not support a certain request length, a 400 (Bad Request) status code is returned.

3You can read more about ETags at http://en.wikipedia.org/wiki/HTTP_ETag.

4This blog is at http://roy.gbiv.com.

5Martin Fowler has a great article on this at http://martinfowler.com/articles/richardsonMaturityModel.html.

6The project official page is at http://projects.spring.io/spring-hateoas/.

7This is at http://stateless.co/hal_specification.html.

8Javadoc for this class can be found at http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/web/client/RestTemplate.html.

9Multiple methods with the same name, but different signatures are provided. Just check out the Spring API for RestTemplate at http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/web/client/RestTemplate.html.

10The class is part of the Apache http-client library. JavaDoc API can be accessed at http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/ conn/PoolingClientConnectionManager.html.

11See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes.

12Spring’s TaskExecutor interface is equivalent to the java.util.concurrent.Executor interface and extends it without modifying the API in order for clients to declare a dependency on an executor and receive any TaskExecutor implementation. It was created to remove the need for Java libraries when using thread pools.

13You can see all methods available for a ThreadPoolTaskExecutor at http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html.

14The plugin can be found at https://addons.mozilla.org/en-US/firefox/addon/poster/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.131.47