Search This Blog

Monday, March 2, 2009

Sub-Resources with Jersey,Spring, jax-rs

A simple example on how to use Sub-Resources with Jersey and Spring.

For the sake of discussion, consider a Resource /orders. One would typically POST to the resource to create an Order, update the Order at the Resource /orders/{id}, GET the Resource from the URI /orders/{id} and also delete the order at the Resource /orders/{id}.

One can therefore state that /orders/{id} is a Sub-Resource of /orders.

In jax-rs, sub-resources are obtained from the parent resource. A parent resource will not define a HTTP method for the sub-resource, but instead simple have a @Path annotation indicating a sub-resource.

For example,


@Path("/orders")
@Component

@Scope("singleton")
public class OrdersResource {
@Context

private ResourceContext resourceContext;

@Path("/{id}")
public OrderResource subResource(@PathParam("id") String id) {

OrderResource resource = resourceContext.getResource(OrderResource.class);

resource.setOrderId(Long.parseLong(id));

return resource;

}
...
}



In the above example, we have an OrdersResource that services the URI pattern of /orders. Calls to /orders/{id} are delegated to a sub-resource of the OrdersResource called OrderResource as shown below:


@PerRequest
@Component
@Scope("prototype")

public class OrderResource {
private Long orderId;
// Jersey injected

@Context
private ResourceContext resourceContext;

// Spring injected
@Autowired

private OrderService orderService;
@Autowired
private MapperIF beanMapper;

....
public void setOrderId(Long orderId) {
this.orderId = orderId;

}

// @Context are Jersey injected

@GET
@Produces("application/xml")

public Response getOrder(@Context HttpHeaders hh,
@Context Request request) { ... }

@Consumes("application/xml")
@PUT
public void updateOrder(@PathParam("id") String id, OrderDto orderDto) {

....
}

@DELETE
public void deleteOrder(@PathParam("id") String id) {

....
}

@Path("/lineItems")
public LineItemListDto getLineItems() throws OrderNotFoundException {

.....
}
}



Both the above mentioned Resources are beautifully managed by Spring and Jersey interoperability. Jersey Resources are injected by Jersey and Spring resources are injected by Spring auto-wiring. So cool!!!!

As shown above the OrderResource is following a Prototype pattern and is obtained via a called to resourceContext.getResource(OrderResource.class).

I quite like the fact that the OrderResource is declared as sub-resource of the OrdersResource and the creation of the Sub-Resource via the prototype pattern matches so beautifully within jax-rs. It becomes a trivial exercise to trace the flow of a uri-call from the parent resource to sub-resources.

5 comments:

Wingyiu Lee said...

// Spring injected
@Autowired
private OrderService orderService;

Should the OrderService be "
@Component
@Scope("prototype")"?

I have these "
AutoWired
UserDao userDao;"
in my codes, but the UserDao can't be injected.What problem?

Sanjay Acharya said...

OrderService is a singleton. Is your scanner scanning for the package for UserDao, does UserDao have @Component?

Sangmoon Oh said...

Excellent work!
Thank you for your nice work and explanation. I have tried to find out the way to handle sub-resouces with Spring for two days. But I coudln't find it. I think I should google and find this blog.

Anonymous said...

Is this still valid for resource/sub-resource relationships? It's an old article,

Brill Pappin said...

In your example, there is a slightly better way to do it, in that you don't specify the {id} on the sub-resource. You add it to the path of the sub-resource, *in* the sub-resource where it belongs.

This means that the paths move down a level, with the OrderResource having a @GET @Path("/{id}") annotated method. This has a few advantages, primary being that the {id} isn't passed in as a property, it's simply part fo the resource path of the sub-resource. Another advantage is that its easy to version your root now, without removing the old versions. you would simply return the same OrderResource in /v2 if hadn't changes, and a new OrderResource in /3 if it does. (especially useful in mobile endpoints)

@Path("/v1")
@Component

@Scope("singleton")
public class OrdersResource {
@Context

private ResourceContext resourceContext;

@Path("/orders")
public OrderResource subResource() {
return resourceContext.getResource(OrderResource.class);
}
...
}