Pages

Monday, 13 April 2015

Declarative Hyperlinking using @InjectLink - Part 1


Declarative hyperlinking is one of the features introduced in Jersey 2.x. This feature is powerful, in that it enables you to create hyperlinks on the fly, thereby helping you in making your REST services adhere to HATEOS principle.

Declarative hyperlinking can be achieved using Jersey's @InjectLink annotation. For a detailed understanding on declarative linking and this annotation, refer jersey page.
In this post, I will show you how links can be generated using @InjectLink API. 

The final output will be something like:



The main URI will be available at all times and will enable users to come back to the list of all accounts. To get information about a specific account, a user can invoke the 'view' link associated with the account. Similarly, to update an account, a user can invoke the 'update' link corresponding to that account. The update link is available only if the balance is greater than zero in the account.

Getting started:
In Part 1 of this post, I will show you how to set up your IDE and create the main URI link. In Part 2, I will show how the account specific links are created.
For this demo, I am using Eclipse with tomcat server, but it should work on similar lines in other IDEs.

To begin, create a new dynamic web application. Next, create 3 classes in the application:

  • Accounts.java  – Bean class holding account information, mainly account number, currency, balance. It also has two fields for links - view, update; which will provide URIs to view/update account specific information.

package accountInfo;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Accounts {
    public Accounts() {
        super();
    }

    Accounts(int account_number, int balance, String currency) {
        this.account_number = account_number;
        this.currency = currency;
        this.balance = balance;
    }

    private int account_number;
    private String currency;
    private int balance;

    private String view; //will hold the link to view account details
    private String update; //will hold the link to update account details

    public void setAccount_number(int account_number) {
        this.account_number = account_number;
    }

    public int getAccount_number() {
        return account_number;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public String getCurrency() {
        return currency;
    }

    public void setView(String view) {
        this.view = view;
    }

    public String getView() {
        return view;
    }

    public void setUpdate(String update) {
        this.update = update;
    }

    public String getUpdate() {
        return update;
    }
}

  • AccountsWrapper.java –  wrapper class encapsulates the account info into a list. The main thing to observe in this class is the @InjectLink(resource = AccountService.class, style = Style.ABSOLUTE) 
          Don't worry if you get compilation issues at this stage. The set-up step discussed later will take care of it.

package accountInfo;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import org.glassfish.jersey.linking.InjectLink.*;
import org.glassfish.jersey.linking.InjectLink;

@XmlRootElement
public class AccountWrapper {
    public AccountWrapper() {
        super();
    }

    public AccountWrapper(List<Accounts> account) {
        this.acccount = account;
    }
    
    List<Accounts> acccount = new ArrayList<Accounts>();

    @InjectLink(resource = AccountService.class, style = Style.ABSOLUTE)
    URI uri;

    public void setUri(URI uri) {
        System.out.println(uri);
        this.uri = uri;
    }

    public URI getUri() {
        System.out.println("URI:" + uri);
        return uri;
    }

    public void setAcccount(List<Accounts> acccount) {
        this.acccount = acccount;
    }

    public List<Accounts> getAcccount() {
        return acccount;
    }

}

  • AccountsService.java – the main service class with HTTP verbs and methods.


package accountInfo;

import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("account_info")
public class AccountService {
    private static List<Accounts> accList = null;

    public AccountService() {
        super();
        if(accList == null)
            populateAccInfo();
    }
    /**
     * Get information of all the accounts
     */
    @GET
    @Produces("application/xml")
    public AccountWrapper allAccountsInfo() {
        return new AccountWrapper(accList);
    }

    /**
     * Helper method - to initialize data
     */
    private void populateAccInfo() {
        accList = new ArrayList<Accounts>();
        System.out.println("populate");
        Accounts acc1 = new Accounts();
        acc1.setAccount_number(111);
        acc1.setBalance(20000);
        acc1.setCurrency("$");
        accList.add(acc1);

        Accounts acc2 = new Accounts();
        acc2.setAccount_number(222);
        acc2.setBalance(-400);
        acc2.setCurrency("$");
        accList.add(acc2);
    }
}

The presence of @InjectLink annotation on 'uri' field in AccountWrapper will ensure that every time allAccountsInfo() method returns the AccountWrapper instance, the link is injected and sent along in the 'uri' field. The InjectLink annotation 'resource' attribute specifies that the link has to created by using the @Path annotation defined in AccountService class.
At this stage, the code is ready. However, for declarative hyperlinking to work, we need to set up a few things.

Set-up:
To use declarative linking feature (& if you are not using maven), first you need to download the Jersey 2.x bundle from jersey site. Additionally, you need to download the JAR for declarative linking from maven repo. In eclipse, you can place these JARS under the WEB-INF -> lib folder of your application.


Additionally, you need to register the declarative linking feature in the Application class. Without doing this, injection will not work. For this, create a new java class, say 'MyApplication' which extends 'org.glassfish.jersey.server.ResourceConfig'. In the default constructor, add the lines to register the declarative linking package as below:

package accountInfo;

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.linking.DeclarativeLinkingFeature;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("resources")

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        packages("accountInfo;org.glassfish.jersey.examples.linking").register(DeclarativeLinkingFeature.class);
        System.out.println("In here");
    }
}
Don't forget to include the package which contains your service class (in my case, accountInfo). Also, remember to add the @ApplicationPath annotation on this class.
The app is now ready with the main URI. Run the service class and invoke the allAccountsInfo method to see it in  action.
Look out for Part 2 of this post which talks about individual links.

2 comments: