Pages

Tuesday, 21 April 2015

Declarative Hyperlinking using @InjectLink - Part 2

In my previous post, I talked about the set-up required to use declarative hyperlinking feature provided by Jersey 2.x. This post is a continuation to that post. In this post, we will create links to individual accounts based on the balance amount available in the account.
Let us first update the service to include a GET method, which returns information for a particular account. We also need to add a method that performs POST operation to update account information. For this, go to AccountService.java and add the below code:
    @GET
    @Path("view/{id}")
    @Produces("application/xml")
    public Accounts accountsInfoById(@PathParam("id") int id) {
        for (int i = 0; i < accList.size(); i++) {
            if (accList.get(i).getAccount_number() == id) {
                return accList.get(i);
            }
        }
        return null;
    }


    @POST
    @Path("update/{id}")
    @Consumes("application/xml")
    @Produces("application/xml")
    public AccountWrapper updateAccountById(@PathParam("id") int id, Accounts account) {
        List<Accounts> tempList = new ArrayList<Accounts>();
        Accounts ac = accountsInfoById(id);
        if (ac != null) {
            ac.setBalance(account.getBalance());
            ac.setCurrency(account.getCurrency());
            tempList.add(ac);
            return new AccountWrapper(tempList);
        } else {
            return null;
        }
    }
Next, go to Accounts.java class and add @InjectLink annotations on the view & update fields.

    @InjectLink(resource = AccountService.class, method="accountsInfoById",  bindings ={@Binding(name = "id", value = "${instance.account_number}")},style =  Style.ABSOLUTE)
    private String view;
    
    @InjectLink(resource = AccountService.class, method="updateAccountById",  bindings ={@Binding(name = "id", value = "${instance.account_number}")}, condition = "${instance.balance >= 0}", style =  Style.ABSOLUTE)
    private String update;

Here, we have specified the InjectLink annotation with a resource attribute. In the resource attribute, we are providing the name of the class whose @Path annotation needs to be used to create the URI. The method parameter further refines the URI by providing the name of the method to look within the class. The @Path annotation specified on this method will be appended to the previously created URI. Using bindings attribute, the path parameter values are being specified. Since, in our case, the path parameter maps to the account number, we have specified the value to be account_number. At runtime, the current value of account number will be injected into this.

Run the service.


Below is the navigation to the update operation for account 111 by following the update link.


In this post, I have shown field level injection. It is also possible to perform method level injection. I will probably go through that in another post.

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.