So far, We have seen how to bind URI patterns using @Path annotation to a specific Java method.This URI binding is static. JAX-RS also provides way to dispatch requests through sub-resource locators dynamically. Sub-resource locators are Java methods annotated with @Path, but with no HTTP method annotation, like @GET, annotated to them. This type of method returns an resource object that is, itself, a JAX-RS annotated service that knows how to process the remainder of the request.
This is best explained using example.
Let's expand our previous example of Product. Till now, we have product resource which serves requests related to products. Now, each products could have review comments. We could add methods to return, add, update, delete review comments in product resource itself however there are few disadvantages with this approach.
First, review comment itself is another resource in its own sense. So having methods related to review comment inside "ProductResource" is not completely correct.
Second, there will be too many methods mapping to each URI for review comments in single "ProductResource" class. What if there are 'likes' to review comments?
We can not keep on adding more methods to same "ProductResource" class and make monolithic design.
This is where sub resource locator helps. In example below, ProductResource is a root resource as this class is annotated with @Path. The getReviewCommentsResource() is sub-resource locator method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package in.blogspot.ashish4java.invoicedetails; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("products") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public class ProductResource { @Path("{productId}/reviewcomments") public final ReviewCommentResource getReviewCommentsResource() { return new ReviewCommentResource(); } } |
ReviewCommentResource class is as below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package in.blogspot.ashish4java.invoicedetails; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; /** * @author admin * This class is for review comment resource * */ public class ReviewCommentResource { /** * This method returns all review comments for particular product. Currently * implementation just returns text with productID appended to it. * * @param productId * the productId for specific product. * @return Response JAX-RS response object. */ @GET public final Response getReviewComments(final @PathParam("productId") int productId) { return Response.status(Status.OK) .entity("This is review comment for product " + productId) .build(); } /** * This method returns particular review comment for particular productId. * Current implementation returns text with reviewCommentId and productId. * * @param reviewCommentId * the reviewCommentId of specific review comment. * @param productId * the productId of specific product. * @return Response JAX-RS response object. */ @GET @Path("{reviewCommentId}") public final Response getReviewComment(final @PathParam("reviewCommentId") int reviewCommentId, final @PathParam("productId") int productId) { return Response.status(Status.OK) .entity("This is review comment for " + reviewCommentId + " and product ID " + productId).build(); } } |
If the client is invoked
GET /webapi/products/4/reviewcomments
The ProductResource.getReviewCommentsResource() will be invoked first. This method locate the sub-resource object which will serve the request and provide it. This request will be dispatched to ReviewCommentsResource.getReviewComments(int productId)
Another example is,
GET /webapi/products/4/reviewcomments/2
In this case, request will be dispatched to ReviewCommentsResource.getReviewComment(int reviewCommentId, int productId)
Another, interesting feature of this is, we can return Object type from sub-resource locator method as well as below,
Another example is,
GET /webapi/products/4/reviewcomments/2
In this case, request will be dispatched to ReviewCommentsResource.getReviewComment(int reviewCommentId, int productId)
Another, interesting feature of this is, we can return Object type from sub-resource locator method as well as below,
1 2 3 4 | @Path("{productId}/reviewcomments") public final Object getReviewCommentsResource() { return new ReviewCommentResource(); } |
and JAX-RS dynamically locate the resource. This is especially really helpful if we need to return resource object based on input parameter. So we can use factory-method design pattern within it to decide which resource needs to be returned.
This is end of tutorial. Till now, we haven't covered examples for @MatrixParam, @QueryParam, @HeaderParam annotations. We have just touched upon how to add 'location' header when adding new product through POST method. This is very helpful in implementing HATEOAS (Hypermedia as the Engine of Application State). We have not seen how to implement JAX-RS client as well till now. I will try to add more tutorials for these examples in future. till then, STAY TUNED!