Thursday, September 16, 2010

Java REST client that supports OAuth

About this tutorial

This tutorial describes how to create a simple Zipster API client that supports OAuth using Java programming language.

Objectives

The Zipster API allows anyone to build their own programs using Zipster data, whether they're on the web, the desktop or mobile devices. In this tutorial, you'll be introduced to the following features of the Zipster API:


Prerequisites

The content of this tutorial is geared toward Java programmers. This tutorial assumes that you have:

  • Knowledge of Maven software management tool.
  • Registered your application with Zipster and have the Consumer Key and Consumer Secret.

System requirements

The exercises in this tutorial require a development environment consisting of:


Create a Maven 2 project

Use Maven 2 Archetype Plugin plugin to create a new Maven 2 project from an existing template:

mvn archetype:create \
   -DarchetypeGroupId=org.apache.maven.archetypes \
   -DgroupId=com.example.zipster \
   -DartifactId=zipster

Execute the command to create a new project. Here's the resulting tree of the created project:

C:\>tree zipster
Folder PATH listing
Volume serial number is F06A-8AA3
C:\ZIPSTER
+---src
    +---main
    ¦   +---java
    ¦       +---com
    ¦           +---example
    +---test
        +---java
            +---com
                +---example

Maven 2 also creates a Project Object Model (POM) file, which contains an XML representation of the Maven project. You can find the pom.xml file in the zipster directory.

For this tutorial, you need to add signpost-core and commons-io as the dependencies and ensure Maven compile the project with JDK 1.6 compatible. Edit the pom.xml file to the following:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.example.zipster</groupId>
   <artifactId>zipster</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>zipster</name>
   <url>http://maven.apache.org</url>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3</version>
            <configuration>
               <source>1.6</source>
               <target>1.6</target>
            </configuration>
         </plugin>
      </plugins>
   </build>
   <dependencies>
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>3.8.1</version>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>oauth.signpost</groupId>
         <artifactId>signpost-core</artifactId>
         <version>1.2.1.1</version>
      </dependency>
      <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
         <version>1.4</version>
      </dependency>
   </dependencies>
</project>

Your development environment is ready for action.

Create an application

As per the Zipster API Authentication document, you first need to obtain the access token and access token secret from Zipster. Update the class com.example.zipster.App (under the src folder of zipster project) to the following:

package com.example.zipster;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.basic.DefaultOAuthProvider;

public class App {

   // Your consumer key from Zipster
   private static final String CONSUMER_KEY = "";
   // Your consumer secret from Zipster
   private static final String CONSUMER_SECRET = "";

   private static final String REQUEST_TOKEN_URL = "https://www.zipster.com.au/oauth/request_token";
   private static final String AUTHORISE_URL = "https://www.zipster.com.au/oauth/authorise";
   private static final String ACCESS_TOKEN_URL = "https://www.zipster.com.au/oauth/access_token";

   public static void main(String[] args) throws Exception {
      obtainAccessToken(CONSUMER_KEY, CONSUMER_SECRET);
   }

   private static void obtainAccessToken(String consumerKey, String consumerSecret) throws Exception {
      OAuthConsumer consumer = new DefaultOAuthConsumer(consumerKey, consumerSecret);
      OAuthProvider provider = new DefaultOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORISE_URL);

      System.out.println("Obtaining request token...");

      String authorisationUrl = provider.retrieveRequestToken(consumer, OAuth.OUT_OF_BAND);

      System.out.println("Request token: " + consumer.getToken());
      System.out.println("Request token secret: " + consumer.getTokenSecret());

      System.out.println("Now visit the following Url and authorise this application:\n" + authorisationUrl);
      System.out.println("Enter the verification code:");

      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
      String verificationCode = bufferedReader.readLine();

      System.out.println("Obtaining access token...");

      provider.retrieveAccessToken(consumer, verificationCode);

      System.out.println("Access token: " + consumer.getToken());
      System.out.println("Access token secret: " + consumer.getTokenSecret());
   }
}

Compile the application:

C:\zipster>mvn compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building zipster
[INFO]    task-segment: [compile]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources {execution: default-resources}]
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\zipster\src\main\resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Nothing to compile - all classes are up to date
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Tue Sep 14 08:51:25 EST 2010
[INFO] Final Memory: 5M/15M
[INFO] ------------------------------------------------------------------------

Execute the application, during the application execution, you will be asked to open a URL in a browser so that you can authenticate and authorise the application to have access to your account.

Once you have granted the application access to your account, Zipster will provide you the verification code, which you need to supply to the application to obtain the access token and access token secret.

C:\zipster>mvn exec:java -Dexec.mainClass=com.example.zipster.App
...
[INFO] [exec:java {execution: default-cli}]
Obtaining request token...
Request token: /* request token */
Request token secret: /* request token secret */
Now visit the following Url and authorise this application:
https://www.zipster.com.au/oauth/authorise?oauth_token=/* request token */
Enter the verification code:
/* verification code from Zipster after you have authorised the application */
Obtaining access token...
Access token: /* your access token */
Access token secret: /* your access token secret */
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 51 seconds
[INFO] Finished at: Tue Sep 14 09:02:03 EST 2010
[INFO] Final Memory: 6M/15M
[INFO] ------------------------------------------------------------------------

That's it, now you have the access token and access token secret, which you can store and use to perform actions on behalf of the user.

Update application

Now that you have the access token and access token secret, you can start retrieving the list job categories and a job category from Zipster.

Update the class com.example.zipster.App to the following:

package com.example.zipster;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.basic.DefaultOAuthProvider;
import oauth.signpost.exception.OAuthException;

import org.apache.commons.io.IOUtils;

public class App {

   // Your consumer key from Zipster
   private static final String CONSUMER_KEY = "";
   // Your consumer secret from Zipster
   private static final String CONSUMER_SECRET = "";

   private static final String REQUEST_TOKEN_URL = "https://www.zipster.com.au/oauth/request_token";
   private static final String AUTHORISE_URL = "https://www.zipster.com.au/oauth/authorise";
   private static final String ACCESS_TOKEN_URL = "https://www.zipster.com.au/oauth/access_token";

   private static final String CATEGORIES_RESOURCE_URL = "http://www.zipster.com.au/services/rest/categories";
   private static final String CATEGORY_RESOURCE_URL = "http://www.zipster.com.au/services/rest/categories/%s";

   public static void main(String[] args) throws Exception {
      OAuthConsumer consumer = obtainAccessToken(CONSUMER_KEY, CONSUMER_SECRET);

      retrieveJobCategories(consumer.getToken(), consumer.getTokenSecret());

      System.out.println("Enter the category ID:");

      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
      String categoryId = bufferedReader.readLine();

      retrieveJobCategory(categoryId, consumer.getToken(), consumer.getTokenSecret());
   }

   private static void retrieveJobCategories(String token, String tokenSecret) throws OAuthException, IOException {
      OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
      consumer.setTokenWithSecret(token, tokenSecret);

      URL url = new URL(CATEGORIES_RESOURCE_URL);

      HttpURLConnection request = (HttpURLConnection) url.openConnection();

      consumer.sign(request);

      System.out.println("Retrieving a list of job categories...");

      request.connect();

      System.out.println("Response code: " + request.getResponseCode());
      System.out.println("Response message: " + request.getResponseMessage());
      System.out.println("Response body: " + IOUtils.toString(request.getInputStream()));

      request.disconnect();
   }

   private static void retrieveJobCategory(String categoryId, String token, String tokenSecret) throws OAuthException,
         IOException {
      OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
      consumer.setTokenWithSecret(token, tokenSecret);

      URL url = new URL(String.format(CATEGORY_RESOURCE_URL, categoryId));

      HttpURLConnection request = (HttpURLConnection) url.openConnection();

      consumer.sign(request);

      System.out.println("Retrieving job category [" + categoryId + "]...");

      request.connect();

      System.out.println("Response code: " + request.getResponseCode());
      System.out.println("Response message: " + request.getResponseMessage());
      System.out.println("Response body: " + IOUtils.toString(request.getInputStream()));

      request.disconnect();
   }

   private static OAuthConsumer obtainAccessToken(String consumerKey, String consumerSecret) throws OAuthException,
         IOException {
      OAuthConsumer consumer = new DefaultOAuthConsumer(consumerKey, consumerSecret);
      OAuthProvider provider = new DefaultOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORISE_URL);

      System.out.println("Obtaining request token...");

      String authorisationUrl = provider.retrieveRequestToken(consumer, OAuth.OUT_OF_BAND);

      System.out.println("Request token: " + consumer.getToken());
      System.out.println("Request token secret: " + consumer.getTokenSecret());

      System.out.println("Now visit the following Url and authorise this application:\n" + authorisationUrl);
      System.out.println("Enter the verification code:");

      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
      String verificationCode = bufferedReader.readLine();

      System.out.println("Obtaining access token...");

      provider.retrieveAccessToken(consumer, verificationCode);

      System.out.println("Access token: " + consumer.getToken());
      System.out.println("Access token secret: " + consumer.getTokenSecret());

      return consumer;
   }
}

Compile and execute the application:

C:\zipster>mvn clean compile exec:java -Dexec.mainClass=com.example.zipster.App
...
[INFO] [exec:java {execution: default-cli}]
Obtaining request token...
Request token: /* request token */
Request token secret: /* request token secret */
Now visit the following Url and authorise this application:
https://www.zipster.com.au/oauth/authorise?oauth_token=/* request token */
Enter the verification code:
/* verification code from Zipster after you have authorised the application */
Obtaining access token...
Access token: /* your access token */
Retrieving a list of job categories...
Response code: 200
Response message: OK
Response body: <?xml version="1.0" encoding="UTF-8"?>
<ResponseCategories>
  <Category>
    <Id><![CDATA[1]]></Id>
    <Name><![CDATA[Analyst: Business]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[2]]></Id>
    <Name><![CDATA[Analyst: Programmer]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[3]]></Id>
    <Name><![CDATA[Analyst: Systems]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[4]]></Id>
    <Name><![CDATA[Architect]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[5]]></Id>
    <Name><![CDATA[Computer Operators]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[6]]></Id>
    <Name><![CDATA[Consultant/Functional Consultant]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[7]]></Id>
    <Name><![CDATA[Database Development & Administration]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[8]]></Id>
    <Name><![CDATA[Engineer: Hardware]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[9]]></Id>
    <Name><![CDATA[Engineer: Network]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[10]]></Id>
    <Name><![CDATA[Engineer: Software]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[11]]></Id>
    <Name><![CDATA[Help Desk/Support]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[12]]></Id>
    <Name><![CDATA[Internet/Multimedia Design]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[13]]></Id>
    <Name><![CDATA[Internet/Multimedia Development]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[14]]></Id>
    <Name><![CDATA[Management]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[15]]></Id>
    <Name><![CDATA[Networks & Systems]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[16]]></Id>
    <Name><![CDATA[Product Management]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[17]]></Id>
    <Name><![CDATA[Project Management]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[18]]></Id>
    <Name><![CDATA[QA/Testers]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[19]]></Id>
    <Name><![CDATA[Sales]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[20]]></Id>
    <Name><![CDATA[Security]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[21]]></Id>
    <Name><![CDATA[Team Leaders]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[22]]></Id>
    <Name><![CDATA[Technical Writers]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[23]]></Id>
    <Name><![CDATA[Telecommunications]]></Name>
  </Category>
  <Category>
    <Id><![CDATA[24]]></Id>
    <Name><![CDATA[Trainers]]></Name>
  </Category>
</ResponseCategories>
Enter the category ID:
1
Retrieving job category [1]...
Response code: 200
Response message: OK
Response body: <?xml version="1.0" encoding="UTF-8"?>
<ResponseCategories>
  <Category>
    <Id><![CDATA[1]]></Id>
    <Name><![CDATA[Analyst: Business]]></Name>
  </Category>
</ResponseCategories>
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 30 seconds
[INFO] Finished at: Tue Sep 14 10:20:59 EST 2010
[INFO] Final Memory: 6M/16M
[INFO] ------------------------------------------------------------------------

Conclusion

In this tutorial, you've learned the basics of using Zipster API to retrieve data. You now have a good foundation from which to build on. You may also want to explore topics not covered in this tutorial, such as posting a job to Zipster and update the job.

You also have seen example of using OAuth to obtain the request token and request token secret. As well as authorise the application to obtain the access token and access token secret, which you can use to perform authorised requests on behalf of the user at any time.

3 comments:

damjan.vantur said...

Hello,
I am trying to use your rest service to retrieve jobs. But when i connect to try to get inputStream I get following:
Response code: 302
Response message: Moved Temporarily
Contenttype message: null
source:

Anyone maybe knows what the problem is?

Anonymous said...

Hi

Are you able to provide part of the code you're using that gives you the error?

Cheers

Anonymous said...

IT would be nice if you put an option up there to download this as a sample project :) The code formatting on the page makes it hard to read

Post a Comment