Fan

 

class

obix::ObixClient

sys::Obj
  obix::ObixClient
//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   20 May 09  Brian Frank  Creation
//

using web

**
** ObixClient implements the client side of the oBIX
** HTTP REST protocol.
**
class ObixClient
{

//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////

  **
  ** Construct with lobby URI and authentication credentials.
  **
  new make(Uri lobby, Str username, Str password)
  {
    this.lobbyUri = lobby
    this.username = username
    this.authHeader = "Basic " + "$username:$password".toBuf.toBase64
  }

//////////////////////////////////////////////////////////////////////////
// Configuration
//////////////////////////////////////////////////////////////////////////

  **
  ** Uri of the lobby object
  **
  const Uri lobbyUri

  **
  ** Username to use for authentication, or null if not
  ** using authentication.
  **
  const Str username

  **
  ** About object relative URI - either set manually or via `readLobby`.
  **
  Uri? aboutUri

  **
  ** Batch operation relative URI - either set manually or via `readLobby`.
  **
  Uri? batchUri

//////////////////////////////////////////////////////////////////////////
// Conveniences
//////////////////////////////////////////////////////////////////////////

  **
  ** Read the lobby object.  This method will set the
  ** `aboutUri` and `batchUri` fields.
  **
  ObixObj readLobby()
  {
    lobby := read(lobbyUri)
    aboutUri = lobby.get("about").href
    batchUri = lobby.get("batch").href
    return lobby
  }

  **
  ** Read about object.  The `aboutUri` must be either set
  ** manually or via `readLobby`.
  **
  ObixObj readAbout()
  {
    if (aboutUri == null) throw Err("aboutUri not set")
    return read(aboutUri)
  }

  **
  ** Perform a batch read for all the given URIs.  The
  ** `batchUri` must be either set manually or via `readLobby`.
  **
  ObixObj[] batchRead(Uri[] uris)
  {
    // sanity checks
    if (batchUri == null) throw Err("batchUri not set")
    if (uris.isEmpty) return ObixObj[,]

    // if only one
    if (uris.size == 1) return [ read(uris[0]) ]

    // build batch-in argument
    in := ObixObj { elemName = "list"; contract = Contract.batchIn }
    baseUri := lobbyUri.pathOnly
    uris.each |uri|
    {
      in.add(ObixObj{elemName = "uri"; contract = Contract.read; val = baseUri + uri })
    }

    // invoke the request
    out := invoke(batchUri, in)
    if (out.elemName == "err") throw Err(out.toStr)

    // return the list of children
    return out.list
  }

//////////////////////////////////////////////////////////////////////////
// Requests
//////////////////////////////////////////////////////////////////////////

  **
  ** Read an obix document with the specified href.
  **
  ObixObj read(Uri uri)
  {
    c := makeReq(uri, "GET")
    c.writeReq.readRes
    if (c.resCode != 200) throw IOErr("Bad HTTP response: $c.resCode")
    return ObixObj.readXml(c.resIn)
  }

  **
  ** Write an obix document to the specified href and
  ** return the server's result.
  **
  ObixObj write(ObixObj obj) { post(obj.href, "PUT", obj) }

  **
  ** Invoke the operation identified by the specified href.
  **
  ObixObj invoke(Uri uri, ObixObj in) { post(uri, "POST", in) }

  private WebClient makeReq(Uri uri, Str method)
  {
    uri = lobbyUri + uri
    c := WebClient(uri)
    c.reqMethod = method
    c.reqHeaders["Content-Type"]  = "text/xml; charset=utf-8"
    c.reqHeaders["Authorization"] = authHeader
    return c
  }

  private ObixObj post(Uri uri, Str method, ObixObj in)
  {
    c := makeReq(uri, method)
    c.writeReq
    in.writeXml(c.reqOut)
    c.reqOut.close
    c.readRes
    if (c.resCode == 100) c.readRes
    if (c.resCode != 200) throw IOErr("Bad HTTP response: $c.resCode")
    return ObixObj.readXml(c.resIn)
  }

//////////////////////////////////////////////////////////////////////////
// Test
//////////////////////////////////////////////////////////////////////////

  static Void main(Str[] args)
  {
    c := ObixClient(args[0].toUri, "", "")
    c.readLobby
    about := c.readAbout
    about.writeXml(Sys.out)
    echo(about->serverName)
    echo(about->vendorName)
    echo(about->productName)
    echo(about->productVersion)
  }

//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////

  ** Log for tracing
  //Log log := Log.get("obix")

  private Str authHeader
}