Examples for Chapter 4 - JAX-RS Workbook - RESTful Java with JAX-RS 2.0 (2013)

RESTful Java with JAX-RS 2.0 (2013)

Part II. JAX-RS Workbook

Chapter 19. Examples for Chapter 4

Chapter 4 discussed three things. First, it mentioned how the @javax.ws.rs.HttpMethod annotation works and how to define and bind Java methods to new HTTP methods. Next, it talked about the intricacies of the @Path annotation, and explained how you can use complex regular expressions to define your application’s published URIs. Finally, the chapter went over the concept of subresource locators.

This chapter walks you through three different example programs that you can build and run to illustrate the concepts in Chapter 4. The first example uses @HttpMethod to define a new HTTP method called PATCH. The second example expands on the customer service database example from Chapter 18 by adding some funky regular expression mappings with @Path. The third example implements the subresource locator example shown in Full Dynamic Dispatching in Chapter 4.

Example ex04_1: HTTP Method Extension

This example shows you how your JAX-RS services can consume HTTP methods other than the common standard ones defined in HTTP 1.1. Specifically, the example implements the PATCH method. The PATCH method was originally mentioned in an earlier draft version of the HTTP 1.1 specification:[21]

The PATCH method is similar to PUT except that the entity contains a list of differences between the original version of the resource identified by the Request-URI and the desired content of the resource after the PATCH action has been applied.

The idea of PATCH is that instead of transmitting the entire representation of a resource to update it, you only have to provide a partial representation in your update request. PUT requires that you transmit the entire representation, so the original plan was to include PATCH for scenarios where sending everything is not optimal.

Build and Run the Example Program

Perform the following steps:

1. Open a command prompt or shell terminal and change to the ex04_1 directory of the workbook example code.

2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.

3. Perform the build and run the example by typing mvn install.

The Server Code

Using PATCH within JAX-RS is very simple. The source code under the ex04_1 directory contains a simple annotation that implements PATCH:

src/main/java/org/ieft/annotations/PATCH.java

package org.ieft.annotations;
 
import javax.ws.rs.HttpMethod;
import java.lang.annotation.*;
 
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH
{
}

As described in Chapter 4, all you need to do to use a custom HTTP method is annotate an annotation class with @javax.ws.rs.HttpMethod. This @HttpMethod declaration must contain the value of the new HTTP method you are defining.

To illustrate the use of our new @PATCH annotation, I expanded a little bit on the example code discussed in Chapter 18. A simple JAX-RS method is added to the CustomerResource class that can handle PATCH requests:

src/main/java/com/restfully/shop/services/CustomerResource.java

package com.restfully.shop.services;
 
@Path("/customers")
public class CustomerResource {
...
 
   @PATCH
   @Path("{id}")
   @Consumes("application/xml")
   public void patchCustomer(@PathParam("id") int id, InputStream is)
   {
      updateCustomer(id, is);
   }
...
}

The @PATCH annotation is used on the patchCustomer() method. The implementation of this method simply delegates to the original updateCustomer() method.

The Client Code

The client code for ex04_1 is pretty straightforward and similar to ex03_1. Let’s look at some initial minor changes we’ve made:

src/test/java/com/restfully/shop/test/PatchTest.java

package com.restfully.shop.test;
 
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
 
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
 
 
/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class PatchTest
{
   private static Client client;
 
   @BeforeClass
   public static void initClient()
   {
      client = ClientBuilder.newClient();
   }
 
   @AfterClass
   public static void closeClient()
   {
      client.close();
   }

First, we initialize our Client object within a JUNit @BeforeClass block. Any static method you annotate with @BeforeClass in JUnit will be executed once before all @Test methods are executed. So, in the initClient() method we initialize an instance of Client. Static methods annotated with @AfterClass are executed once after all @Test methods have run. The closeClient() method cleans up our Client object by invoking close() after all tests have run. This is a nice way of putting repetitive initialization and cleanup code that is needed for each test in one place.

The rest of the class is pretty straightforward and similar to ex03_1. I’ll highlight only the interesting parts:

      String patchCustomer = "<customer>"
              + "<first-name>William</first-name>"
              + "</customer>";
      response = client.target(location)
                       .request().method("PATCH", Entity.xml(patchCustomer));
      if (response.getStatus() != 204)
          throw new RuntimeException("Failed to update");
      response.close();

To make a PATCH HTTP invocation, we use the javax.ws.rs.client.SyncInvoker.method() method. The parameters to this method are a string denoting the HTTP method you want to invoke and the entity you want to pass as the message body. Simple as that.

Example ex04_2: @Path with Expressions

For this section, I’ll illustrate the use of an @Path annotation with regular expressions. The example is a direct copy of the code in ex03_1 with a few minor modifications.

Build and Run the Example Program

Perform the following steps:

1. Open a command prompt or shell terminal and change to the ex04_2 directory of the workbook example code.

2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.

3. Perform the build and run the example by typing maven install.

The Server Code

The CustomerResource class copied from the ex03_1 example is pretty much the same in ex04_2, except that a few of the @Path expressions have been modified. I also added an extra method that allows you to reference customers by their first and last names within the URL path:

@Path("/customers")
public class CustomerResource {
...
 
   @GET
   @Path("{id : \\d+}")
   @Produces("application/xml")
   public StreamingOutput getCustomer(@PathParam("id") int id)
   {
      ...
   }
 
   @PUT
   @Path("{id : \\d+}")
   @Consumes("application/xml")
   public void updateCustomer(@PathParam("id") int id, InputStream is)
   {
     ...
   }

The @Path expression for getCustomer() and updateCustomer() was changed a little bit to use a Java regular expression for the URI matching. The expression dictates that the id segment of the URI can only be a string of digits. So, /customers/333 is a legal URI, but/customers/a32ab would result in a 404, “Not Found,” response code being returned to the client:

   @GET
   @Path("{first : [a-zA-Z]+}-{last:[a-zA-Z]+}")
   @Produces("application/xml")
   public StreamingOutput getCustomerFirstLast(
                                   @PathParam("first") String first,
                                   @PathParam("last") String last)
   {
     ...
   }

To show a more complex regular expression, I added the getCustomerFirstLast() method to the resource class. This method provides a URI pointing to a specific customer, using the customer’s first and last names instead of a numeric ID. This @Path expression matches a string of the first name and last name separated by a hyphen character. A legal URI is /customers/Bill-Burke. The name can only have letters within it, so /customers/Bill7-Burke would result in a 404, “Not Found,” being returned to the client.

The Client Code

The client code is in src/test/java/com/restfully/shop/test/ClientResourceTest.java. It is really not much different than the code in example ex03_1, other than the fact that it additionally invokes the URI represented by the getCustomerFirstLast() method. If you’ve examined the code from Chapter 18, you can probably understand what is going on in this client example, so I won’t elaborate further.

Example ex04_3: Subresource Locators

The ex04_3 example implements the subresource locator example shown in Full Dynamic Dispatching in Chapter 4.

Build and Run the Example Program

Perform the following steps:

1. Open a command prompt or shell terminal and change to the ex04_3 directory of the workbook example code.

2. Make sure your PATH is set up to include both the JDK and Maven, as described in Chapter 17.

3. Perform the build and run the example by typing maven install.

The Server Code

There’s really not much to go over that wasn’t explained in Chapter 4.

The Client Code

The client code lives in src/test/java/com/restfully/shop/test/CustomerResourceTest.java:

public class CustomerResourceTest
{
   @Test
   public void testCustomerResource() throws Exception {
     ...
   }
 
   @Test
   public void testFirstLastCustomerResource() throws Exception {
     ...
   }
}

The code contains two methods: testCustomerResource() and testFirstLastCustomerResource().

The testCustomerResource() method first performs a POST to /customers/europe-db to create a customer using the CustomerResource subresource. It then retrieves the created customer using GET /customers/europe-db/1.

The testFirstLastCustomerResource() method performs a POST to /customers/northamerica-db to create a customer using the FirstLastCustomerResource subresource. It then uses GET /customers/northamerica-db/Bill-Burke to retrieve the created customer.


[21] For more information, see http://www.ietf.org/rfc/rfc2068.txt.