An image of some Dominos

Mobilizing Domino Data Using REST Part 1

Print Friendly and PDF

Posted: January 19, 2011 | | Categories: IBM Lotus Domino

The first part of a series on mobilizing IBM Lotus Domino data for mobile platforms that don't directly support XML-based Web Services (like Android and the iPhone).

Introduction

Note: removed obsolete attachments November 11, 2022

Back when I worked for Research in Motion, I presented topics at Lotusphere and the View Developer Conference on how to connect to Domino databases from rich client applications on BlackBerry. Research in Motion had some pretty cool tools at the time (MDS Studio) that made it painfully easy – I could build a BlackBerry application that talked to a Domino Web Service in about 3 minutes.

Beginning with BlackBerry Device Software 4.2, Research in Motion added the ability to connect to Web Services from a BlackBerry Java application (using JSR 172), so I added building a BlackBerry Java application to my set of demos. I then wrote a series of articles on this site that documented the whole process. Here are links to the articles:

  • Domino & BlackBerry Java Applications Part 1
  • Domino & BlackBerry Java Applications Part 2
  • Domino & BlackBerry Java Applications Part 2.5
  • Domino & BlackBerry Java Applications Part 3

After I left Research in Motion, I continued to present at the same conferences and added the Midwest Lotus User Group (MWLUG) conference to my standard tour. Because of the Web Services capabilities of Microsoft Visual Studio, it was pretty easy to add a demo of a Windows Mobile application to my presentations as well.

The iPhone and Android platforms were released without the ability to connect to XML-based Web services, so I had to find a different way for those platforms to connect to Domino. Android includes the JSON libraries from json.org, and there were several versions of JSON libraries for the iPhone platform up there as well, so I decided I'd use REST and JSON to accommodate Android and iPhone. At Lotusphere 2010, while recovering from pneumonia, I added an Android demo to the presentation and was even able to show an almost complete (I never finished it) iPhone example as well. At the conference I promised I'd publish an article here demonstrating how I built the Android application, so here it begins (finally). This article is the first part of a new series that's all about how to use REST and JSON to mobilize Domino data on Android. There'll be two articles in this series here, plus I'll eventually publish an article in the View illustrating the iPhone application.

The Sample Application

In case you missed the first part of this series, the application we're building here allows a mobile user to lookup contacts in the Domino Directory. The assumption here is that most every Domino customer has some extra database of contacts and mobile users will need the ability to lookup contact information. This is the same process as demonstrated in the original series; it's just modified so it leverages JSON instead of Web Services.

About REST

REST stands for Representational State Transfer and essentially it's a form of Web Service where parameters for a request are sent on the URL to the server and the server's response is returned in the body of the HTTP response typically in XML or JSON format although it could be in any format. RESTful Web Services are much easier to use then XML Web Services since it's so much less work to call the service and there's much less overhead when the data is returned as JSON rather than XML.

About JSON

JSON stands for JavaScript Object Notation and it's a way of representing data in a textual format that's easy to parse and manipulate. To quote json.org (www.json.org):

“JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

JSON is built on two structures:

  • A collection of name/value pairs. In various languages, this is realized as an object, record, struct, dictionary, hash table, keyed list, or associative array.

  • An ordered list of values. In most languages, this is realized as an array, vector, list, or sequence.

These are universal data structures. Virtually all modern programming languages support them in one form or another. It makes sense that a data format that is interchangeable with programming languages also be based on these structures.”

In JSON, an array looks like this:

And an Object looks like this:

You'll see how these apply in the following section.

*JSON Images shamelessly 'borrowed' from www.json.org.

Designing the JSON Agent

In the Web Services version of the agent I created for the BlackBerry and Windows Mobile versions of the mobile application, I created a single service that supported two operations: GetUserList and GetUserDetails. Since the needs for this application were for there to be the same two operations and each operation would be called by a URL, it would seem to make sense to make a separate Domino web agent for each operation. Unfortunately, that approach would require two agents and a bunch of duplicated code between them, so I decided to create a single agent that exposed the two operations through a single interface. So, there is going to be a single Domino agent called by the mobile application and different results would be returned to the calling program depending on what information is passed on the URL to the Domino server.

So, the URL a mobile application will be using to use the service will look like this:

https://server/database.nsf/domdirlookuprest?openagent&cmd=COMMAND&searchstr=SEARCHSTRING

Where the cmd= and searchStr= portions of the URL are places where the calling program can pass in parameters to control what the service returns.

To obtain a list of contacts whose last name begins with 'war', the mobile application will call the following URL:

https://server_name/bbnames.nsf/domdirlookuprest?openagent&cmd=list&searchstr=war

In this case, the cmd is list and the searchStr is 'War' – when the agent runs on the Domino server, it will return the following JSON array (assuming that there are two contacts defined in the database whose last names begin with War):

["John Wargo", "Anna Wargo"]

Once a mobile application's user selects a contact, the agent is called again with the following URL:

https://localhost/bbnames.nsf/domdirlookuprest?openagent&cmd=details&searchstr=john+wargo

In this case, the cmd is 'details' and the searchStr is my contact name. When the agent runs on the Domino server, it will return the following JSON object (notice that the first call, the call to get the list, returns a JSON array, while the second call (for the details) returns an object):

{  
  "FullName" : "John Wargo",  
  "LastName" : "Wargo",  
  "FirstName" : "John",  
  "EmailAddress" : "someguy@some-email.com",  
  "OfficePhone" : "330.123.4567",  
  "MobilePhone" : "330.987.6543"  
}

That's it, that's all there is to the agent I'm showing you how to build in this article.

Probably asking yourself “Why not use the JSON capabilities already built into Domino to provide this functionality?” Well, Domino's ability to generate JSON output is designed for rendering views in a format that's easy for an application to process. Using the following URL:

https://server1/database.nsf.nsf/viewname?ReadViewEntries&OutputFormat=JSON

would cause the Domino server to render the view as JSON, it doesn't help us here because we're passing in search strings and trying to retrieve only one document. It could be done (I think) with a single category view, but taking that approach was much more work than I wanted to do.

Building the JSON Agent

Let's dig into the agent. A sample database that contains this agent is attached at the bottom of this article.

It essentially begins with the standard stuff I put into any Domino agent:

Option Public  
Option Declare  
Option Base 1

Then I define some common variables I'll need as I process the database view:

'Notes Objects  
Dim db As NotesDatabase  
Dim doc As NotesDocument  
Dim s As NotesSession  
Dim tmpName As NotesName  
Dim userView As NotesView  
Dim userDoc As NotesDocument  
  
'Other variables  
Dim i As Integer  
Dim jsonText As String

Finally some constants that are used to prepare the JSON text outputted by the agent:

Const amp = |&|  
Const BR = |<br />|  
Const comma = |, |  
Const errorStr = |ERROR: |  
Const quoteStr = |"|

It's the Initialize subroutine where all of the processing happens. Remember, the service works by having essentially a single URL that's called by the calling program. Parameters passed on the URL instruct the agent what to do. The Initialize subroutine essentially does nothing but parse the URL and call the appropriate subroutine depending on which command ('list' or 'details') is passed to the agent on the URL.

Since we're trying to output JSON in this agent, Initialize begins with a print statement that causes Domino to skip the building of an HTML page to be sent to the calling program. If this wasn't there, Domino would assume the agent was delivering HTML content and would prepend the beginning parts of a web page to the agent's output.

Next, the agent gets a handle to the current Notes Session object, database object then the agent's context (through set doc = s.DocumentContext). After that, the agent gets the URL string from the document context and parses it through repeated calls to the GetCmdLineValue function. Once it knows what command has been sent (list or details) and what search string to use, it calls the appropriate subroutine (GetContactList or GetContactDetails) to lookup the appropriate document(s) and output the necessary JSON text.

Sub Initialize()  
  
  'Print out this line to force Domino to not write it's own  
  'HTML gunk at the beginning of the resulting page  
  Print "Content-Type:text/html"  
  
  Dim cmdName As String  
  Dim queryStr As String  
  Dim searchStr As String    
  Dim tmpStr As string  
  Dim tmpInt As integer  
  
  'Initialize our Notes session object  
  Set s = New NotesSession  
  'Then get a handle to the current database  
  Set db = s.CurrentDatabase  
  'Get a handle to the agent's context (header variables and so on)  
  Set doc = s.DocumentContext    
  
  'Parse the command line and call the correct function  
  queryStr = LCase(doc.Query_String_Decoded(0)) & amp
  cmdName = GetCmdLineValue(queryStr, "&cmd=", amp)  
  searchStr = GetCmdLineValue(queryStr, "&searchstr=", amp)  
   
  'Launch the appropriate function based upon what's passed in the URL  
  If cmdName = "details" Then  
    GetContactDetails(searchStr)  
  Else  
    If cmdName = "list" Then  
      GetContactList(searchStr)  
    Else  
      'We have an invalid command passed on the Query_String,  
      'so return an error  
      Print errorStr  
    End If    
  End If  
  
End Sub

The GetCmdLineValue function simply takes an input string and returns everything between the two delimiters.

Function GetCmdLineValue(`textStr` As String, `delim1` As String, `delim2` As String) As String  
  
  Dim startPos As Integer    
  Dim tmpInt As Integer  
  Dim valLen As Integer  
  
  'find the first ocurrance of the delimeter  
  tmpInt = InStr( textStr, delim1)  
  'Only continue if we've found something  
  If (tmpInt > 0) Then  
    'Figure out where the value starts  
    startPos = tmpInt + Len(delim1)  
    'Then look past there for the second delimeter  
    valLen = InStr(startPos, textStr, delim2) - startPos  
    'The value we're looking for is between the two delimeters  
    GetCmdLineValue = Mid( textStr, startPos, valLen)  
  Else  
    GetCmdLineValue = ||  
  End If     
End Function

The GetContactList subroutine uses the provided searchString to search the contents of the default Domino Directory $VIMPeopleByLastName view.  If it finds any documents, it builds a JSON array containing each contact name then prints the array; thereby sending the array information to the calling program.

sub GetContactList(searchStr As String)  
  
  Const arrayStart = "["  
  Const arrayEnd = "]"  
  
  'Local Notes objects  
  Dim ve As NotesViewEntry  
  Dim vec As NotesViewEntryCollection  
    
  'Other variables  
  Dim numContacts As Integer  
    
  'Open the view we're going to lookup against  
  Set userView = db.GetView("($VIMPeopleByLastName)")  
  If Not userView Is Nothing Then  
    'Do we have a search string?  
    If Len(Trim(searchStr)) > 0 Then  
      'See if we can find any users by the search string  
      Set vec = userView.GetAllEntriesByKey(searchStr)  
    Else  
      'Otherwise get all documents  
      Set vec = userView.AllEntries  
    End If  
    'Now, if we have any entries - put them into the array  
    If vec.Count > 0 Then  
      'Get the number of contacts to use throughout the rest of the code  
      numContacts = vec.Count  
      'Start the Array JSON text  
      jsonText = arrayStart  
      'Process all of the entries in the View Entry Collection  
      For i = 1 To numContacts  
        Set ve = vec.GetNthEntry(i)  
        If Not ve Is Nothing Then  
          Set userDoc = ve.Document  
          If Not userDoc Is Nothing Then  
            Set tmpName = New NotesName(userDoc.FullName(0))  
            If Not tmpName Is Nothing Then  
              jsonText = jsonText & quoteStr & tmpName.Common & _  
                quoteStr  
            Else  
              jsonText = jsonText & quoteStr & errorStr & _  
                |Unable to obtain Contact Name| & quoteStr  
            End If  
          Else  
             'Unable to get the document  
             jsonText = jsonText & errorStr & _  
              |Unable to open the contact document| & quoteStr  
          End If  
        Else  
         'Unable to access the View Entry  
          jsonText = jsonText & quoteStr & errorStr & _
            |Unable to access the ViewEntry| & quoteStr  
        End If  
        'Add a comma if we need one (when there's more than  
        'one name being returned)  
        If (i < numContacts) Then  
          jsonText = jsonText & comma  
        End If  
      Next i  
      'Write our resulting JSON results to the browser  
      Print jsonText & arrayEnd  
   Else  
      'We got nothing, so return an empty array to the  
      'calling program  
      Print arrayStart & arrayEnd  
    End If  
  End If  
End sub

The GetContactDetails subroutine uses the provided searchString to search the contents of the default Domino Directory $VIMPeople view.  If it finds a document (It should find only one unless there are more than one contacts in the database with the exact same name), it builds a JSON object that contains each of the fields retrieved from the document then prints the object, causing the object to be returned to the calling program.

sub GetContactDetails(searchStr As String)  
  
   Const jsonStart = |{|  
  Const jsonEnd = |}|  
  
  'Do we have a search string?  
  'the calling program should check, but have to make sure  
  If Len(Trim(searchStr)) > 0 Then  
    'Open the view we're going to lookup against  
    'this is a different view because we're searching against  
    'the full name where above we're using last name    
    Set userView = db.GetView("($VIMPeople)")  
    'Make sure we have the view  
    If Not userView Is Nothing Then  
      'Try to get the user document using abbreviated full name as a key  
      'This should work because the view is sorted that way.  
      Set userDoc = userView.GetDocumentByKey(searchStr)  
      If Not userDoc Is Nothing Then  
        'Start the JSON text we'll be returning  
        jsonText = jsonStart  
        'Populate the result fields  
        Set tmpName = New NotesName(userDoc.FullName(0))  
        jsonText = jsonText & |"FullName" : | & quoteStr & _  
          tmpName.Abbreviated & quoteStr & comma  
        jsonText = jsonText & |"LastName" : | &  quoteStr & _  
          userDoc.LastName(0) & quoteStr & comma  
        jsonText = jsonText & |"FirstName" : | &  quoteStr & _  
          userDoc.FirstName(0) & quoteStr & comma  
        jsonText = jsonText & |"EmailAddress" : | &  quoteStr & _  
          userDoc.InternetAddress(0) & quoteStr & comma  
        jsonText = jsonText & |"OfficePhone" : | &  quoteStr & _  
          userDoc.OfficePhoneNumber(0) & quoteStr & comma  
        jsonText = jsonText & |"MobilePhone" : | &  quoteStr & _  
          userDoc.CellPhoneNumber(0) & quoteStr & jsonEnd  
        Print jsonText  
      Else  
        Print errorStr & |Unable to open Contact Document|  
      End If          
    Else  
      Print errorStr & |Unable to open User View|  
    End If  
  Else  
    Print errorStr & |Search String not provided|  
  End If  
End sub

Conclusion

That's all there is to it. In the next installment, I'll show you how to build an Android application that consumes the service. It only took me a year to deliver on my promise to publish this article. I wonder how long it will take me to publish the next one article. Stay tuned.


Next Post: Kryos AppXtender and More

Previous Post: What does AT&T do

If this content helps you in some way, please consider buying me a coffee.

Header image: Photo by Tamara Gak on Unsplash.