Network Working Group Rob Weltman INTERNET-DRAFT Netscape Communications Corp. Christine Tomlinson Innosoft International, Inc. April 28, 1999 The Java LDAP Application Program Interface Asynchronous Extension draft-ietf-ldapext-ldap-java-api-asynch-ext-00.txt Status of this Memo This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026. Internet-Drafts are working documents of the Internet Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts. Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet Drafts as reference material or to cite them other than as "work in progress." The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt The list of Internet-Draft Shadow Directories can be accessed at http://www.ietf.org/shadow.html. Abstract This document defines asynchronous extensions to the java language application program interface to the lightweight directory access protocol (LDAP) defined in draft-ietf-ldapext-ldap-java-api-04.txt [JAVALDAP]. Expires 10/28/99 [Page 1] JAVA LDAP API April 1999 1. Introduction.....................................................3 2. Overview of the LDAP asynchronous extension classes..............3 2.1 Interfaces......................................................3 2.2 Classes.........................................................3 3. Overview of extended asynchronous LDAP API use...................4 4. The java LDAP asynchronous extension classes.....................4 4.1 public interface LDAPAsynchronousConnection.....................4 4.1.1 add..........................................................4 4.1.2 bind.........................................................5 4.1.3 delete.......................................................6 4.1.4 modify.......................................................6 4.1.5 rename.......................................................7 4.1.6 search.......................................................8 4.2 public class LDAPExtendedResponse extends LDAPResponse.........10 4.2.1 getID.......................................................10 4.2.2 getValue....................................................10 4.3 public abstract class LDAPMessage..............................10 4.3.1 getControls.................................................10 4.3.2 getType.....................................................10 4.4 public abstract class LDAPResponse extends LDAPMessage.........11 4.4.1 getErrorMessage.............................................11 4.4.2 getMatchedDN................................................11 4.4.3 getReferrals................................................11 4.4.4 getResultCode...............................................11 4.5 public class LDAPResponseListener..............................11 4.5.1 getResponse.................................................11 4.5.2 isResponseReceived..........................................12 4.6 public class LDAPSearchListener extends LDAPResponseListener...12 4.6.1 getSearchResults............................................12 4.7 public class LDAPSearchResult extends LDAPMessage..............12 4.7.1 getEntry....................................................12 4.8 public class LDAPSearchResultReference extends LDAPMessage.....12 4.8.1 getUrls.....................................................12 5. Bibliography....................................................12 6. Authors' Addresses..............................................13 7. Appendix A - Sample java LDAP asynchronous extension programs...14 Expires 10/28/99 [Page 2] JAVA LDAP API April 1999 1. Introduction The LDAP class library defined in [JAVALDAP] is designed to provide powerful, yet simple, access to LDAP directory services. It defines a synchronous interface to LDAP, with support for partial results on searching, to suit a wide variety of applications. However, in some cases it is advantageous for a programmer to access the underlying asynchronous mechanisms close to the protocol layer. This document describes an extension interface - LDAPAsynchronousConnection - and supporting classes. The public class methods are described in detail, followed by an appendix that provides some example code demonstrating the use of the classes. 2. Overview of the LDAP asynchronous extension classes The central element is the interface LDAPAsynchronousConnection. It provides methods authenticate to an LDAP server, as well as methods to search for, modify, compare, and delete entries in the directory. Unlike LDAPConnection, LDAPAsynchronousConnection returns an LDAPResponseListener as the result of an LDAP operation, and may also take an LDAPResponseListener as input. The listener is a message queue associated with the request, and it is the responsibility of the client to read messages out of the queue and process them. Messages retrieved from an LDAPResponseListener are either result objects derived from LDAPResponse or search results. None of the ancillary asynchronous classes are intended to be instantiated by a client, so they lack public constructors. 2.1 Interfaces LDAPAsynchronousConnection Encapsulates a connection to an LDAP server, providing access to the input queue for messages received. 2.2 Classes LDAPExtendedResponse The response returned by an LDAP server on an extended operation request. It extends LDAPResponse. LDAPMessage Abstract base class for LDAP request and response messages. Expires 10/28/99 [Page 3] JAVA LDAP API April 1999 LDAPResponseListener Low-level mechanism for processing messages received from a server. LDAPResponse Abstract class representing a message received from an LDAP server in response to a request. LDAPSearchListener Extends LDAPResponseListener, producing search results received from a server. LDAPSearchResponse The response returned by an LDAP server on a search request. It extends LDAPResponse. 3. Overview of extended asynchronous LDAP API use An application generally uses the asynchronous methods as follows: - Construct an LDAPAsynchronousConnection. Initialize an LDAP session with a directory server. The LDAPAsynchronousConnection.connect() call establishes a handle to the session, allowing multiple sessions to be open at once, on different instances of LDAPAsynchronousConnection. - Invoke an LDAP operation, passing null for LDAPResponseListener. The LDAPAsynchronousConnection returns a new LDAPResponseListener. - Loop on reading from the LDAPResponseListener, which blocks until there is a response available, until the operation has completed, and interpret the results. An LDAPResponseListener may be shared between operations, for multiplexing the results. In this case, the object returned on one operation is passed in to one or more other operations, rather than passing in null. The following sections describe the asynchronous extension classes in more detail. 4. The java LDAP asynchronous extension classes 4.1 public interface LDAPAsynchronousConnection LDAPAsynchronousConnection provides access to the message queue in a connection to a server, allowing clients to multiplex results from multiple connections or do low-level processing of incoming messages. 4.1.1 add public LDAPResponseListener add(LDAPEntry entry, Expires 10/28/99 [Page 4] JAVA LDAP API April 1999 LDAPResponseListener listener) throws LDAPException public LDAPResponseListener add(LDAPEntry entry, LDAPResponseListener listener, LDAPConstraints cons) throws LDAPException Adds an entry to the directory. Parameters are: entry LDAPEntry object specifying the distinguished name and attributes of the new entry. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. cons Constraints specific to the operation. 4.1.2 bind public LDAPResponseListener bind(String dn, String passwd, LDAPResponseListener listener) throws LDAPException public LDAPResponseListener bind(String dn, String passwd, LDAPResponseListener listener) throws LDAPException public LDAPResponseListener bind(String dn, String[] mechanisms, String packageName, Properties props, SASLClientCB getter) throws LDAPException Authenticates to the LDAP server (that the object is currently connected to) using the specified name and password. If the object has been disconnected from an LDAP server, this method attempts to reconnect to the server. If the object had already authenticated, the old authentication is discarded. Parameters are: dn If non-null and non-empty, specifies that the connection and all operations through it should Expires 10/28/99 [Page 5] JAVA LDAP API April 1999 be authenticated with dn as the distinguished name. passwd If non-null and non-empty, specifies that the connection and all operations through it should be authenticated with dn as the distinguished name and passwd as password. mechanisms A list of acceptable mechanisms. The first one for which a Mechanism Driver can be instantiated is returned. packageName A package from which to instantiate the Mechanism Driver, e.g. "myclasses.SASL.mechanisms". If null, a system default is used. getter A class which may be called by the Mechanism Driver to obtain additional information required. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. 4.1.3 delete public LDAPResponseListener delete(String dn LDAPResponseListener listener) throws LDAPException public LDAPResponseListener delete(String dn, LDAPResponseListener listener) LDAPConstraints cons) throws LDAPException Deletes the entry for the specified DN from the directory. Parameters are: dn Distinguished name of the entry to modify. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. cons Constraints specific to the operation. 4.1.4 modify public LDAPResponseListener modify(String dn, Expires 10/28/99 [Page 6] JAVA LDAP API April 1999 LDAPModification mod, LDAPResponseListener listener) throws LDAPException public LDAPResponseListener modify(String dn, LDAPModification mod, LDAPResponseListener listener, LDAPConstraints cons) throws LDAPException Makes a single change to an existing entry in the directory (for example, changes the value of an attribute, adds a new attribute value, or removes an existing attribute value). The LDAPModification object specifies both the change to be made and the LDAPAttribute value to be changed. public LDAPResponseListener modify(String dn, LDAPModificationSet mods) LDAPResponseListener listener) throws LDAPException public LDAPResponseListener modify(String dn, LDAPModificationSet mods, LDAPResponseListener listener, LDAPConstraints cons) throws LDAPException Makes a set of changes to an existing entry in the directory (for example, changes attribute values, adds new attribute values, or removes existing attribute values). Parameters are: dn Distinguished name of the entry to modify. mod A single change to be made to the entry. mods A set of changes to be made to the entry. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. cons Constraints specific to the operation. 4.1.5 rename public LDAPResponseListener rename(String dn, String newRdn, Expires 10/28/99 [Page 7] JAVA LDAP API April 1999 boolean deleteOldRdn, LDAPResponseListener listener) throws LDAPException public LDAPResponseListener rename(String dn, String newRdn, boolean deleteOldRdn, LDAPResponseListener listener, LDAPConstraints cons) throws LDAPException Renames an existing entry in the directory. Parameters are: dn Current distinguished name of the entry. newRdn New relative distinguished name for the entry. deleteOldRdn If true, the old name is not retained as an attribute value. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. cons Constraints specific to the operation. 4.1.6 search public static LDAPSearchListener search(LDAPUrl toGet, LDAPSearchListener listener) throws LDAPException Performs the search specified by the LDAP URL. public static LDAPSearchResults search(LDAPUrl toGet, LDAPSearchListener listener, LDAPConstraints cons) throws LDAPException Perfoms the search specified by the LDAP URL. This method also allows specifying constraints for the search (such as the maximum number of entries to find or the maximum time to wait for search results). public LDAPSearchListener search(String base, int scope, String filter, String attrs[], boolean typesOnly, Expires 10/28/99 [Page 8] JAVA LDAP API April 1999 LDAPSearchListener listener) throws LDAPException Performs the search specified by the parameters. public LDAPSearchListener search(String base, int scope, String filter, String attrs[], boolean typesOnly, LDAPSearchListener listener, LDAPConstraints cons) throws LDAPException Performs the search specified by the parameters, also allowing specification of constraints for the search (such as the maximum number of entries to find or the maximum time to wait for search results). As part of the search constraints, the function allows specifying whether or not the results are to be delivered all at once or in smaller batches. If specified that the results are to be delivered in smaller batches, each iteration blocks only until the next batch of results is returned. Parameters are: base The base distinguished name to search from. scope The scope of the entries to search. The following are the valid options: LDAPv2.SCOPE_BASE Search only the base DN LDAPv2.SCOPE_ONE Search only entries under the base DN LDAPv2.SCOPE_SUB Search the base DN and all entries within its subtree filter Search filter specifying the search criteria, as defined in [3]. attrs Names of attributes to retrieve. typesOnly If true, returns the names but not the values of the attributes found. If false, returns the names and values for attributes found. Expires 10/28/99 [Page 9] JAVA LDAP API April 1999 toGet LDAP URL representing the search to run. It may contain host, port, base DN, attributes to return, and a search filter. listener Handler for messages returned from a server in response to this request. If it is null, a listener object is created internally. cons Constraints specific to the search. 4.2 public class LDAPExtendedResponse extends LDAPResponse An LDAPExtendedResponse object encapsulates a server response to an extended operation request. 4.2.1 getID public String getID() Returns the OID of the response. 4.2.2 getValue public byte[] getValue() Returns the raw bytes of the value part of the response. 4.3 public abstract class LDAPMessage Base class for LDAP request and response messages. 4.3.1 getControls public LDAPControl[] getControls() Returns any controls in the message. 4.3.2 getType public int getType() Returns the LDAP operation type of the message. The type is one of the following: public final static int BIND_REQUEST = 0; public final static int BIND_RESPONSE = 1; public final static int UNBIND_REQUEST = 2; public final static int SEARCH_REQUEST = 3; public final static int SEARCH_RESPONSE = 4; public final static int SEARCH_RESULT = 5; public final static int MODIFY_REQUEST = 6; public final static int MODIFY_RESPONSE = 7; Expires 10/28/99 [Page 10] JAVA LDAP API April 1999 public final static int ADD_REQUEST = 8; public final static int ADD_RESPONSE = 9; public final static int DEL_REQUEST = 10; public final static int DEL_RESPONSE = 11; public final static int MODIFY_RDN_REQUEST = 12; public final static int MODIFY_RDN_RESPONSE = 13; public final static int COMPARE_REQUEST = 14; public final static int COMPARE_RESPONSE = 15; public final static int ABANDON_REQUEST = 16; public final static int SEARCH_RESULT_REFERENCE = 19; public final static int EXTENDED_REQUEST = 23; public final static int EXTENDED_RESPONSE = 24; 4.4 public abstract class LDAPResponse extends LDAPMessage Represents the response to a particular LDAP operation. 4.4.1 getErrorMessage public String getErrorMessage() Returns any error message in the response. 4.4.2 getMatchedDN public String getMatchedDN() Returns the partially matched DN field, if any, in a server response. 4.4.3 getReferrals public String[] getReferrals() Returns all referrals, if any, in a server response. 4.4.4 getResultCode public int getResultCode() Returns the result code in a server response. 4.5 public class LDAPResponseListener Represents the message queue associated with a particular LDAP operation or operations. 4.5.1 getResponse public LDAPResponse getResponse() Expires 10/28/99 [Page 11] JAVA LDAP API April 1999 Blocks until a response is available, or until all operations associated with the object have completed or been canceled, and returns the response. 4.5.2 isResponseReceived public boolean isResponseReceived() Reports true if a response has been received from the server. 4.6 public class LDAPSearchListener extends LDAPResponseListener An LDAPSearchListener manages search results and references returned on one or more search requests. 4.6.1 getSearchResults public Enumeration getSearchResults() Returns an enumeration of all search results and references available in the queue. 4.7 public class LDAPSearchResult extends LDAPMessage An LDAPSearchResult object encapsulates a single search result. 4.7.1 getEntry public LDAPEntry getEntry() Returns the entry of a server search response. 4.8 public class LDAPSearchResultReference extends LDAPMessage An LDAPSearchResultReference object encapsulates a continuation reference from a search operation. 4.8.1 getUrls public String[] getUrls() Returns any URLs in the object. 5. Bibliography [JAVALDAP] R. Weltman, T. Howes, M. Smith, "The Java LDAP Application Program Interface", Internet Draft draft-ietf-ldapext-ldap- java-api-04.txt, April, 1999. Expires 10/28/99 [Page 12] JAVA LDAP API April 1999 6. Authors' Addresses Rob Weltman Netscape Communications Corp. 501 E. Middlefield Rd. Mountain View, CA 94043 USA +1 650 937-3301 rweltman@netscape.com Christine Tomlinson Innosoft International, Inc. 8911 Capital of Texas Highway Suite 4140 Austin, TX US 78759 +1 512 231 1600 christine.tomlinson@innosoft.com Expires 10/28/99 [Page 13] JAVA LDAP API April 1999 7. Appendix A - Sample java LDAP asynchronous extension programs import netscape.ldap.*; import java.util.*; /* Assume a class LDAPAsynch which extends LDAPConnection and implements LDAPAsynchronousConnection */ public class SearchJensen { public static void main( String[] args ) { try { LDAPAsynch ld = new LDAPAsynch(); /* Connect to server */ String MY_HOST = "localhost"; int MY_PORT = 389; ld.connect( MY_HOST, MY_PORT ); /* Asynchronous authentication */ LDAPResponseListener r = ld.bind( "uid=admin, o=Airius.com" ); /* Do something else, just to show that we're not blocked yet */ System.out.println( "Started authenticating" ); /* Wait until it completes*/ LDAPResponse response = r.getResponse(); int resultCode = response.getResultCode(); if (resultCode != LDAPException.SUCCESS) { throw new LDAPException ("error result", resultCode, response.getErrorMessage(), response.getMatchedDN()); } /* search for all entries with surname of Jensen */ String MY_FILTER = "sn=Jensen"; String MY_SEARCHBASE = "o=Ace Industry, c=US"; LDAPSearchListener l = ld.search( MY_SEARCHBASE, ld.SCOPE_ONE, MY_FILTER, null, false, null, cons ); /* Loop on results until finished */ LDAPMessage res; while ( (res = l.next()) != null ) { /* Next directory entry */ if ( res instanceof LDAPSearchResultReference ) { Expires 10/28/99 [Page 14] JAVA LDAP API April 1999 String[] urls = ((LDAPSearchResultReference)res).getUrls[]; } else { LDAPEntry findEntry = ((LDAPSearchResult))res.getEntry(); } /* The rest of the processing is the same as for LDAPConnection */ /* ... */ } } catch( LDAPException e ) { System.out.println( "Error: " + e.toString() ); } /* Done, so disconnect */ if ( ld.isConnected() ) ld.disconnect(); } } Expires 10/28/99 [Page 15] JAVA LDAP API April 1999 import netscape.ldap.*; import java.util.*; /* This example multiplexes the input from three different servers */ public class MultiplexServers { public static void main( String[] args ) { try { LDAPAsynch[] ld = new LDAPAsynch[3]; String[] hosts = { "foo1", "foo2", "foo3" }; int[] ports = { 389, 389, 2018 } String[] bases = { "o=Airius.com", "o=Acme.com", "dc=Acme, "dc=com" }; /* search for all entries with surname of Jensen */ String MY_FILTER = "sn=Jensen"; for( int i = 0; i < ld.length; i++ ) { ld[i] = new LDAPAsynch(); /* Connect to server */ ld[i].connect( hosts[i], ports[i] ); } /* Get a response listener for one search */ LDAPSearchListener l = ld[0].search( bases[0], ld.SCOPE_SUB, MY_FILTER, null, false, null ); /* Share the listener */ for( i = 1; i < ld.length; i++ ) { ld[i].search( bases[i], ld[i].SCOPE_SUB, MY_FILTER, null, false, l ); } /* Loop on results until finished */ LDAPMessage res; while ( (res = l.next()) != null ) { /* Next directory entry */ if ( res instanceof LDAPSearchResultReference ) { String[] urls = ((LDAPSearchResultReference)res).getUrls[]; } else { LDAPEntry findEntry = ((LDAPSearchResult))res.getEntry(); } Expires 10/28/99 [Page 16] JAVA LDAP API April 1999 /* The rest of the processing is the same as for LDAPConnection */ /* ... */ } } catch( LDAPException e ) { System.out.println( "Error: " + e.toString() ); } /* Done, so disconnect */ if ( ld.isConnected() ) ld.disconnect(); } } Expires 10/28/99 [Page 17] JAVA LDAP API April 1999 import netscape.ldap.*; import java.util.*; /* This example multiplexes the input from three searches in different subtrees of the same server */ public class MultiplexTrees { public static void main( String[] args ) { try { LDAPAsynch ld = new LDAPAsynch(); /* Connect to server */ String MY_HOST = "localhost"; int MY_PORT = 389; ld.connect( MY_HOST, MY_PORT ); String MY_FILTER = "sn=Jensen"; String[] bases = { "o=Airius.com", "o=Acme.com", "dc=Acme, "dc=com" }; /* Get a response listener for one search */ LDAPSearchListener l = ld.search( bases[0], ld.SCOPE_SUB, MY_FILTER, null, false, null ); /* Share the listener */ for( i = 1; i < bases.length; i++ ) { ld.search( bases[i], ld.SCOPE_SUB, MY_FILTER, null, false, l ); } /* The rest is the same as in the MultiplexServers example */ /* ... */ } catch( LDAPException e ) { System.out.println( "Error: " + e.toString() ); } /* Done, so disconnect */ if ( ld.isConnected() ) ld.disconnect(); } } Expires 10/28/99 [Page 18]