Skip to main content
Last updated August 30, 2011 18:10, by spericas
Feedicon  

Old Hypermedia / HATEOAS Proposals


Transitional Links (Option 1)


Transitional links enable the HATEOAS principle. As such, they should be the responsibility of the resource (controller) and not the model (or a model's view). Suppose two new annotations are added: @AddTransitionalLinks and @GetTransitionalLinks. The former is used for methods that return instances of the model (or views) whose representation should be extended with transitional links; the latter is used to annotate a method in a resource class that would build all the transitional links. Using the model defined in DeclarativeHyperlinkingInJersey:

@Path("/items/{id}")
public class ItemResource {
    
    private String id;
    private ItemsModel model = ItemsModel.getInstance();
    
    public ItemResource(@PathParam("id") String id) {
        this.id = id;
    }
    @GET 
    @AddTransitionalLinks(Type.HEADERS)
    public ItemModel get() {
        return model.getItem(id);
    }
    @GetTransitionalLinks
    public void getLinks(@Context TransitionalLinks links) {
       links.addLink("self", 
           UriBuilder.fromResource(ItemResource.class).build(id));  
       if (model.hasNext(id)) {
           links.addLink("next",
               UriBuilder.fromResource(ItemResource.class).build(
                   model.getNextId(id)));
       }
       if (model.hasPrev(id)) {
           links.addLink("prev",
               UriBuilder.fromResource(ItemResource.class).build(
                   model.getPrevId(id)));           
       }
    }   
}

In the example above, the get method returns an instance of ItemModel. Because of the presence of @AddTransitionalLinks, the method getLinks is called by the JAX-RS implementation, and the links added to the link headers. Note that the existing UriBuilder class is used to generate all the links.

 

Transitional Links (Option 2)


An alternative to supporting transitional links is to extend the existing API to simplify the generation of link headers, and use UriBuilder as in Option 1. One way to do this is by extending Response and Response.ResponseBuilder to support adding link headers. By adding the method linkHeader(String rel, URI uri), the example above can be re-written as follows:

@Path("/items/{id}")
public class ItemResource2 {

    private String id;
    private ItemsModel model = ItemsModel.getInstance();

    public ItemResource2(@PathParam("id") String id) {
        this.id = id;
    }

    @GET
    public Response get() {
        ResponseBuilder rb = Response.ok(model.getItem(id));   
        addLinks(rb);
        return rb.build();
    }
    
    private void addLinks(ResponseBuilder rb) {
        rb.linkHeader("self",
                UriBuilder.fromResource(ItemResource2.class).build(id));
        if (model.hasNext(id)) {
            rb.linkHeader("next",
                    UriBuilder.fromResource(ItemResource2.class).build(
                    model.getNextId(id)));
        }
        if (model.hasPrev(id)) {
            rb.linkHeader("prev",
                    UriBuilder.fromResource(ItemResource2.class).build(
                    model.getPrevId(id)));
        }        
    }
}

The main advantage of this approach is its simplicity: only a few new methods are added to the API and there's no need for annotations or new semantics. A similar approach is possible using the JAX-RS 2.0 class HttpResponse instead of Response.

Client API Support for Transitional Links


Supporting transitional links in the Client API is fairly simple. The HttpResponse can be extended with a follows method that returns a transitional link given the relation name (throwing an exception if it isn't available). For example, the following code shows how to retrieve the first item and follow its "next" link to retrieve the following item.

// Get item 1
HttpResponse res1 = client.request("http://.../items/{id}").get()
    .pathParam("id", 1).invoke();
ItemModel item1 = res1.getEntity(ItemModel.class);

// Follow item1's "next" relational link to get following item
HttpResponse res2 = res1.follows("next").get().invoke();
ItemModel item2 = res2.getEntity(ItemModel.class);

Related Work



References


 
 
Close
loading
Please Confirm
Close