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:
- Sub-resource method—directly implements an HTTP verb for a sub-resource and is decorated with one of the annotations described in the section called “Specifying HTTP verbs”.
- Sub-resource locator—points to a class that implements the sub-resource.
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:
- 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. - Repeat the previous step until the root resource is reached.
- 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 HTTPGET
requests for resources whose URI matches /customerservice/orders/{orderId}/.updateOrder()
handles HTTPPUT
requests for resources whose URI matches /customerservice/orders/{orderId}/.newOrder()
handles HTTPPOST
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.