|
The Simple Digital Library Interoperability Protocol (SDLIP-Core) |
Contents:
1.1 Grouping of Operations Into Interfaces
1.2 Different Ways of Using SDLIP
1.3 When Can Servers Discard State?
1.4 Implementation Architecture
2.1 Search Interface
2.2 The Result Access Interface
2.3 The Source Metadata Inferface
3.1 Property Lists
3.2 Exceptions
3.3 SearchResult
3.4 Subcollection Specifications
3.5 Source Metadata
3.6 Server Delegates
4. Implementing SDLIP With IETF's DASL (details in separate
document!)
5. Implementing SDLIP With CORBA (details in separate document!)
Appendix A: Error codes and their meanings
This document describes the Simple Digital Library Interoperability Protocol (SDLIP; pronounced S-D-Lip). Clients use SDLIP to request searches to be performed over information sources. The result documents are returned synchronously, or they are streamed from service to client as they become available. Implementations can be constructed over HTTP or CORBA based transports. In fact, any search service can be accessible through both kinds of transports at the same time. Implementations for IETF's HTTP based DASL protocol, and for CORBA are available.
Figure 1 shows a typical example of where SDLIP is relevant.
|
The 'Library Service Proxy' (LSP) in Figure 1 wraps two external sources. Through its back end, the LSP interacts with the external services via the transport and higher-level protocols required for these services. One LSP may thus serve out multiple 'subcollections'. At the front end, the proxy supports SDLIP. Of course, an information source may itself provide SDLIP access. In that case, the client can interact directly with the source.
The basic interaction is for the client to request a search across the network. Part of the request specifies how many documents are to be returned initially, once the search will be complete. The request also specifies which portion of each document is to be returned. For example, the client might ask for authors and titles of the first 10 documents to be returned right away. The client may later request more documents of the result, or it may request additional portions of the documents already delivered.
We define two levels of SDLIP capabilities: SDLIP-Core implements synchronous operations only. Clients invoke search operations on servers, and 'hang' until the operations return with the result. This document focuses on SDLIP-Core. The second level, SDLIP-Asynch adds the ability for clients to invoke search operations that return immediately. Services then deliver result information back to the client through one or more callbacks. SDLIP-Asynch thus subsumes SDLIP-Core. SDLIP-Asynch's additional capabilities are described in the separate SDLIP-Asynch document.
SDLIP has the following goals:
Figure 2 shows how the SDLIP operations are divided into three interfaces.
|
The result access interface allows client applications to access the set of result documents, wherever that set is maintained. The source metadata interface, finally, allows clients or services such as metasearch engines to question a library service proxy about its capabilities. This might include a list of the subcollections served by the LSP, or the attributes that may be searched.
The partitioning into interfaces has three advantages. First, the interfaces make it clear which role each operation plays, and for which participants of the search transaction the operation needs to be implemented. Second, the interface notion enables clean expansions to the protocol in the future. One can subclass the existing interfaces to accommodate more elaborate facilities, or one can add additional interfaces. For example, one could use interface inheritance to add operations to the source metadata interface, if in the future some LSPs wish to export additional metadata, or wish to export that data in some new format. Or maybe one might want to add a whole new interface for financial transactions. Neither of these expansions would impact the existing core protocol. A third advantage of organizing SDLIP's operations into functionally coherent interfaces is that for some scenarios, or 'configurations', some of the interfaces are not needed. Rather than having to list various operations to be dropped for these cases, we can then simply say that interface X is not needed. For example, if a server is stateless, it does not need to implement a result access interface, because all results are returned in the operations of the search interface.
The minimum a stateless SDLIP server needs to implement is the search interface. Clients can rely on it being present. If a server maintains result sets which clients can access, then the server also needs to implement the result access interface. Though not required, all servers should implement the source metadata interface. As documented below, this is a very simple interface to provide.
Figure 3 illustrates how SDLIP-Core can be used in three configurations. The simplest is the configuration of Figure 3a. It features one library service proxy serving the information, and a single client application object. The client submits the search request synchronously via the service's search interface. The results are returned as part of that call.
Figure 3b shows a somewhat more sophisticated usage in which the server maintains the result set of the search, at least for a while. Later, the client might, again synchronously, ask for more documents of the same result set (2).
Figure 3a: Synchronous requests for search. Server does not save any state |
Figure 3b: Synchronous requests for search, and follow-up information |
Figure 3c: Server delegates delivery |
Figure 3: Different Configurations for Using SDLIP-Core |
Figure 3c, finally, illustrates how services can delegate interactions with clients if the service object gets overloaded (2), yet wishes to maintain state for the client. When servers return (partial) results from a search operation, they can also specify a future contact address (3). All future interactions regarding the result set are made by the delegate. If the client wishes, for example, to recall more documents of the result set than it asked for in its initial search request, then it will use the delegate's address, rather than the main address (4). More configurations are possible if the asynchronous SDLIP extensions are also supported.
If SDLIP claims that it enables both stateful and stateless implementations of servers, how do clients and servers agree on whether or not there is state at the server? The notion of a 'state parking meter' takes care of this. It is a very simple notion.
When clients submit a search request to a server, they include the amount of time they would like the service to retain the state associated with the session. The server returns the actual time it is willing to maintain the result set and related state. For a completely stateless server, this time could be zero. The clock starts ticking right after the search call returns. Once the time has expired, the server is free to discard all state associated with the search. The client, meanwhile, has the option of invoke an 'extend state timeout' operation on the server to add additional time. The server has the option of granting or refusing the request. This is rather like the client feeding a parking meter. Note that this scheme ignores some uncertainties in that the server and client clocks might not be synchronized. Also, the server starts its clock when it returns from the search call, while the client starts counting down when the return process is complete. Since state maintenance times are expected to be large compared to these differences, the issue is ignored in favor of simplicity.
Of course, if clients and servers are to converse about a result set, they need the ability to refer to the set, and to (potentially multiple, parallel) operations the client invokes on the service. These references are provided through a server session ID, and client request IDs, respectively: One of the values a server returns with every new search request is a 'cookie' that uniquely identifies the result set within the server. The client must pass this session ID to the server whenever the client requests more information from the result access interface.
In addition to referencing a result set, clients may need to reference operations they have invoked, and that have not returned. For example, the client might use multiple threads to invoke several follow-up requests to an existing result set. The client might then wish to use an additional thread to cancel one of the hanging invocations. In order to identify multiple requests, many of the SDLIP operations include a client-side request ID. Like the server session ID, client request IDs are cookies. The server may compare request IDs for equality, but other than that, these IDs are opaque to the server. In addition to the server request ID, clients pass their own request ID with every result access request. When cancelling an operation, clients use these two IDs to uniquely identify which operation they want to stop.
Notice that this scheme does not require server or client to generate any globally unique identifiers. Server session IDs must be unique only within the server. Similarly, client request IDs need to be unique only within the client.
One of SDLIP's key goals is to make it very easy to build SDLIP clients, and to construct library service proxies (LSPs) that wrap arbitrary sources. Figure 4 shows how SDLIP implementations accomplish this. Implementors need to produce only the client application and/or the library service proxy in figure 4. Everything else is taken care of by standard libraries. An important point: Client applications and services need not be aware of the methods used for transporting operation requests and replies. The transmission of requests and replies might be accomplished through different 'transport bindings': CORBA, HTTP, or, maybe in the future, some other means. Client applications are unaffected by the transport binding. A client application merely creates a client transport module object in its local address space. This module implements the SDLIP interface. The client then invokes SDLIP operations on this local module. The module packages the operations for transport via one of the supported SDLIP transport bindings. Any given client transport module instance uses one particular transport binding. If a different binding is to be used, the client application simply instantiates a different class of transport module.
Implementation of an LSP is analogous. The LSP is an object that implements one or more of the SDLIP interfaces. It is similar in spirit to Web servlets. The LSP's operations are invoked locally by the server transport module object. Again, the details of transport are of no concern to the LSP. One LSP could provide service via different transport modes simply by instantiating two kinds of server transport modules. That is, an LSP could make itself available via both CORBA and [DASL] transports at the same time, simply by instantiating a CORBA server transport module and a DASL transport module.
Please see the documentation for the transport module libraries (Java or C++) for examples of code.
Let's get to specifics. Section 2 will describe SDLIP operations in detail. Section 3 explains the XML structures used with SDLIP. Section 4 sketches how the IETF Distributed Authoring, Searching and Locating (DASL) facility can be used as an SDLIP transport. Section 5, finally, summarizes SDLIP's mapping to a CORBA transport. The Appendix A lists the error codes used in SDLIP.
We describe each interface in turn. For each interface, we list the associated operations, and explanations for each parameter. Whenever a parameter is a specially encoded XML string, we just state its purpose. Section 3 defines the XML formats in more detail. For clarity, we do summarize the simple XML property list format ahead of time below. A concise Interface Definition Language (IDL) specification is available in the combination of SDLIPCore.idl and SDLIPLocal.idl.
Some technologies that might be used to implement an SDLIP transport allow parameter defaulting. For CORBA transports, defaults are NULL values. For HTTP based transports, a defaulted parameter is simply left out. SDLIP enables implementations to make use of such defaulting facilities by specifying default values for as many parameters as possible. Apart from saving on required bandwidth, these defaults also make it easier to gracefully degrade interactions in which either the client or the service are not truly SDLIP compliant. For example, DASL clients might interact with SDLIP services. These clients will not include some of the parameters in their search requests. Defaulting these parameters ensures that the SDLIP servers can still provide a reasonable level of service. Whenever a parameter can be left out in the specifications below, its default value is specified in curly braces in the parameter explanations.
First, a couple of conventions we follow, and some very brief preview hints to set the reader at ease about how these operations work in an implementation.
Entity Addresses: When we use the word 'address' to specify the target of a method invocation, we could use the term 'object identifier' (OID). We instead use the term 'address', so that the mapping to HTTP based implementations is more obvious. The realization of 'address' in that case is, of course, a URL.
Property Lists: Some of the method parameters below are property lists. These are XML-encoded lists of attribute value pairs. For example:
<propList> <QualityOfService>Fastest</QualityOfService> <UserID>Miller</UserID> </propList>
We use property lists as a catch-all expansion facility. Since property lists can be as large as needed, they are a great way to take care of special needs that arise in the future, and cannot be included in a core protocol, such as SDLIP. For the formal DTD of property lists, see Section 3.1.
XMLObject: We introduce the following type that is used when an operation's parameter is supposed to be an XML-encoded string:
interface XMLObject { string getString(); void setString(in string XMLStr); };
All XML-encoded strings are packaged into an XMLObject. So, in the specifications below, you might see:
...
void search() { XMLObject subcols, // Choice of collections to search w/in LSP. ...
}
This parameter specification calls an XMLObject to be passed into the call. This object is to contain an XML string with the subcollections to be searched. Again, the formats of all XML strings are explained in Section 3. Implementations of this interface come with the standard SDLIP transport module libraries.
In its simplest form, XMLObject just stores an XML string and provides the two operations for setting and getting that string. Individual implementations may subclass XMLObject and provide richer services to SDLIP client/server applications. For example, the transport modules for DASL and CORBA provide some of the facilities specified in the Document Object Model (DOM). This makes it very easy for clients to extract values from the XML they receive as results of SDLIP operations, and to construct valid XML to pass into the calls.
If an XML element is to contain binary data, such as images, maps, or other digital objects, it needs to be encoded in base64 [BASE64]. Base64 character data must be wrapped into an SDLIP:base64 element:
<my:bindata> <SDLIP:base64>SGV5LCB5b3UglzIG1lc3Mh</SDLIP:base64> </my:bindata>
The syntax of the tag 'SDLIP:base64' uses the notion of XML name spaces. The tag denotes the identifier 'base64' in the 'SDLIP' name space. Tags without name space specifications are understood to be in the SDLIP name space. For example, the <propList> tag that introduces a property list really stands for <SDLIP:propList>. Clients and server programs do not need to include the SDLIP name space specification when constructing the XML tags that are used for the built-in SDLIP XML structures.
Error reporting (Exceptions): SDLIP's approach to exceptions is to be as robust as possible. This means that if a request can be filled at least partially, the operation proceeds. Of course, if something really does go wrong, LSPs may raise exceptions in response to operations invoked on them. The information returned with an exception is always an XML-encoded string as defined in Section 3.2. It includes information about the type of exception, a short description, and possibly additional debug information. Like all other return information, exceptions travel from the server-side LSP to the client via the transport modules. These modules are responsible for transmitting the exceptions across the wire. When the client transport module receives an exception it raises an error condition for the client application to catch. This is done using the error signaling facility of the programming language common to the transport module and the client application. Examples are the C++ throw/catch mechanism, or the analogous facility in Java. Exception objects have the following interface:
interface SDLIPException { int getCode(); int getReason(); XMLObject getDetails(); }
The search() method is the basic way of submitting a search to a server. We distinguish between IN parameters and OUT parameters. IN parameters hold information passed to the callee. All information that operations return to callers are passed back in OUT parameters. Consequently, the return type of all operations is void. For distributed object implementations, OUT parameters are a familiar notion. The SDLIP DASL binding specifies how OUT parameters are returned via HTTP.
void search( Long clientSID, // {0} Client-side session ID (unique within client) XMLObject subcols, // {service's default (or sole) subcollection} // Choice of collections to search w/in LSP. XMLObject query, // The query. // (e.g. <ADL:GazeteerLang>Lake Tahoe</ADL:GazeteerLang>) Long numDocs, // {-1} Number of documents to return right away (-1: all) XMLObject docProps, // {all possible properties} Properties to return // for each result doc // (e.g. <propList><Abstract/><Title/>, ...) Long stateTimeoutReq, // {0} Request for number of seconds to // maintain state at server. -1: request unlimited time XMLObject queryOptions, // {none} Additional info for the LSP. OUT Long expectedTotal // {0} -1 if unknowable. -2 if not yet known. OUT Long stateTimeout, // {0} Time server is willing to maintain state OUT Long serverSID, // {0} ID by which server identifies this session OUT XMLObject serverDelegate, // {same as for original query} For followup requests OUT XMLObject result // XML-Encoded result list. )
The client invents a clientSID, which allows the service proxy to refer to this query request later on. This ID only needs to be as unique as the client requires. LSP implementations use their own internal mechanisms to separate sessions with different clients.
The subcols parameter is used when one LSP serves out many collections. This is sometimes true for commercial information providers, or for Z39.50 sites. If an LSP only serves one source of information, this parameter can be empty. A special case of subcollection are the result sets of previous searches: For the purpose of query refinement, clients must be able to specify such existing result sets within the LSP. The subcollection string is formated like this (for details on server sessionIDs, see later in this document):
<subcols> <subcolName>[subcollection name]</subcolName> <resSet>[server sessionID]</resSet> <resSet>[server sessionID]</resSet> <subcolName>[subcollection name]</subcolName> <resSet>[server sessionID]</resSet> </subcols>
One or more result sets or subcollections may be specified in any order. Of course, the result sets referenced must still be 'alive', that is clients must have fed the state timeout parking meter. See Section 3.4 for details on the format of this string.
The query parameter contains the query itself. This is an XML string whose outermost tag names the query language being used. The value inside depends on the query language. For example, a query for a Dialog Corporation database might look like this:
<Dialog:StandardQuery> au=Miller and py=1994 <Dialog:StandardQuery>
The same query issued using the DASL basicsearch query language might look like this:
<basicsearch xmlns="DAV:" xmlns:Dialog="http://dialog.com/"> <where> <and> <eq> <prop><Dialog:au/></prop> <literal>Miller</literal> </eq> <eq> <prop><Dialog:py/></prop> <literal>1994</literal> </eq> </and> </where> </basicsearch>
The numDocs parameter specifies how many documents the LSP is to return initially. A value of '-1' means 'return all documents that are found'. Remember that the client may use the result access interface later on to request additional documents.
The docProps parameter is a property list. Properties are the names of document properties the LSP is to return for each of the result documents. One example value is:
<propList> <Title/> <Author/> </propList>
This is a somewhat funny looking property list: none of the properties have values. This lack of values is indicated by the trailing '/'. This syntax is standard XML.
A more involved example for a document property specification is:
<propList> <USMARC:245/> <DublinCore:Creator/> </propList>
The detailed format of the property names is not part of SDLIP. But SDLIP does assume that an XML namespace notation may be used to introduce the 'attribute model' within which the name of the respective attribute should be interpreted. For example, USMARC:245 is assumed to denote 'author' in the Library of Congress' USMARC attribute model.
The stateTimeoutReq is the number of seconds the client would like the server to hold on to the result set. After that time, the server may discard the result state. A value of -1 requests that the server hold state indefinitely, or until the client calls cancelRequest().
The queryOptions parameter is a property list (i.e. a <propList> XML structure) that is not further defined by SDLIP. Clients and services may use properties to hold additional information regarding the query being transmitted. For example, the property list might be used to pass authorization information, financial arrangements, or quality of service specifications to the LSP.
The remaining parameters are all OUT parameters. They contain the following information.
The expectedTotal is the total number of hits found. Sometimes, servers cannot tell right away how many hits will be found. In this case, expectedTotal is set to -2. If, for example, the client asks for only the first 10 hits to be returned right away, then a stateful server may return right away, once the first 10 hits have been retrieved from the underlying collection. The server would then continue to build its result set while the client processes the initial results. The client can later use the result access interface's getSessionInfo() to find out the final number of hits. Sometimes, a total number of hits will never be known. Servers indicate this by setting expectedTotal to -1. Example: a service that takes a single query and keeps filling a result set forever. A news subscription service might do this. Clients would pull information from the result set at their convenience.
The stateTimeout parameter is the number of seconds the server is willing to hold the state. Recall that this number may be different from the number of seconds requested in the stateTimeoutReq. In particular, it may be zero if the server is stateless.
The serverSID is the session ID the server uses to identify this session. All correspondence with the server regarding this session must include that ID. Using a separate session identifier for the client and the server avoids having to invent globally unique IDs.
The serverDelegate is important only for servers that are stateful and are performing load balancing. This parameter is an XML structure listing the addresses of server-side delegates that are willing to serve follow-on requests over the result set. Much of the time, this parameter will be defaulted. The default means that follow-on requests are to be directed to the same address as the original query. When not defaulted, the format of this return parameter looks like this:
<redirect> <serverDelegate>[URL_or_IOR_1]</serverDelegate> <serverDelegate>[URL_or_IOR_2]</serverDelegate> ... </redirect>
The client can pick one of the delegates and use it for follow-on requests: each of the delegates implements the result access interface. Client applications can use the transport module facilities to make this switch easy: The module contains a static method that takes one of the 'URL_or_IOR' strings and returns a transport object of the right kind. The client application can then invoke the result access methods on that object.
The search() operation blocks until the LSP has finished setting up the result set. Then the result is returned in the result OUT parameter. This return type is an XML string that contains a list of numDocs documents (if that many were indeed found). For each document, only the attributes specified in docProps are returned. The format of the search result type is explained in Section 3.3. Note that if some of the requested properties are not available, the server still returns the other requested properties that can be retrieved.
Once some of the documents have been returned, clients might want to get more documents than they had originally requested, or they might need to request additional properties of documents they already have. This is accomplished through the result access interface.
The getSessionInfo()allows clients to find out about the result set, especially if the initial return parameters of the search() operation could not return the total number of hits:
void getSessionInfo() Long serverSID, // {0} OUT Long expectedTotal,// {0} -1 if unknowable. -2 if not yet known. OUT Long stateTimeout // {0} Total number of seconds server is willing to // hold state. -1 if forever. )
The expectedTotal return parameter is -1 if the remote LSP has indicated that the total number of documents
cannot be determined. If the callee simply does not know yet, but there is a chance that it will find out later,
a -2 is returned.
The stateTimeout is the total number of seconds that the server is willing to hold the state without
receiving an extendStateTimeout() call.
The getDocs() operation is the means by which clients ask for more documents, or for additional portions of partially transfered documents in a result set. The key notion is that client and server think of the result documents as being arranged in order within a result array. Documents are referenced by their index into that array.
void getDocs( // Returns a SearchResult XML string Long serverSID, // {0} ... of the original query Long reqID, // {0} new each time XMLObject docProps, // {all possible properties} properties to get. // A property list. String docsToGet, // {1-} document indexes to retrieve. 1- for all. OUT XMLObject result // the XML-encoded search result. )
The serverSID is the session number that was established during the original search request. The reqID is an identifyer for this particular request within the overall session. It can be used to cancel this particular delivery request via a separate thread (see cancelRequest()).
The docProps is the same kind of parameter that is used in the search() call: it specifies which document properties should be included with each document.
The docsToGet parameter specifies which documents to get. Documents are identified by their index into the result array. The array is one-based. We make the array start with the index '1' to allow document range descriptions that are familiar from document print dialogs in common user interfaces. For example, "1,3,5-7" will retrieve documents 1, 3, 5, 6, and 7. A dangling "-" after a number denotes an open range. For example: "3-" means "all documents beginning with the third one". Similarly: "1-" means "all documents in the result set".
When the originally agreed upon time limit for result state maintenance is about to expire, and the client wants the server to maintain the result set for an additional amount of time, the client needs to call extendStateTimeout():
void extendStateTimeout(// Request more time for server to Long serverSID, // {0} maintain search result state. Long additionalTime, OUT Long timeAllotted // Num of secs server agrees to maintain state )
The LSP returns the amount of additional time it is actually willing to maintain the state for the client.
Alternatively, sometimes, a client may want to release server state prematurely, or it may have changed its mind about a request it delivered earlier. It can use cancelRequest() for these situations. (Even though the getDocs() call is synchronous, a multi-threaded client might use a separate thread to contact the server and cancel a 'hanging' getDocs()):
void cancelRequest( Long serverSID, // {0} Long reqID // {0} )
The reqID is 0 if all outstanding requests for this session are to be canceled. This has the semantics of closing the session, and allowing the server to free its resources. If reqID is not zero, only the corresponding request within the session is canceled.
By source metadata we mean information about the service itself, and about the collections that are being served out. In this context, one could ask many complicated questions. Example: 'Which of your subcollections has a searchable DublinCore:Creator property?' While such questions are certainly of interest, SDLIP takes a minimalist approach. The emphasis of the source metadata interface is to provide the most important information easily, but to require almost no implementation effort on the server side. This improves the chance that services will actually provide this interface, which is traditionally the most neglected facility in server implementations. In particular, the interface is defined in such a way that services can return one constant XML string for each request. If services are more ambitious, they may provide a subcollection called 'SourceMetadata' which can be queried like other subcollections.
The source metadata interface includes three operations:
The operation for asking sources about their interface versions:
void getInterface( // Get info about LSP's interfaces OUT XMLObject version // XML-encoded info about the versions of each supported interface. // Ex: // <SDLIPInterface> // <SearchInterface> // <version>1.0</version> // </SearchInterface> // <ResultAccessInterface> // <version>1.1</version> // </ResultAccessInterface> // <MetadataInterface> // <version>1.0</version> // </MetadataInterface> // </SDLIPInterface> )
The returned value contains information about all of the supported interfaces. If more than one version is supported, the <version> element may be repeated. See Section 3.5 for details on the information that is returned.
To get the names and supported query languages of all the subcollections a service makes accessible:
void getSubcollectionInfo( OUT XMLObject subcolInfo // XML list of subcollections and their supported query languages )
The getSubcollectionInfo() operation returns something like this:
<subcolInfo> <subcol> <subcolName>New York Times</subcolName> <defaultSubcol/> <queryLangs> <DAV:basicsearch/> <DialogCorp:standard/> <Z3950:RPN> </queryLangs> </subcol> <subcol> <subcolName>StockQuotes</subcolName> <subcolDesc> Current stock market values. Delayed by at least 15 minutes</subcolDesc> <queryLangs> <DAV:basicsearch/> </queryLangs> </subcol> </subcolInfo>
Three pieces of information are provided for each subcollection: Its name, an optional human-readable description of the subcollection's contents, a list of query languages that may be used to query the subcollection, and whether this is the service's default subcollection. Knowing the default subcollection is, of course, important when operation parameters that specify a service's subcollection are left out during operation invocations.
Finally, to get information about a service's supported document properties and attribute models:
void getPropertyInfo( // Returns a propList XML string String subcolName, // {null} If not supplied, request for default subcollection. OUT XMLObject propInfo // Acceptable attribute models and document properties for the // specified subcollection, and whether they are // searchable/retrievable )
This operation allows clients to retrieve information about which document properties may be searched or retrieved for the specified subcollection. Here is an example of what is returned in propInfo.
<propList> <DublinCore:creator> <searchable/> <retrievable/> </DublinCore:creator> <USMARC:245> <searchable/> <retrievable/> <phraseSearch/> </USMARC:245> <USMARC:711c> <retrievable/> <accessPermission>ALL</accessPermission> </USMARC:711c> </propList>
In order to keep SDLIP's datatypes simple, parameters that contain multiple pieces of information are encoded as XML. This section describes these XML formats. We use DTD-like syntax to describe the structures. Note, however, that we are taking some liberties in that we assume extensibility. It is expected that more entities might be added within SDLIP's XML structures over time, even though a strict adherence to the DTDs would not allow that.
XML has some primitive data types that are difficult to remember. Here are the ones that are used below:
An example of a property list is this:
<propList> <QualityOfService>Fastest</QualityOfService> <UserID>Miller</UserID> </propList>
Think of this as a dictionary, or list of key/value pairs. You may have empty elements in a propList, as in the following list of delivered groceries:
<propList> <Ham/> <Eggs/> <Bacon/> </propList>
In summary, propList is: <!ELEMENT propList (ANY)>.
Exceptions are delivered in whatever form the underlying transport allows. Once the SDLIP transport module receives the exception information over the wire, it raises an exception for the client or server implementation on the local machine. The exception will have the following interface:
interface SDLIPException { unsigned short getCode(); string getReason(); XMLObject getDetails(); }
Either two or three pieces of information are provided in an exception: an error code, a human-readable message, and optionally a property list with additional information.
Here is an example:
catch (SDLIPException e) { e.getCode(); // returns 451 e.getReason(); // returns "Bad Query" e.getDetails().getString(); /* returns "<propList> <remedy>Read the manual, you idiot!</remedy> <stacktrace>...</stacktrace> </propList>" */
The error codes follow a subset of HTTP conventions. All error codes are three-digit numbers. In SDLIP there
are two classes of error codes. Codes of the form 4xx indicate errors in the information supplied by the
client. Errors of the form 5xx indicate errors the server encountered, even though the client has supplied
correct information. See Appendix A for the full sets of codes.
This kind of XML string is used to return result documents in response to a search. An explanation follows:
<SearchResult> <doc> <DID>1</DID> <propList> <author>Bill Smith</author> <author>Frank Miller</author> <title>This is My Life</title> <abstract>It's been great so far.</abstract> </propList> </doc> <doc> ... </doc> </SearchResult>
For each document in a search result we communicate its document ID (DID) which is a numeric index into the result set (one-based), and the document's properties as requested by the client (i.e. author, title, etc). Note that exactly which properties are delivered depends on what the client asked for in its request (e.g. in the property list parameter of the search() call).
Here is the DTD for search results:
<!ELEMENT SearchResult (doc*)> <!ELEMENT doc (DID, propList)> <!ELEMENT DID (#PCDATA)>
Notice that a server could generate a more elaborate structure for the documents. For example, the author field could be subdivided into first name initials and last name portions, and a 'pict' attribute could be added, which points to a gif image of the author:
<SearchResult> <doc> <DID>1</DID> <propList> <DublinCore:Creator> <initials>A.</initials> <lastName>Miller</lastName> <authorPict>http://peopleServer.org/~miller/icon.jpg</authorPict> </DublinCore:Creator> <title>How I Did It</title> <abstract>With lots of effort.</abstract> </propList> </doc> <doc> ... </doc> </SearchResult>
When requesting a search, clients specify a set of subcollections and/or result sets to run the search over. This set is expressed as an XML string. For example:
<subcols> <subcolName>New York Times</subcolName> <resSet>3<resSet> <resSet>5<resSet> <subcolName>Washington Post</subcolName> </subcols>
This instructs the LSP to search over the New York Times, the Washington Post, and result sets produced in session
IDs 3 and 5.
The DTD for this is:
<!ELEMENT subcols (subcolName | resSet)*> <!ELEMENT subcolName (#PCDATA)> <!ELEMENT resSet (#PCDATA)>
Version information about supported SDLIP interfaces are returned by a simple XML element:
<SDLIPInterface> <SearchInterface> <version>1.0</version> </SearchInterface> <ResultAccessInterface> <version>1.1</version> </ResultAccessInterface> <MetadataInterface> <version>1.0</version> </MetadataInterface> </SDLIPInterface>
The DTD is:
<!Element SDLIPInterface (SearchInterface, ResultAccessInterface?, MetadataInterface?, SearchAsynchInterface?, DeliveryInterface?, ANY)> <!Element SearchInterface (version?, ANY)> <!Element version (#PCDATA)> <!Element ResultAccessInterface (version?, ANY)> <!Element MetadataInterface (version?, ANY)> <!Element SearchAsynchInterface (version?, ANY)> <!Element DeliveryInterface (version?, ANY)>
The searchAsynch and delivery interfaces are part of the optional SDLIP-Asynch extension. The ANY at the end of the SDLIPInterface element definition above allows services to provide additional interfaces, such as authorization, payment, etc. Similarly, the ANY in the interface definitions allow additional details about each interface to be included. If any of the interface elements are empty, version 1.0 is assumed. Example:
<SDLIPInterface> <SearchInterface/> </SDLIPInterface>
Information about a service's subcollections has the following DTD (for an example, see Section 2.3):
<!Element subcolInfo (subcolName, subcolDesc?, defaultSubcol?, queryLangs)> <!Element subcolName (#PCDATA)> <!Element subcolDesc (#PCDATA)> <!Element defaultSubcol EMPTY> <!Element queryLangs ANY>
When clients ask LSPs for information about the properties that are available for the documents in the collections the LSPs serve out, an attribute list is returned. The following example indicates that a source supports a small portion of Dublin Core and USMARC:
<propList> <DublinCore:creator> <searchable/> <retrievable/> </DublinCore:creator> <USMARC:245> <searchable/> <retrievable/> <phraseSearch/> </USMARC:245> <USMARC:711c> <retrievable/> <accessPermission>ALL</accessPermission> </USMARC:711c> </propList>
The properties in the Dublin Core and USMARC name spaces each contain one or more subelements which describe what may be done with the respective property. Standard capabilities for a property are <searchable/> and <retrievable/>. Services may add others, as exemplified by the <phraseSearch/> and <accessPermission> examples.
Notice that we again use empty XML fields for Boolean conditions. For example, if the (empty) <searchable/> attribute is present, then the attribute being described may be searched in queries. The absence of the <searchable/> element indicates that the source does not maintain a search index for this attribute.
Server delegate specifications have a very simple DTD:
<!Element redirect (serverDelegate+)> <!Element serverDelegate (#PCDATA)>
The Internet Engineering Task Force (IETF) is defining a standard for searching document repositories over the Web. The effort is part of the Web-based Distributed Authoring and Versioning (WebDAV) initiative, and is called Distributed Authoring, Searching and Locating (DASL). DASL is an HTTP-based protocol. It defines how search requests are delivered, and how results are returned. It also defines a basic query language that every DASL server must support. In contrast to SDLIP, the DASL protocol is very Web-centric.
SDLIP defines a mapping of SDLIP-Core operations onto DASL. The goal of the mapping is (i) to provide a coherent transport between SDLIP clients and services over HTTP, (ii) to allow SDLIP clients to search DASL servers, and (iii) to allow DASL clients a minimum of search capabilities over SDLIP servers.
SDLIP's DASL binding is described in a separate document.
Client and server transport modules may use CORBA to communicate search requests and results. With this transport binding, client applications still communicate with their local client transport module through local SDLIP calls, and library service proxies are still called by their local server transport modules. The CORBA based interactions between the transport modules are specified with CORBA IDL (SDLIPCorba.idl and SDLIPCore.idl). The interactions are very similar to the SDLIP specifications of this document.
SDLIP's CORBA binding is described in a separate document.
Error conventions follow a subset of HTTP conventions: 4xx are errors in information supplied by the client. Error codes of the form 5xx signal problems at the server.
Code |
Error Name |
Meaning |
400 |
eInvalidRequest | Use if none of the more specific error codes fits |
401 |
eUnauthorized | Operation invocation may be correct, but it requires authorization |
402 |
ePaymentRequired | Client needs to supply payment |
404 |
eNotFound | The requested document is not served by this server |
405 |
eIllegalMethod | Specified operation is not part of SDLIP |
408 |
eRequestTimeout | Server has discarded state |
450 |
eQueryLanguageUnknown | Server does not support the specified query language |
451 |
eBadQuery | Query malformed |
452 |
eInvalidProperty | One or more specified document properties are not supported |
453 |
eInvalidSessionID | Session ID specified unknown to this server |
454 |
eInvalidSubcollection | Specified subcollection not supported on this server |
455 |
eMalformedXML | An XML parameter is not parsable |
500 |
eServerError | Use if none of the more specific errors fits |
501 |
eNotImplemented | Operation is legal SDLIP, but server doesn't support it |
503 |
eServiceUnavailable | Service or requested subcollection is supported in principle, but is currently down |
[BASE64]
N. Borenstein, N. Freed. Base64 Content Transfer Encoding, in MIME Part One, RFC 1521, Sep 1993
http://src.doc.ic.ac.uk/packages/rfc/rfc1521.txt
[DASL]
Saveen Reddy, Dale Lowry, Surenda Reddy, Rick Henderson, Jim Davis, Alan Babich: DAV Searching & Locating,
Internet Draft, June 3, 1999
http://www.webdav.org/dasl/protocol/draft-dasl-protocol-00.html