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.