45.5. Working with sub-resources

Overview

It is likely that a service will need to be handled by more than one resource. For example, in an order processing service best-practices suggests that each customer would be handled as a unique resource. Each order would also be handled as a unique resource.
Using the JAX-RS APIs, you would implement the customer resources and the order resources as sub-resources. A sub-resource is a resource that is accessed through a root resource class. They are defined by adding a @Path annotation to a resource class' method. Sub-resources can be implemented in one of two ways:

Specifying a sub-resource

Sub-resources are specified by decorating a method with the @Path annotation. The URI of the sub-resource is constructed as follows:
  1. Append the value of the sub-resource's @Path annotation to the value of the sub-resource's parent resource's @Path annotation.
    The parent resource's @Path annotation maybe located on a method in a resource class that returns an object of the class containing the sub-resource.
  2. Repeat the previous step until the root resource is reached.
  3. The assembled URI is appended to the base URI at which the service is deployed.
For example the URI of the sub-resource shown in Example 45.6, “Order sub-resource” could be baseURI/customerservice/order/12.

Example 45.6. Order sub-resource

...
@Path("/customerservice/")
public class CustomerService
{
  ...
  @Path("/orders/{orderId}/")
  @GET
  public Order getOrder(@PathParam("orderId") String orderId)
  {
    ...
  }
}

Sub-resource methods

A sub-resource method is decorated with both a @Path annotation and one of the HTTP verb annotations. The sub-resource method is directly responsible for handling a request made on the resource using the specified HTTP verb.
Example 45.7, “Sub-resource methods” shows a resource class with three sub-resource methods:
  • getOrder() handles HTTP GET requests for resources whose URI matches /customerservice/orders/{orderId}/.
  • updateOrder() handles HTTP PUT requests for resources whose URI matches /customerservice/orders/{orderId}/.
  • newOrder() handles HTTP POST requests for the resource at /customerservice/orders/.

Example 45.7. Sub-resource methods

...
@Path("/customerservice/")
public class CustomerService
{
  ...
  @Path("/orders/{orderId}/")
  @GET
  public Order getOrder(@PathParam("orderId") String orderId)
  {
    ...
  }

  @Path("/orders/{orderId}/")
  @PUT
  public Order updateOrder(@PathParam("orderId") String orderId,
                           Order order)
  {
    ...
  }

  @Path("/orders/")
  @POST
  public Order newOrder(Order order)
  {
    ...
  }
}
Note
Sub-resource methods with the same URI template are equivalent to resource class returned by a sub-resource locator.

Sub-resource locators

Sub-resource locators are not decorated with one of the HTTP verb annotations and do not directly handle are request on the sub-resource. Instead, a sub-resource locator returns an instance of a resource class that can handle the request.
In addition to not having an HTTP verb annotation, sub-resource locators also cannot have any entity parameters. All of the parameters used by a sub-resource locator method must use one of the annotations described in Chapter 46, Passing Information into Resource Classes and Methods.
As shown in Example 45.8, “Sub-resource locator returning a specific class”, sub-resource locator allows you to encapsulate a resource as a reusable class instead of putting all of the methods into one super class. The processOrder() method is a sub-resource locator. When a request is made on a URI matching the URI template /orders/{orderId}/ it returns an instance of the Order class. The Order class has methods that are decorated with HTTP verb annotations. A PUT request is handled by the updateOrder() method.

Example 45.8. Sub-resource locator returning a specific class

...
@Path("/customerservice/")
public class CustomerService
{
  ...
  @Path("/orders/{orderId}/")
  public Order processOrder(@PathParam("orderId") String orderId)
  {
    ...
  }

  ...
}

public class Order
{
  ...
  @GET
  public Order getOrder(@PathParam("orderId") String orderId)
  {
    ...
  }

  @PUT
  public Order updateOrder(@PathParam("orderId") String orderId,
                           Order order)
  {
    ...
  }

}
Sub-resource locators are processed at runtime so that they can support polymorphism. The return value of a sub-resource locator can be a generic Object, an abstract class, or the top of a class hierarchy. For example, if your service needed to process both PayPal orders and credit card orders, the processOrder() method's signature from Example 45.8, “Sub-resource locator returning a specific class” could remain unchanged. You would simply need to implement two classes, ppOrder and ccOder, that extended the Order class. The implementation of processOrder() would instantiate the desired implementation of the sub-resource based on what ever logic is required.