ISY Developers:Java ISY REST Requester Example

From Universal Devices, Inc. Wiki
Jump to: navigation, search

Simple/Basic Java REST Web Services Client (with source)

This page presents the Java code for a basic REST Web services client. It also shows how to use it to wrap around ISY REST services, including the HTTP username/password Authentication.

It is kept as small and lightweight as possible but includes all the source code to get a working authenticated service connection with a restful web services server. Think of it as the "hello world"/getting started how-to Java code to be re-used and extended to wrap around the full processing of any published REST web services - specifically ISY REST services!

Background

If you are writing a Java application that needs to interact with the UDI ISY, you have a few robust options;

  1. its full and robust Java SDK,
  2. use the WebServices interface ISY-WSDK, or to keep it as light as possible
  3. use the REST Interface services.

The author of this article was writing an Android app and wanted to keep it as small and lean as possible - without the inclusion of the UDI SDK Java libraries. Using REST was perfect for this. This page shows the code to make a simple, but fully functional, authenticating REST client to access the ISY services. It is self-functioning, from a commandline, or can be used/included within a different Java application by instantiating the ISYRestRequester class and then making a REST call.

ISYRestRequester instance = new ISYRestRequester("baseUrl", "userName", "userPasswd");

StringBuilder resp =  instance.serviceGet("/sys");
System.out.println(resp.toString()); System.out.println();

The full documentation is at http://links.webarts.ca/isyRestRequester

Scope of this REST Client

  • use as few external libraries as possible - keep it to the standard Java Library
    • unfortunately Authentication requires the use of Base64 encoding so I used an Apache library for this
  • provide a extendable class with the basic code to connect, send service requests, and receive responses
  • has a basic set of exposed methods to setup and use published Rest Web Services
  • handles Authentication to the service, if it is needed
  • has a main method to allow testing or calling services from the commandline
  • configurable server url
  • handles all the connections to the web services URL
  • POST requests or GET requests
  • Minimal or no processing of the responses other than returning the results as a StringBuilder (and dumping to System.out when run from the commandLine)

Java Source Code

The source code is inline below and free to use and modify as needed. The latest version is also available at the authors Subversion repo viewer at http://svn.webarts.bc.ca/astroVersion in the WebARTS open repo:

  • ca.bc.webarts.tools.RestRequester
  • ca.bc.webarts.tools.isy.ISYRestRequester

The source is also contained in the isyRest.jar file at the bottom of this page.

Class Diagram

RestRequesterDiagram.png

Extending this class to wrap around the ISY REST services

The generic class works out of the box on any configured Server URL, however, it is easier and cleaner to create a new class that extends the above base class with specific REST service parameters specific to the ISY services, such as

  • ISY REST Service Base URL (for example http://isy994i/rest)
  • Authentication username and password
  • control the expected response type - plain XML or JSON
  • response processing - pull out the ISY specific information from the XML responses and transcode it into whatever
  • wrap individual service calls into callable Java methods that "do something" with the responses

HTTP Authentication

This authentication is handled by the RestRequester class - callService method. It simply encodes the username and password with a Base64 encoder and puts it into the HTTP header. See the full base class code below for all the details.

Here is a simplified summary of what is done (in Java) to do the Authentication and send a REST Request:

String usrlStr = (baseUrl_+serviceName).replace(" " ,"%20");  // make sure any spaces are urlEncoded
URL url = new URL(usrlStr);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/xml");

/* The http header String needs to be in the following format */
String userpassword = username_ + ":" + password_;

/* Here are your options for what Base64 encoder to use */
//BASE64Encoder enc = new sun.misc.BASE64Encoder();  //  deprecated
//String encodedAuthorization = android.util.Base64.encodeToString( userpassword.getBytes(), android.util.Base64.DEFAULT );

// Encode the User/pass
String encodedAuthorization = new String(org.apache.commons.codec.binary.Base64.encodeBase64( (userpassword.getBytes()) ));

// Embed the Base64 encoded Authorization Header Property
conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);

// Make the request and get the response
if (conn.getResponseCode() == 200)   // SUCCESS
{
  BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  if (br!=null)
  {
    // do something with the Rest Response!
  }
}
conn.disconnect();
// Thats it!

Dependencies - Apache Commons Codec

The code depends (NOT Derivative) on a few Java files from the Apache Commons Codec library to perform Base64 encoding required by the http basic authentication. These files are licensed and re-distributed unmodified, as links below, under the Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 .

Everything is in the isyRest.jar file at the bottom of the page. so you don't have to download the files individually.

Using Android Base64 Instead

If you plan on using this (or other REST client) within an Android app, you can use the Android Base64 class in the Android SDK that will handle the Encoding instead of the Apache Commons Codec library files. I commented out the import android.util.Base64 in the source. I tried it it works fine as well.

Base Class - RestRequester.java

/*
 *
 *  Written by Tom Gutwin - WebARTS Design.
 *  Copyright (C) 2014 WebARTS Design, North Vancouver Canada
 *  http://www.webarts.ca
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package ca.bc.webarts.tools;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.File;
import java.lang.StringBuilder;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;

//import android.util.Base64;
import org.apache.commons.codec.binary.Base64;

/**
 * A class to encapsulate the calls to Restful Web Services. It is kept very basic with low overhead to live in android apps.
 * It might be best to extend this class with your specific REST servers details.
 **/
public class RestRequester
{
  protected static String CLASSNAME = "ca.bc.webarts.tools.RestRequester";
  public static final String LOG_TAG = CLASSNAME;
  public static boolean debugOut_ = false;

  /**  A holder for this clients System File Separator.  */
  public final static String SYSTEM_FILE_SEPERATOR = File.separator;

  /**  A holder for this clients System line termination separator.  */
  public final static String SYSTEM_LINE_SEPERATOR =
                                           System.getProperty("line.separator");

  protected static String baseUrl_ = ""; // http://isy994
  public static boolean authenticating_ = true;
  protected static String username_ = "";
  protected static String password_ = "";
  protected static boolean acceptJSON_ = false;

  public void setUsername(String uName){username_=uName;}
  public void setPassword(String uPasswd){password_=uPasswd;}
  public void setBaseUrl(String url){baseUrl_=url;}
  public void setAcceptJSON(boolean acceptJson){acceptJSON_=acceptJson;}
  public String getUsername(){return username_;}
  public String getPassword(){return password_;}
  public String getBaseUrl(){return baseUrl_;}
  public boolean getAcceptJSON(){return acceptJSON_;}


  public RestRequester()
  {
  }


  public RestRequester(String baseUrl)
  {
    setBaseUrl( baseUrl);
    authenticating_=false;
  }


  /**
   * Constructor to take the service BASE url and authenticate with the passed userName and Password.
   **/
  public RestRequester(String baseUrl,String uName,String uPasswd)
  {
    setBaseUrl( baseUrl);
    authenticating_=true;
    setUsername( uName);
    setPassword( uPasswd);
  }


  public boolean isInit()
  {
    boolean retVal = true;
    if( baseUrl_.equals("") ||
        (authenticating_ &&
            (username_.equals("") || password_.equals(""))
        )
      )
      retVal=false;
    return retVal;
  }


  /** Sends the rest service GET request off and retruns the results.
    * @param serviceName is the service (string) to append to the baseURL - example /rest/sys
    * @return the serviceResult as a stringBuilder, null if error
    **/
  public StringBuilder serviceGet(String  serviceName)
  { return callService(serviceName, true);}


  /** Sends the rest service POST request off and retruns the results.
    * @param serviceName is the service (string) to append to the baseURL - example /rest/sys
    * @return the serviceResult as a stringBuilder, null if error
    **/
  public StringBuilder servicePost(String  serviceName)
  { return callService(serviceName, false);}


  /** Sends the rest service request off and retruns the results.
    * @param serviceName is the service (string) to append to the baseURL - example /rest/sys
    * @param getNotPost is a flag to tell this method to do a get or post based on this flag - true does a GET, false does a POST
    * @return the serviceResult as a stringBuilder, null if error
   **/
  public StringBuilder callService(String  serviceName, boolean getNotPost)
  {
    StringBuilder retVal = null;
    if(isInit())
      try
      {
        String usrlStr = (baseUrl_+serviceName).replace(" " ,"%20");
        URL url = new URL(usrlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if(getNotPost)
          conn.setRequestMethod("GET");
        else
          conn.setRequestMethod("POST");
        if(acceptJSON_)
          conn.setRequestProperty("Accept", "application/json");
        else
          conn.setRequestProperty("Accept", "application/xml");

        //BASE64Encoder enc = new sun.misc.BASE64Encoder();
        String userpassword = username_ + ":" + password_;
        //String encodedAuthorization = android.util.Base64.encodeToString( userpassword.getBytes(), android.util.Base64.DEFAULT );
        String encodedAuthorization = new String(Base64.encodeBase64( (userpassword.getBytes()) ));
        conn.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);

        if (conn.getResponseCode() == 200)
        {
          BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
          if (br!=null)
          {
            retVal = new StringBuilder();
            String output;
            if (debugOut_) System.out.println("Output from Server .... \n");
            while ((output = br.readLine()) != null)
            {
              if (debugOut_) System.out.println(output);
              retVal.append(output);
              retVal.append("\n");
            }
          }
        } // valid http response code
        conn.disconnect();
      }
      catch (MalformedURLException e)
      {
       e.printStackTrace();
      }
      catch (IOException e)
      {
         e.printStackTrace();
      }
    return retVal;
  }

  public StringBuilder responseIndenter(StringBuilder sb)
  {
    StringBuilder retVal = new StringBuilder("");
    int indent = -1;
    boolean opening = false;
    boolean closing = false;
    boolean lf = false;
    char [] sbChar = sb.toString().toCharArray();

    for (int i=0; i< sbChar.length;i++)
    {
      opening = false;
      closing = false;
      lf = false;
      if(sbChar[i]=='<')
      {
        indent++;
        opening = true;
        retVal.append("\n");
        for (int j=0;j<indent;j++) retVal.append("  ");
        retVal.append("<");
      }
      else if (sbChar[i]=='/'&&sbChar[i-1]=='<')
      {
        indent--;indent--;
        closing = true;
        retVal.append("/");
      }
      else if (sbChar[i]=='>')
      {
        lf = true;
        retVal.append(">\n");
        for (int j=0;j<indent;j++) retVal.append("  ");
      }
      else
      {
        retVal.append(sbChar[i]);
      }
    }

    return retVal;
  }

}


ISY-994 Extension Class - ISYRestRequester.java

/*
   *
   *  Written by Tom Gutwin - WebARTS Design.
   *  Copyright (C) 2014 WebARTS Design, North Vancouver Canada
   *  http://www.webarts.ca
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without_ even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   */

package ca.bc.webarts.tools.isy;

import ca.bc.webarts.tools.RestRequester;

public class ISYRestRequester extends RestRequester
{
  protected static String CLASSNAME = "ca.bc.webarts.tools.isy.ISYRestRequester"; 
  private static StringBuffer helpMsg_ = new StringBuffer(SYSTEM_LINE_SEPERATOR);

  /** Constructor to over-ride the base class with the ISY specifics **/
  public ISYRestRequester()
  {
    setBaseUrl( "http://isy994/rest");  //  <<<---  Add your ISY IP URL here
    authenticating_=true;
    setUsername( "admin");             //  <<<---  Add your ISY userID here
    setPassword( "*******");           //  <<<---  Add your ISY password here
  }

  /**
   * Class main commandLine entry method.
   **/
  public static void main(String [] args)
  {
    final String methodName = CLASSNAME + ": main()";
    ISYRestRequester instance = new ISYRestRequester();  
    // could also instatiate with the RestRequester(String baseUrl,String uName,String uPasswd)

    /* Simple way af parsing the args */
    if (args ==null || args.length<1)
      System.out.println(getHelpMsgStr());
    else
    {
      if (args[0].equals("test"))
      {
        System.out.println("Testing ISY Rest Service: "+ "/sys");
        StringBuilder resp =  instance.serviceGet("/sys");
        System.out.println(resp.toString());
        System.out.println();
      }
      else
      {
        // Parse the command
        String allcommands = args[0];
        for (int i=1;i< args.length;i++) allcommands+=" "+args[i];
        System.out.print("Sending ISY Rest Service: "+allcommands);
        String passedCommand = (allcommands.startsWith("/rest/") ? allcommands.substring(5) : allcommands);
        System.out.println(" ("+passedCommand+")");
        StringBuilder resp =  instance.serviceGet(passedCommand);
        if (resp!=null)
        {
          System.out.println(instance.responseIndenter(resp).toString());
          System.out.println();
        }
        else
        {
          System.out.println("Response Error");
          System.out.println();
        }
      }
    }
  } // main

    /** gets the help as a String.
   * @return the helpMsg in String form
   **/
  private static String getHelpMsgStr() {return getHelpMsg().toString();}


  /** initializes and gets the helpMsg_
  class var.
   * @return the class var helpMsg_
   **/
  private static StringBuffer getHelpMsg()
  {
    helpMsg_ = new StringBuffer(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("---  WebARTS ISYRestRequester Class  -----------------------------------------------------");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("---  $Revision:$ $Date:$ ---");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("-------------------------------------------------------------------------------");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("WebARTS ca.bc.webarts.tools.isy.ISYRestRequester Class");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("SYNTAX:");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("   java ");
    helpMsg_.append(CLASSNAME);
    helpMsg_.append(" test or restCommand");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("Available Commands:");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("see: http://wiki.universal-devices.com/index.php?title=ISY_Developers:API:REST_Interface");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);
    helpMsg_.append("---------------------------------------------------------");
    helpMsg_.append("----------------------");
    helpMsg_.append(SYSTEM_LINE_SEPERATOR);

    return helpMsg_;
  }

}


isyRest.jar - source and classes

Both classes and source are included in the following:

Unzip it, make changes (isyURL/user/pass), javac and away you go!

You can also run this directly from the jar:

java -jar isyRest.jar  /sys

Author - Tom Gutwin

http://tom.webarts.ca