SDLIP Java Transport Module 

SDLIP document map and recommended reading sequence (click to navigate):

This document provides a brief tutorial to writing Java implementations of SDLIP clients and servers, and contains the Java binding for the SDLIP interface. The document further contains complete code examples for both clients and collection wrappers (LSPs). It is assumed that the reader is familiar with the SDLIP-Core document.

Content:

1 Overview 

2 Tutorial 

  2.1 SDLIP toolkit 

  2.2 Setting up the environment 

  2.3 SimpleClient 

  2.4 SimpleLSP 

  2.5 XMLObject

  2.6 Toolkit Utilities

  2.7 Useful example applications

3 Java Binding 

4 References

1 Overview

[SDLIP-Core] desribes an abstract interface which shields application builders from the underlying transport mechanisms.  The SDLIP Application Programming Interface (API) is defined using the Interface Definition Language (IDL). IDL provides an interface description which is independent from any particular programming language. This document specifies how the SDLIP interface is realized when writing in Java. We call this specification SDLIP's "Java binding".

For reference, Figure 1 recalls SDLIP's implementation architecture, which was introduced in the SDLIP-Core document.

Figure 1: This document covers client application and LSP construction

For the left side of Figure 1, this document explains how to write a client application. For the right side of the Figure, the following material explains how to write a Library Service Proxy (LSP). We also explain an 'SDLIP Toolkit' that provides useful facilities to make the creation of applications and LSPs easy. Among the toolkit's facilities are client and server transport modules for both DASL and CORBA transports. The reader does not need to understand the details of these transports. But this document explains how to instantiate and use the respective facilities. Recall that a single LSP may be made accessible through multiple transports, such as DASL and CORBA. We show how this is done.

The SDLIP Java binding contains three Java interfaces to correspond to the three main SDLIP interfaces:

These interfaces comprise methods to be implemented by LSPs and to be called by clients. Furthermore, there is an auxiliary interface which facilitates convenient implementations of XML data structures. The rationale for this interface is presented in Sec 2.5. Finally, is the implementation of the SDLIP exception conditions.

LSP developers must implement at least the Search interface. This interface is sufficient for a realization of a stateless server. The optional Metadata interface allows SDLIP clients to discover the capabilities of SDLIP LSPs. It was designed to be very simple to encourage support from LSP developers.

Both client application and LSP builders would typically employ transport modules like the ones provided in the SDLIP distribution toolkit.  The following tutorial gives an example of an SDLIP client that uses the toolkit. The client performs a query, and then lists the properties of all documents found. An example LSP is included as well. After these code examples, some of the toolkit utilities are introduced. Section 3, finally, details the Java interfaces that realize the SDLIP operations.

2 Tutorial

This section provides a brief tutorial on how to write a simple SDLIP client and a simple SDLIP LSP.

2.1 Setting up the environment

The SDLIP distribution can be downloaded at http://www-diglib.stanford.edu/~testbed/doc2/SDLIP/download.htm. It contains four Java archives (jar-files). The SDLIP toolkit is packaged as a file called sdlip-1_x.jar. The example applications described below are contained in the file examples-1_x.jar (Replace x by the corresponding minor version numbers of the distribution). You have to include both archives into your CLASSPATH environment variable before running the examples.

If you use Windows 95/NT type
    set CLASSPATH=%CLASSPATH%;c:\your-dir\sdlip-1_x.jar;c:\your-dir\examples-sdlip-1_x.jar
On UNIX using tcsh type
    setenv CLASSPATH "$CLASSPATH":/your-dir/sdlip-1_x.jar:/your-dir/examples-sdlip-1_x.jar

where your-dir is the directory in which the Java archives can be found. The file corba-sdlip-1_x.jar contains the SDLIP/CORBA binding and and a complete CORBA Object Request Broker (ORB). Include it in your CLASSPATH before the two archives above if you wish to run servers and clients using the SDLIP/CORBA transport modules. Notice that this provides you with effortless CORBA accessibility: Since the jar file contains the CORBA ORB, no additional CORBA installation or expertise is required.

2.2 SDLIP Toolkit

The applications demonstrated in the following sections make use of the toolkit libraries included in the SDLIP distribution. These libraries can be divided into two main groups:
  1. Transport modules (package sdlip.helpers). Client applications and LSPs can use these as black boxes.

  2.  
    1. The SDLIP/DASL modules implement the transport mapping described in the document "SDLIP DASL Transport Mapping". The corresponding classes are ClientDaslTransport and ServerDaslTransport for the client side and the server side, respectively.

    2.  
    3. The SDLIP/CORBA module described in the document "SDLIP CORBA Transport Mapping" is implemented by ClientCorbaTransport and ServerCorbaTransport.

    4.  
  3. Programming libraries for parsing, serializing and manipulating XML structures. These libraries are contained in the package sdlip.xml.dom. The only two classes in this package that client and LSP implementations would want to use directly are (a) sdlip.xml.dom.XMLObject described in Sec 2.5 and (b) sdlip.xml.dom.DOMUtil which contains a number of convenience methods for manipulating XML structures.
The complete API for the toolkit can be found here.

A typical SDLIP client begins by creating a transport module. For this it uses a static method of the class ClientTransportModule (a parent of both ClientDaslTransport and ClientCorbaTransport). The address of an LSP is represented using a URI which is passed to that static method. This URI has to be made available by the LSP maintainers. More on that later in this section. Depending on the type of the URI, either an SDLIP/DASL or SDLIP/CORBA transport module instance is created and returned: If the LSP address is a URL (i.e. starts with "http://"), then a DASL transport is created. If instead the URI is a CORBA object identifier of the server transport module object (i.e. starts with "IOR:"), then a CORBA transport is created. Note that at compilation time the client developer does not have to worry about whether the SDLIP/DASL or SDLIP/CORBA transport module or both will be used later on in the client application. The corresponding transport modules are loaded dynamically at runtime.

The client invokes all subsequent SDLIP operations on that local client transport module instance. Every transport module implements the Search, ResultAccess and Metadata interfaces. Methods invoked on the transport module instance transparently access the corresponding SDLIP LSP via the server transport module (as per Figure 1). Here is an example of a client sending a search request to an SDLIP LSP:
 
import sdlip.helpers.*;

public static void main(String[] args) {
    String URL_or_IOR = "...";
    ClientTransportModule tm = ClientTransportModule.create(URL_or_IOR);
    tm.search(...);
}

Analogously on the server side, the initialization sequence of a typical LSP comprises the following steps:

  1. create one or more LSP instances that implement the Search (ResultAccess, Metadata) interface(s)
  2. create one or more transport module instances
  3. pass the LSP instance reference(s) that were obtained in (1) to the transport modules that were initialized in (2)
An example of LSP initialization code is given below:
 
import sdlip.helpers.*;

public static void main(String[] args) {
    Search lsp = new MySearchLSP();                       /* 1 */
    ServerDaslTransport t1 = new ServerDaslTransport();   /* 2 */
    ServerCorbaTransport t2 = new ServerCorbaTransport(); /* 2 */
    t1.addLSP("myLSP", lsp);                              /* 3 */
    t2.addLSP("myLSP", lsp);                              /* 3 */
}

The server developer has to make a decision, which transport mechanisms (DASL, CORBA, or both) are to be supported by the LSP. This decision affects only one or two lines of code of the LSP. It can be postponed until runtime using the following "trick":
 
    /* Initialize CORBA transport only if runtime libraries are available */
    try {
      ServerCorbaTransport t2 = new ServerCorbaTransport();
      t2.addLSP("myLSP", lsp);
    } catch (NoClassDefFoundError err) {}

As noted earlier, the LSP maintainer has to publish the address (URI) of the LSP, so that SDLIP clients can access it. Assume that "myhost.edu" is the DNS name of the machine on which the LSP runs in the above example, and that the transport module listens on port 8080. Then the SDLIP/DASL address of the LSP would be

http://myhost.edu:8080/myLSP
In the case of CORBA, name services are used to find an object identifier (IOR) from an object name. The CORBA server transport module that is included with the SDLIP distribution includes a simple name server that serves the IORs of the LSPs that are accessible through the transport module. This name server is accessible via HTTP, and by default listens on port 8181. Thus, assuming that the libraries for the SDLIP/CORBA binding are available at runtime (which is the case if corba-sdlip-1_x.jar is included in your CLASSPATH) , then pointing to the URL
http://myhost.edu:8181/myLSP
using your favorite browser would deliver the IOR of the CORBA object implementing the SDLIP/CORBA binding. Both the DASL URL http://myhost.edu:8080/myLSP, and the IOR returned for the CORBA object would be valid URIs for the initialization of the client transport. They refer to the same LSP.

To spare the client applications the step of resolving object references, ClientTransportModule also accepts a URI of the form sdlipns:http://myhost.edu:8181/myLSP and performs the necessary address resolution transparently. Thus, the LSP's URIs that the client application uses to create a client transport module could for our example be any of the following:

2.3 SimpleClient

The complete SimpleClient application below sends a simple search request to an LSP. The address of the LSP is specified as the first parameter of the command line. The client constructs a query of type DAV:basicsearch and sends it to the LSP. The query retrieves all properties of the documents that contain a text string specified in a second command line parameter. For example, the client application can be invoked using the following command:

java sdlip.examples.SimpleClient http://coke.stanford.edu:8080/simplelsp "test query"

SimpleClient uses the transport modules provided as part of the standard SDLIP toolkit. The client prints out the list of URIs of the found documents and the total expected number of results.

The complete Java code of the client is listed below:
 
package sdlip.examples;

import sdlip.SDLIPException;
/* holder for integer values */
import org.omg.CORBA.IntHolder;
/* transport module libraries */
import sdlip.helpers.*;
/* the following packages are imported so that DOM-aware XMLObjects can be used */
import sdlip.xml.dom.*;
import org.w3c.dom.*;
/* SDLIP XML element names */
import sdlip.SDLIP;

public class SimpleClient {

  public static void main(String args[]) {

    try {
      /* extract command line parameters */
      String lspUri = args[0];
      String queryStr = args[1];

      /* an SDLIP/CORBA or SDLIP/DASL transport module is created
       * depending on whether a URL or an IOR is supplied */
      ClientTransportModule tm = ClientTransportModule.create(lspUri);

      /* the query result will be held in a DOM-enabled sdlip.xml.dom.XMLObject
       * which is passed into the search call */
      XMLObject result = new XMLObject();
      /* total expected number of results for this query */
      IntHolder expectedTotal = new IntHolder();

      tm.search(12345,           /* some client SID */
                null,            /* search the default subcollection */
                new XMLObject("<basicsearch xmlns='DAV:'>" +
                              "  <where><contains>" + queryStr + 
                              "</contains></where>" +
                              "</basicsearch>"),
                10,              /* return 10 results only */
                null,            /* retrieve all properties */
                0,               /* do not maintain state */
                null,            /* no query options */
                expectedTotal,   /* place to return expectedTotal */
                new IntHolder(), /* place to return stateTimeout - don't care */
                new IntHolder(), /* place to return serverSID - don't care */
                new XMLObject(), /* place to return serverDelegate - don't care */
                result           /* place to return search result */
      );

      /* get the top XML element from an XMLObject */
      Element searchResult = result.getElement();

      /* iterate over all found documents & print docIDs and doc properties*/
      NodeList docList = DOMUtil.getChildElements(searchResult, SDLIP.doc);
      for(int i=0; i < docList.getLength(); i++) {

        /* print document ID */
        Element eDoc = (Element)docList.item(i);
        System.out.println("DOCUMENT: " + DOMUtil.getChildText(eDoc, SDLIP.DID));

        /* print all document properties in format NAME = "VALUE" */
        Element ePropList = DOMUtil.getChild(eDoc, SDLIP.propList);
        NodeList propList = DOMUtil.getChildElements(ePropList, "*");
        for(int j=0; j < propList.getLength(); j++) {
           Element eProp = (Element)propList.item(j);
           System.out.println("\t" + eProp.getTagName() +
                              " = \"" + DOMUtil.getText(eProp) + "\"");
        }
      }
      System.out.println("EXPECTED TOTAL: " + expectedTotal.value);

    } catch (SDLIPException sdlip) {
      System.err.println("SDLIP exception (" + sdlip.getCode() + "): " +
                          sdlip.getReason());
    }
  }
}

2.4 SimpleLSP

This section gives an example of a simple LSP which implements the Search and Metadata interfaces. The proxy provides access to a single collection "Dummy-Collection", and always returns the same two document descriptions for each query. The original query is printed out on the screen. The collection does not have any searchable properties. The only retrievable properties are DublinCore Title and Creator [DublinCore].

The LSP uses the SDLIP/DASL transport module. The transport module starts listening on the port specified on the command line. For example, the following command can be used to start the application on port 8080:

java sdlip.examples.SimpleLSP 8080

The complete Java code of the server is listed below:
 
package sdlip.examples;

import sdlip.SDLIPException;
/* transport module libraries */
import sdlip.helpers.*;
/* DOM-enabled XMLObject */
import sdlip.xml.dom.XMLObject;
/* holder for integer values */
import org.omg.CORBA.IntHolder;

public class SimpleLSP implements sdlip.Search, sdlip.Metadata {

  public void search(
       int clientSID,
       sdlip.XMLObject subcollections,
       sdlip.XMLObject query,
       int numDocs,
       sdlip.XMLObject docProps,
       int stateTimeoutReq,
       sdlip.XMLObject queryOptions,
       IntHolder expectedTotal,
       IntHolder stateTimeout,
       IntHolder serverSID,
       sdlip.XMLObject serverDelegate,
       sdlip.XMLObject result
       )
    throws SDLIPException {

    /* Makes sure that the XMLObject passed into query is
     * a DOM-enabled sdlip.xml.dom.XMLObject and converts it if not;
     * Print nicely formatted XML query */
    System.out.println("Query is:\n" + XMLObject.create(query).getPrettyString());

    /* deliver the result; in a real LSP this would access the underlying collection */
    result.setString("<SearchResult xmlns='http://interlib.org/SDLIP/1.0#' " +
                     "              xmlns:dc='http://purl.org/metadata/dublin_core#'>" +
                     "  <doc>" +
                     "    <DID>1</DID>" +
                     "    <propList>" +
                     "      <dc:Title>Fighting a Wind Mill</dc:Title>" +
                     "      <dc:Creator>Don Quijote</dc:Creator>" +
                     "    </propList>" +
                     "  </doc>" +
                     "  <doc>" +
                     "    <DID>2</DID>" +
                     "    <propList>" +
                     "      <dc:Title>Document Without Creator</dc:Title>" +
                     "    </propList>" +
                     "  </doc>" +
                     "</SearchResult>");
    /* set the total expected number of results */
    expectedTotal.value = 2;
  }

  /* Methods of the source metadata interface */

  public void getInterface(sdlip.XMLObject version) throws SDLIPException {

    version.setString("<SDLIPInterface xmlns='http://interlib.org/SDLIP/1.0#'>" +
                      "  <SearchInterface/>" +
                      "  <MetadataInterface/>" +
                      "</SDLIPInterface>");
  }

  public void getSubcollectionInfo(sdlip.XMLObject subcolInfo) throws SDLIPException {

    subcolInfo.setString("<subcolInfo xmlns='http://interlib.org/SDLIP/1.0#'>" +
                         "  <subcol>" +
                         "    <subcolName>Dummy-Collection</subcolName>" +
                         "    <subcolDesc>This collection always returns " +
"two results independent of the query</subcolDesc>" +
                         "    <defaultSubcol/>" +
                         "    <queryLangs/>" +
                         "  </subcol>" +
                         "</subcolInfo>");
  }

  public void getPropertyInfo(String subcolName, sdlip.XMLObject propInfo) throws SDLIPException {

    if(!"Dummy-Collection".equals(subcolName))
      throw new SDLIPException(SDLIPException.INVALID_SUBCOLLECTION_EXC,
                               "The only subcollection available is 'Dummy-Collection'");

    propInfo.setString("<propList xmlns='http://interlib.org/SDLIP/1.0#'" + 
                       "          xmlns:dc='http://purl.org/metadata/dublin_core#'>" +
                       "  <dc:Title><retrievable/></dc:Title>" +
                       "  <dc:Creator><retrievable/></dc:Creator>" +
                       "</propList>");
  }

  public static void main(String args[]) throws Exception {

    /* Instantiate an SDLIP/DASL transport module
     * This also starts the server on the given port. Note that
     * one LSP may be passed into the constructor, saving one
     * call to AddLSP() */
    new ServerDaslTransport( Integer.parseInt(args[0]), new SimpleLSP() );
  }
}

Running SimpleClient against the SimpleLSP application produces the following output on the server side:

java sdlip.examples.SimpleLSP 8080
Query is:
<a:basicsearch xmlns:a="DAV:">
        <a:select>
               <a:allprop/>
        </a:select>
        <a:where>
                <a:contains>test</a:contains>
        </a:where>
</a:basicsearch>

The client's output is listed below:

java sdlip.examples.SimpleClient http://localhost:8080 "test query"
DOCUMENT: 1
        http://purl.org/metadata/dublin_core#Title = "Fighting a Wind Mill"
        http://purl.org/metadata/dublin_core#Creator = "Don Quijote"
DOCUMENT: 2
        http://purl.org/metadata/dublin_core#Title = "Document Without Creator"
EXPECTED TOTAL: 2

2.5 XMLObject

The rationale for defining a special interface to represent an XML structure is twofold. On the one hand, it provides a standard minimalist way of exchanging XML structures between LSP implementations and transport modules. On the other hand, it enables efficient realizations of XML structures if needed. A simple implementation of the XMLObject interface that is included in the distribution is depicted below:
 
package sdlip.helpers;

public class SimpleXMLObject implements sdlip.XMLObject {
    String xml;

    public String getString() {
        return xml;
    }
    public String setString( String XMLStr ) {
        xml = XMLStr;
    }
}

For reasons of efficiency, the SDLIP toolkit provides an implementation of the XMLObject interface that uses the Document Object Model [DOM] representation to minimize the number of parsing processes. It offers a set of methods that allow the XML structure to be manpulated by using DOM interfaces. The class implementation is located in the package sdlip.xml.dom. The two essential new methods that it provides are:

    public Element getElement();
and
    public void setElement(Element e);

Thus, an XMLObject can be viewed as a tree of Elements, which may be accessed conveniently via appropriate methods. The complete documentation of the DOM-enabled XMLObject included in the SDLIP distribution can be found here. The toolkit utilities, which are described in the following section, include methods for extracting elements from XML elements.

2.6 Toolkit Utilities

The SDLIP toolkit contains a number of "convenience" functions. The developers are not required to use them in their implementations. The provided classes facilitate access to XML structures and implement some fundamental concepts of the SDLIP protocol like the "parking meter" and the document selection. These utilities help the developers to get started writing powerful services with less effort.

DOMUtil

The Document Object Model (DOM) defines the basic interfaces for manipulating XML structures as trees of XML elements. To keep DOM simple, some often needed functions purposefully were not included into the interface when they can be derived from the basic set of methods. The class sdlip.xml.dom.DOMUtil provides a number of static functions which are summarized in the table below.
 
Interface Description
Element base64Encode(Document d, byte[] b) Binary data must be encoded in XML using some character-based encoding. For interoperability, SDLIP requires all binary data to be encoded using BASE64 standard. This method creates an element tagged with <base64> containing the BASE64-encoded binary data (the tag <base64> belongs to the SDLIP namespace).
byte[] base64Decode(Element e) Translated encoded binary data within <base64> tag into an array of bytes.
Element createTextNode(Document d, String name, String value) Creates an XML element tagged with the given name containing a PCDATA element with the content given in value
Example:
createTextNode(d, "mytag", "some text") returns an XML structure <mytag>some text</mytag>
Element getChild(Element parent, String tagName) Returns the first child of the parent element having the given tagName.
NodeList getChildElements(Element parent, String tagName) Returns a list of all children of parent having the given tagName.
String getChildText(Element parent, String tagName) Delivers the textual content of the specified child element. 
Shortcut for getText(getChild(parent, tagName))
Element getDescendant(Element e, String tagName) Returns the first descendant of the given element in preorder traversal.
Element getDescendantText(Element e, String tagName) Delivers the textual content of the descendant. 
Shortcut for getText(getDescendant(e, tagName))
String getText(Element e) Returns the textual content of the given element. 
Example:
if e corresponds to <mytag>some text</mytag>, this method would deliver the string "some text".

Note that the interface org.w3c.dom.Document is used in DOM as a factory for creating XML Elements. Therefore, it must be passed into all methods that create Elements. In DOM, Elements cannot be shared between different documents. If you intend to use the DOM-enabled implementation of XMLObject, you have to create all Elements using the Document instance delivered by the static method XMLObject.getDocument().

Timer

The SDLIP protocol uses the concept of a "parking meter" for the state maintenance between LSPs and clients. Once a parking meter expires, a "parking enforcement" authority should take appropriate measures like releasing LSP cache etc. The utility sdlip.helpers.Timer supports this metaphor by providing the developer with a simple asychronous notification mechanism.

When created, a timer is initialized with a number of seconds until it expires. On expiry the timer calls a special method on the callback object passed to it during initialization. The callback object has to implement the single method

public void timeout(Object handle)
of the interface sdlip.helpers.Timeoutable. The timer can be used in two scenarios:
  1. the session is a first-class object and is notified directly by the timer
  2. the timer notifies a monitor like LSP and gives it the "handle" to the timed out session
In first case, a session has to implement the interface Timeoutable. A timer is created using the constructor
Timer timer = new Timer(5, session);
Once the timer expires, it calles the method session.timeout(null).

In the second scenario, some other object like the LSP itself implements the interface Timeoutable. A timer for the given session could be created using the constructor

Timer timer = new Timer(5, lsp, session);
On expiry the timer calls the method lsp.timeout(session) and the LSP has to free the session's resources.

The timeout interval of a timer can be extended by X seconds using the method invocation

timer.setTimeout( timer.getTimeout() + X );
For efficiency, all timer objects use a single thread. By default, the timers created are "robust". That means that once a timer expires, an extra thread is created and the timeout() method is called within this new thread. Thus, other timers are not affected. However, if the timeout() method returns immediately in a particular implementation, "regular" timers can be deployed for even more efficiency. A "regular" timer is created using the following constructor:
Timer timer  = new Timer(5, lsp, session, Timer.REGULAR);

RangeEnumerator

The SDLIP result access interface assumes logical ordering of the documents in the result set. A client may request more information passing a document selection to the LSP. An example of such selection is "1,3-5,7-" which corresponds to the set of integers "1,3,4,5,7,8,9,10,...". The class sdlip.helpers.RangeEnumerator can be used to enumerate a document selection. All integers of a selection can be printed out as follows:
RangeEnumerator en = new RangeEnumerator("1,3-5,7-");
while(en.next())
    System.out.println("Next integer: " + en.get());
The method next() must be called before any invocation of get(). An enumeration can be started from an arbitraty position, e.g. 4, using the call
en.set(4);
This method can also be used to verify whether the range contains some given integer X:
en.set(X);
if(en.next())
    // X is within the range

2.7 Useful Example Applications

Apart of the SimpleClient and SimpleLSP applications the distribution file examples-1_x.jar includes the following useful applications:
 
DiscoverMetadata Usage: java sdlip.examples.DiscoverMetadata <URL_or_IOR>

Fetches and prints out the complete metadata exposed by the LSP.

Client Usage: java sdlip.examples.Client <URL_or_IOR> <method> ...method parameters...

Allows to invoke all methods defined in the Search and ResultAccess interfaces on an LSP using a command line tool. The <method> parameter corresponds to the name of the method to be invoked. Following methods are supported:

  search             <clientSID> <queryStr> <numDocs> <stateTimeoutReq>
  getSessionInfo     <serverSID>
  getDocs            <serverSID> <reqID> <docsToGet>
  extendStateTimeout <serverSID> <additionalTime>
  cancelRequest      <serverSID> <reqID>

SwitchBoardLSP Usage: java sdlip.examples.SwitchBoardLSP <port>

Implements a stateful LSP for the Web site switchboard.com. This LSP runs on a specified port and accepts a DAV:basicsearch query containing a DAV:contains operator. The query must contain a last name of the person to search for and may optionally be preceded by the person's first name. For every query, all available properties are returned which include the full name, address and phone number.

This LSP implements all currently defined SDLIP interfaces and methods. SwitchBoardLSP can be queried using the Client example application. It demonstrates the usage of RangeEnumerator and Timer utilities described in previous section. The LSP maintains state for maximum 10 minutes unless the extendStateTimeout method is called by the client. 

3 Java Binding

The Java binding specifies how the parameters of SDLIP operations are realized in Java implementations. In SDLIP-Core, these operations are specified using CORBA's Interface Definition Language (IDL). The SDLIP Java binding closely follows the standard CORBA IDL/Java binding. In particular, since the Java language does not support multiple out parameters for methods, these return values are wrapped into so-called "holders". These holders are classes containing a single public variable. SDLIP only uses integers and XML encoded strings. The Java binding therefore uses the single "holder" class called org.omg.CORBA.IntHolder for wrapping integer return values. This class is included in the distribution. XML structures are held in XMLObjects.

Recall that SDLIP operations of concern in the Java binding always occur within one address space: either between client application and client transport module, or between LSP and server transport module. Therefore, the Java binding makes two minor modifications to the Java bindings generated by an IDL compiler:

  1. SDLIPException does not inherit from org.omg.CORBA.UserException class.
  2. Since XMLObject already serves as a "holder" for XML data, all occurrences of XMLObjectHolder are replaced with XMLObject to avoid double-wrapping the XML strings.
package SDLIP;

import org.omg.CORBA.IntHolder;
 

public interface XMLObject {
    public String getString() throws SDLIPException;
    public void setString( String XMLStr ) throws SDLIPException;
}
 

public interface Search {

  public static final int RETURN_ALL_DOCS = -1;
  public static final int MAINTAIN_UNLIMITED = -1;
  public static final int UNKNOWABLE = -1;
  public static final int NOT_YET_KNOWN = -2;

  public void search(
       int clientSID,                      // Client-side session ID
       XMLObject subcols,                  // Choice of collections to search
       XMLObject query,                    // The query
       int numDocs,                        // Number of docs to return or RETURN_ALL_DOCS
       XMLObject docPropList,              // Properties to return for each doc
       int stateTimeoutReq,                // Number of seconds to maintain or MAINTAIN_UNLIMITED
       XMLObject queryOptions,             // Additional info for the LSP
       IntHolder expectedTotal,            // Expected number of results
                                           // or UNKNOWABLE or NOT_YET_KNOWN
       IntHolder stateTimeout,             // state timeout set at the server
       IntHolder serverSID,                // Server-side session ID
       XMLObject serverDelegate,           // Address for future service requests
       XMLObject result )

    throws SDLIPException ;

}
 

public interface ResultAccess
{
  public static final int CANCEL_ALL_REQUESTS = 0;

  public void getSessionInfo(
        int serverSID,
        IntHolder expectedTotal, // or UNKNOWABLE or NOT_YET_KNOWN
        IntHolder stateTimeout
        )
    throws SDLIPException ;

  public void getDocs(
        int serverSID,
        int reqID,
        XMLObject docPropList,
        String docsToGet,
        XMLObject res )
    throws SDLIPException ;

  public void extendStateTimeout(
     int serverSID,
     int additionalTime,
     IntHolder timeAllotted )
    throws SDLIPException ;

  public void cancelRequest(
       int serverSID,
       int reqID )
    throws SDLIPException ;

  public void removeDocs(
    int serverSID,
    String docsToRemove )
    throws SDLIPException ;
}
 

public interface Metadata {

  public void getVersion(
    XMLObject version /* XML-encoded info about the versions of each
           supported interface. */
    )
    throws SDLIPException;

  public void getSubcollectionInfo(
       /* XML list of subcollections and their
          supported query languages */
       XMLObject subcolInfo
       )
    throws SDLIPException;

  public void getPropertyInfo(
         /* {null} If not supplied, request for default
     subcollection. */
         String subcolName,
         /* Acceptable properties of
     specified subcollection, and whether they are
     searchable/retrievable */
         XMLObject propInfo
         )
    throws SDLIPException;
 

public class SDLIPException extends Exception {

  public static final short INVALID_REQUEST_EXC = 400;
  public static final short UNAUTHORIZED_EXC = 401;
  public static final short PAYMENT_REQUIRED_EXC = 402;
  public static final short NOT_FOUND_EXC = 404;
  public static final short ILLEGAL_METHOD_EXC = 405;
  public static final short REQUEST_TIMEOUT_EXC = 408;
  public static final short QUERY_LANGUAGE_UNKNOWN_EXC = 450;
  public static final short BAD_QUERY_EXC = 451;
  public static final short INVALID_PROPERTY_EXC = 452;
  public static final short INVALID_SESSIONID_EXC = 453;
  public static final short INVALID_SUBCOLLECTION_EXC = 454;
  public static final short MALFORMED_XML_EXC = 455;
  public static final short SERVER_ERROR_EXC = 500;
  public static final short NOT_IMPLEMENTED_EXC = 501;
  public static final short SERVICE_UNAVAILABLE_EXC = 503;

  short code;
  XMLObject details;

  public SDLIPException(short code, String message) {

    super(message);
    this.code = code;
  }

  public SDLIPException(short code, String message, XMLObject details) {
    this(code, message);
    this.details = details;
  }

  public short getCode() {
    return code;
  }

  public String getDescription() {
    return super.getMessage();
  }

  public XMLObject getDetails() {
    return details;
  }
}

4 References

[DOM] DOM Working Group: Document Object Model (DOM) Level 1 Specification, Version 1.0, W3C Recommendation 1 October, 1998 
http://www.w3.org/TR/REC-DOM-Level-1/
[DublinCore] Dublin Core Metadata Initiative: The Dublin Core: A Simple Content Description Model for Electronic Resources, 1999 
http://purl.org/dc/
[SDLIP-Core] The Simple Digital Library Interoperability Protocol (SDLIP-Core), 1999 
http://www-diglib.Stanford.EDU/~testbed/doc2/SDLIP/