Network Working Group                                        Rob Weltman
INTERNET-DRAFT                             Netscape Communications Corp.
                                                            Christine Ho
                                           Netscape Communications Corp.
                                                       November 20, 1997


                           Java LDAP Controls
                  draft-weltman-ldap-java-controls-00.txt

Status of this Memo

   This document is an Internet Draft.  Internet Drafts are working
   documents of the Internet Engineering 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.  Internet Drafts may be updated, replaced, or obsoleted by
   other documents at any time.  It is not appropriate to use Internet
   Drafts as reference material or to cite them other than as a "working
   draft" or "work in progress".

   To learn the current status of any Internet-Draft, please check the
   1id-abstracts.txt listing contained in the Internet-Drafts Shadow
   Directories on ds.internic.net, nic.nordu.net, ftp.isi.edu, or
   munnari.oz.au.


Abstract

   This document defines support for the Preferred Language Control, the
   Server Sorting Control, the Virtual List Control, and the Persistent
   Search Control in the java LDAP API. Controls are an LDAP protocol
   version 3 extension, to allow passing arbitrary control information
   along with a standard request to a server, and to receive arbitrary
   information back with a standard result.

















Expires 5/98                                                  [Page 1]


JAVA LDAP CONTROLS                                        November, 1997



   1. Introduction.....................................................3
   2. Overview of the LDAP Control classes.............................3
   3. The java LDAP Control classes....................................4
   3.1 public class LDAPLanguageControl................................4
   3.1.1 Constructors..................................................4
   3.2 public class LDAPVirtualListControl.............................5
   3.2.1 Constructors..................................................5
   3.2.2 getAfterCount.................................................6
   3.2.3 getBeforeCount................................................6
   3.2.4 getListSize...................................................6
   3.2.5 setRange......................................................6
   3.2.6 parseResponse.................................................7
   3.3 public class LDAPSortControl....................................7
   3.3.1 Constructors..................................................7
   3.3.2 parseResponse.................................................8
   3.4 public class LDAPPersistSearchControl...........................8
   3.4.1 Constructors..................................................8
   3.4.2 getChangeTypes................................................9
   3.4.3 public boolean getReturnControls()............................9
   3.4.4 setChangeTypes................................................9
   3.4.5 setChangesOnly................................................9
   3.4.6 setReturnControls............................................10
   3.4.7 parseResponse................................................10
   4. Security Considerations.........................................10
   5. Bibliography....................................................10
   6. Authors' Addresses..............................................11
   7. Appendix A - Sample usage of the java LDAP controls.............12

























Expires 5/98                                                  [Page 2]


JAVA LDAP CONTROLS                                        November, 1997



1. Introduction

   Version 3 of the LDAP protocol provides a means of supplying
   arbitrary additional information along with a request to an LDAP
   server, and receiving arbitrary additional response information. A
   few applications of the Control mechanism have been identified as
   having general interest, and the protocol defined for their
   transmission [5] and [6]. This document defines how support for the
   Preferred Language Control, the Server Sorting Control, the Virtual
   List Control, and the Persistent Search Control are supported
   in the java LDAP API. The java LDAP API in general is described in
   [2]. The Control protocol extension is described in [1], section
   4.1.12, and applications of it in [5] and [6].


2. Overview of the LDAP Control classes

   LDAPControl is part of a basic LDAP class package. Specific
   applications/implementations of Controls are in a subpackage called
   "controls".

   The base class LDAPControl is defined in [2] as:

      public class LDAPControl implements Cloneable

      An LDAPControl encapsulates optional additional parameters or
      constraints to be applied to LDAP operations. If set as a Server
      Control, it is sent to the server along with operation requests.
      If set as a Client Control, it is not sent to the server, but
      rather interpreted locally by the client. LDAPControl is an
      LDAPv3 extension, and is not supported in an LDAPv2 environment.

      Constructors

         public LDAPControl(String id,
                            boolean critical,
                            byte vals[])

         Parameters are:

         id           The type of the Control, as a string.

         critical     True if the LDAP operation should be discarded if
                      the server does not support this Control.

         vals         Control-specific data.

      getID

         public String getID()


Expires 5/98                                                  [Page 3]


JAVA LDAP CONTROLS                                        November, 1997


         Returns the identifier of the control.

      isCritical

         public boolean isCritical()

         Returns true if the control must be supported for an
         associated operation to be executed.

      getValue

         public byte[] getValue()

         Returns the control-specific data of the object.


   The following Controls are defined for the controls subpackage:


   LDAPLanguageControl       Is used to set a preferred language for
                             results from the server.


   LDAPVirtualListControl    Encapsulates requests for a subset of a
                             virtual list of search results, and the
                             response of a server to such a request.


   LDAPSortControl           Encapsulates a requested sorting order for
                             search results returned by a server, and
                             the server's response to the request.

   LDAPPersistSearchControl  Used to start a persistent search, one
                             which runs continuously, returning results
                             as the Directory is modified.


3. The java LDAP Control classes


3.1 public class LDAPLanguageControl
                 extends LDAPControl

   The LDAPLanguageControl class represents control data for setting a
   preferred language for results returned by a Directory Server.


3.1.1 Constructors

   public LDAPLanguageControl(String lang)



Expires 5/98                                                  [Page 4]


JAVA LDAP CONTROLS                                        November, 1997


   Constructs a new LDAPLanguageControl object using a string
   specification of language and optionally also country and a variant.


   public LDAPLanguageControl(Locale locale)

   Constructs a new LDAPLanguageControl object using a Locale object to
   select language.

   Parameters are:

      lang           A string of the form "en" or "ja-JP-kanji". The
                      syntax is defined in [3] and [4]. The first two
                      characters are a language specification (as
                      defined in ISO 639); they may be followed by a
                      dash and a two character country specification
                      (as defined in ISO 3166); the latter may be
                      followed by a dash and a language variant.

                       Examples:

                         "lang-en"
                         "lang-en-us"
                         "lang-ja-JP-kanji"

      locale         A Locale object to be used to define the
                      preferred language. A control for the default
                      locale language may be created with

                      LDAPLanguageControl lc =
                        new LDAPLanguageControl( Locale.getDefault() );


3.2 public class LDAPVirtualListControl
                 extends LDAPControl

   LDAPVirtualListControl is a Server Control to specify that results
   from a search are to be returned in pages, subsets of the entire
   virtual result set. On success, an updated LDAPVirtualList object is
   returned as a response Control, containing information on the virtual
   list size and the actual first index. This object can then be updated
   by the client with a new requested position or length and sent to the
   server to obtain a different segment of the virtual list. The
   protocol elements are defined in [6].


3.2.1 Constructors

   public LDAPVirtualListControl( String subFilter,
                                  int beforeCount,
                                  int afterCount )


Expires 5/98                                                  [Page 5]


JAVA LDAP CONTROLS                                        November, 1997


   Constructs a virtual list control using the specified filter
   expression, which defines the extent of the virtual search results,
   and the number of entries before and after a located index to be
   returned.

   Parameters are:

      subFilter      A search expression that defines the extent of
                      the virtual search results. The filter expression
                      in the search operation itself may be, for
                      example, "objectclass=person" and the subFilter
                      expression in the virtual list control may be
                      "cn=m*", to retrieve a subset of entries starting
                      at or centered around those with a common name
                      beginning with the letter "M".

      beforeCount    The number of entries before "listIndex" to be
                      returned.

      afterCount     The number of entries after "listIndex" to be
                      returned.


3.2.2 getAfterCount

   public int getAfterCount()

   Returns the number of entries after the top/center one to return per
   page of results.


3.2.3 getBeforeCount

   public int getBeforeCount()

   Returns the number of entries before the top/center one to return per
   page of results.


3.2.4 getListSize

   public int getListSize()

   Returns the size of the virtual search results list. For a newly
   constructed control - one which is not the result of parseResponse on
   a control returned by a server - the method returns -1.


3.2.5 setRange

   public void setRange( int listIndex,
                         int beforeCount,

Expires 5/98                                                  [Page 6]


JAVA LDAP CONTROLS                                        November, 1997


                         int afterCount )

   Sets the center or starting list index to return, and the number of
   results before and after.

   Parameters are:

      listIndex      The center or starting list index to be returned.

      beforeCount    The number of entries before "listIndex" to be
                      returned.

      afterCount     The number of entries after "listIndex" to be
                      returned.


3.2.6 parseResponse

   public LDAPVirtualListControl parseResponse()

   public static LDAPVirtualListControl parseResponse(
                                        LDAPControl[] controls )

   When applied to a virtual list control returned by a server,
   parseResponse returns a new virtual list control which can be used
   for subsequent searches using the same substringFilter. The second
   form processes a list of controls, one of which may or may not be an
   LDAPVirtualListControl. The input is typically provided with the
   LDAPConnection.getResponseControls method.

   Parameters are:

      controls       An array of controls. It may be null.


3.3 public class LDAPSortControl
                 extends LDAPControl

   LDAPSortControl is a Server Control to specify how search results are
   to be sorted by the server (see [5]). If a server does not support
   sorting in general or for a particular query, the results will be
   returned unsorted, along with a control indicating why they were not
   sorted (or that sort controls are not supported). If the control was
   marked "critical", the whole search operation will fail if the sort
   control is not supported.


3.3.1 Constructors

   public LDAPSortControl( LDAPSortKey key, boolean critical)

   Constructs a sort control with a single key.

Expires 5/98                                                  [Page 7]


JAVA LDAP CONTROLS                                        November, 1997




   public LDAPSortControl( LDAPSortKey[] keys, boolean critical)

   Constructs a sort control with multiple sort keys.

   Parameters are:

      key            A sort key object, which specifies attribute,
                      order, and optional matching rule.

      keys           An array of sort key objects, to be processed in
                      order.

      critical       True if the search operation is to fail if the
                      server does not support this control.


3.3.2 parseResponse

   public static String parseResponse( LDAPControl[] controls,
                                       int results[] )

   Processes an array of Server Controls to determine if sorting was
   rejected, and if so, why. If sorting was rejected, the return value
   is the attribute name which caused sorting to fail. results[0]
   contains the result code on return. The result code is one defined in
   [1], section 4.1.10.

   Parameters are:

      controls       An array of Server Controls, typically obtained
                      with LDAPConnection.getResponseControls(). It may
                      be null, which is equivalent to no error.


3.4 public class LDAPPersistSearchControl
                 extends LDAPControl

   The LDAPPersistSearchControl class is used to start a persistent
   search, one that doesn't end after returning any initial results, but
   continues to monitor changes in a designated part of a Directory,
   reporting the results as changes are made. The protocol elements are
   defined in [9].

3.4.1 Constructors

   public LDAPPersistSearchControl(int changeTypes,
                                   boolean changesOnly,
                                   boolean returnControls,
                                   boolean isCritical)


Expires 5/98                                                  [Page 8]


JAVA LDAP CONTROLS                                        November, 1997


   Parameters are:

      changeTypes    The change types to be monitored as a logical OR
                      of any or all of these types: ADD, DELETE,
                      MODIFY, and/or MODDN.

      changesOnly    true if the initial search is to be skipped.

      returnControls true if entry change controls are to be returned
                      with the search results.

      isCritical     true if the search is to be abandoned if the
                      server doesn't support this control.

3.4.2 getChangeTypes

   public int getChangeTypes()

   Returns the change types to be monitored as a logical OR of any or
   all of these types: ADD, DELETE, MODIFY, and/or MODDN.


3.4.3 public boolean getReturnControls()

   Returns true if entry change controls are to be returned with the
   search results.


3.4.4 setChangeTypes

   public void setChangeTypes(int types)

   Sets change types to be monitored.

   Parameters are:

      types          The change types to be monitored as a logical OR
                      of any or all of these types: ADD, DELETE,
                      MODIFY, and/or MODDN.

3.4.5 setChangesOnly

   public void setChangesOnly(boolean changesOnly)

   Requests that only changes be returned - skip the initial search.

   Parameters are:

      changesOnly    true to skip the initial search.




Expires 5/98                                                  [Page 9]


JAVA LDAP CONTROLS                                        November, 1997


3.4.6 setReturnControls

   public void setReturnControls(boolean returnControls)

   Requests that entry change controls are returned with the search
   results.

   Parameters are:

   returnControls       true to return entry change controls.


3.4.7 parseResponse

   public static LDAPEntryChangeControl parseResponse(
                                                LDAPControl[] controls)

   Processes an array of Server Controls and returns an
   LDAPEntryChangeControl if there is one.

   Parameters are:

      controls       An array of Server Controls, typically obtained
                      with LDAPConnection.getResponseControls(). It may
                      be null.


4. Security Considerations

   See [2] for security considerations in the java LDAP API.


5. Bibliography

   [1]  M. Wahl, T. Howes, S. Kille, "Lightweight Directory Access
        Protocol (v3)", Internet Draft draft-ietf-asid-ldapv3-protocol-
        06.txt, July 1997.

   [2]  R. Weltman, T. Howes, M. Smith, "The Java LDAP Application
        Program Interface", Internet Draft draft-ietf-asid-ldap-java-
        api-01.txt, September 1997.

   [3]  H. Alvestrans, "Tags for the Identification of Languages",
        Request for Comments 1766, March 1995.

   [4]  M. Wahl, T. Howes, "Use of Language Codes in LDAPv3", Internet
        Draft draft-ietf-asid-ldapv3-lang-02.txt, June 1997.

   [5]  A. Herron, T. Howes, M. Wahl, "LDAP Control Extension for Server
        Side Sorting of Search Results", Internet Draft draft-ietf-asid-
        ldapv3-sorting-00.txt, April 1997.


Expires 5/98                                                 [Page 10]


JAVA LDAP CONTROLS                                        November, 1997


   [6]  D. Boreham, "LDAP Control Extension for Virtual List View
        Browsing of Search Results", Internet Draft draft-ietf-ldapext-
        ldapv3-virtuallistview-01.txt, November 1997.

   [7]  C. Weider, A Herron, T. Howes, "LDAP Control Extension for
        Simple Paged Results Manipulation", Internet Draft draft-ietf-
        asid-ldapv3-simple-paged-01.txt, March 1997.

   [8]  R. Weltman, "Java LDAP Controls", draft-ietf-ldapext-ldap-java-
                    controls-00.txt, September 1997.

   [9]  M. Smith, G. Good, T. Howes, M. Smith, R. Weltman, ‘
                                                           ‘Persistent
        Search: A Simple LDAP Change Notification Mechanism’
                                                            ’, draft-
        ietf-smith-ldap-psearch-00.txt, November 1997


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 Ho
   Netscape Communications Corp.
   501 E. Middlefield Rd.
   Mountain View, CA 94043
   USA
   +1 650 937-5939
   chrisho@netscape.com




















Expires 5/98                                                 [Page 11]


JAVA LDAP CONTROLS                                        November, 1997


7. Appendix A - Sample usage of the java LDAP controls

   Doing a search with results sorted on the server

   import netscape.ldap.*;
   import netscape.ldap.controls.*;
   import java.util.*;

   public class SearchJensenSorted {

       public static void main( String[] args ) {
           try {
               LDAPConnection ld = new LDAPConnection();
               /* Connect to server */
               String MY_HOST = "localhost";
               int MY_PORT = 389;
               ld.connect( MY_HOST, MY_PORT );

               /* search for all entries with surname of Jensen */
               String MY_FILTER = "sn=Jensen";
               String MY_SEARCHBASE = "o=Ace Industry, c=US";

               /* Get the common name, uid, and telephone number */
               String[] attrs = new String[3];
               attrs[0] = "cn";
               attrs[1] = "telephonenumber";
               attrs[2] = "uid";

               /* Sort by lastname, firstname */
               LDAPSortKey[] keys = new LDAPSortKey[2];
               keys[0] = new LDAPSortKey( "sn" );
               keys[1] = new LDAPSortKey( "givenname" );
               LDAPSortControl sort = new LDAPSortControl( keys, true );
               ld.setOption( LDAPConnection.SERVERCONTROLS, sort );

               LDAPSearchResults res =
                                 ld.search( MY_SEARCHBASE,
                                            LDAPConnection.SCOPE_ONE,
                                            MY_FILTER,
                                            attrs,
                                            false );

              /* Loop on results until finished */
              while ( res.hasMoreElements() ) {

                  /* Next directory entry */
                  LDAPEntry findEntry = (LDAPEntry)res.nextElement();
                  System.out.println( findEntry.getDN() );

                  /* Get the attributes of the entry */
                  LDAPAttributeSet findAttrs =
                                          findEntry.getAttributeSet();

Expires 5/98                                                 [Page 12]


JAVA LDAP CONTROLS                                        November, 1997


                  Enumeration enumAttrs = findAttrs.getAttributes();
                  System.out.println( "Attributes: " );

                  /* Loop on attributes */
                  while ( enumAttrs.hasMoreElements() ) {
                      LDAPAttribute anAttr =
                              (LDAPAttribute)enumAttrs.nextElement();

                      String attrName = anAttr.getName();
                      System.out.println( "" + attrName );
                      /* Loop on values for this attribute */
                      Enumeration enumVals = anAttr.getStringValues();
                      while ( enumVals.hasMoreElements() ) {
                          String aVal =
                                     ( String )enumVals.nextElement();
                          System.out.println( "" + aVal );
                      }
                  }
              }

              /* Check if the server had something to say about the
                 sort request */
              LDAPControl[] controls = ld.getResponseControls();
              if ( controls != null ) {
                  int[] results = new int[1];
                  String bad =
                        LDAPSortControl.parseResponse( controls,
                                                       results );
                  if ( results[0] != 0 ) {
                      System.out.println( "Error code: " + results[0] );
                      if ( bad != null )
                          System.out.println( "Offending attribute: " +
                                              bad );
                      else
                          System.out.println( "No offending attribute +
                                              "returned" );
                  }
              }
          }
          catch( LDAPException e ) {
              System.out.println( "Error: " + e.toString() );
          }
          /* Done, so disconnect */
          if ( ld.isConnected() )
              ld.disconnect();
       }
   }






Expires 5/98                                                 [Page 13]


JAVA LDAP CONTROLS                                        November, 1997



   Using virtual list controls - excerpts from an application

   import netscape.ldap.*;
   import netscape.ldap.controls.*;

       // Call this to initialize the list box, whenever the search
       // conditions change.
       // "filter" may be "objectclass=person", for example, and
       // "subFilter" might be "cn>=m*"
       void initListBox( String base, int scope, String filter,
                         String attrs, String subFilter ) {
           // Record the virtual list box settings
           beforeCount = 2;
           afterCount = getScrollVisibleSize() - beforeCount - 1;
           this.base = base;
           this.scope = scope;
           this.filter = filter;
           this.attrs = attrs;
           // Create the initial virtual list control
           pageControls = new LDAPControl[2];
           pageControls[0] = new LDAPVirtualListControl( subFilter,
                                                         beforeCount,
                                                         afterCount );
           // virtual list also require a sort control
           pageControls[1] = new LDAPSortControl( new LDAPSortKey("cn"),
                                                  true );
           // We have no idea yet how big the virtual list is, so just
           // hint that there is more than what is visible
           setScrollRange( 0, 100 );
           // Do an initial update of the scroll box
           onScrollListBox();
       }

       // This is called any time the list box needs to be updated. It
       // fetches an appropriate page of results from the server.
       void onScrollListBox() {
           // Where is the list scrolled to now?
           int topOfList = getScrollTop();
           // Check if we have a control returned from a previous search
           LDAPVirtualListControl nextCont =
                          LDAPVirtualListControl.parseResponse(
                                             ld.getResponseControls() );
           // If this is the first search, use the initial settings;
           // otherwise use the just-retrieved control
           if ( nextCont != null ) {
               nextCont.setRange( topOfList, beforeCount, afterCount );
               pageControls[0] = nextCont;
               // Now we know the total size of the virtual list box
               setScrollRange( 0, nextCont.getListSize() );
           }
           ld.setControls( pageControls );

Expires 5/98                                                 [Page 14]


JAVA LDAP CONTROLS                                        November, 1997


           // Fetch a page of results
           LDAPSearchResults res = ld.search( base, scope, filter,
                                              attrs, false );
           // and display them in the list box
           showResults( res );
       }

       private int beforeCount, afterCount;
       private String base, filter, attrs;
       private int scope;
       private LDAPControl[] pageControls;










































Expires 5/98                                                 [Page 15]


JAVA LDAP CONTROLS                                        November, 1997


   Starting a persistent search

   import netscape.ldap.*;
   import netscape.ldap.controls.*;
   import java.util.*;

   public class PersistSearch implements Runnable{

       public PersistSearch() {
       }

       public static void main(String[] argv) {
           Thread th = new Thread(new PersistSearch(), "conn");
           th.start();
           System.out.println("Main thread, waiting for " +
                              "some action" );
       }

       public static void printResults(String str,
                                       LDAPSearchResults myResults) {
           LDAPEntry myEntry = null;

           /* hasMoreElements() will block until there is a change
              on the server satisfying our search conditions. When
              it returns, we can see what has changed. The loop is
              then repeated, and hasMoreElements() will block again
              until there are additional changes on the server. */
           while ( myResults.hasMoreElements() ) {
               /* A new Richard has appeared, let's get his
                  attributes */
               System.out.println("**** " + str + "****");
               try {
                   myEntry = myResults.next();
               } catch (LDAPReferralException e) {
                   /* Or was it a referral? */
                   LDAPUrl[] urls = e.getURLs();
                   System.out.println("Referral received:" );
                   for( int i = 0; i < urls.length; i++ )
                       System.out.println("  " + urls[i].getUrl() );
               }
               String nextDN = myEntry.getDN();
               System.out.println( nextDN );
               LDAPAttributeSet entryAttrs = myEntry.getAttributeSet();
               Enumeration attrsInSet = entryAttrs.getAttributes();
               while ( attrsInSet.hasMoreElements() ) {
                   LDAPAttribute nextAttr =
                            (LDAPAttribute)attrsInSet.nextElement();

                   String attrName = nextAttr.getName();
                   System.out.println( "\t" + attrName + ":" );
                   Enumeration valsInAttr = nextAttr.getStringValues();
                   while ( valsInAttr.hasMoreElements() ) {

Expires 5/98                                                 [Page 16]


JAVA LDAP CONTROLS                                        November, 1997


                       String nextValue =
                                      (String)valsInAttr.nextElement();
                       System.out.println( "\t\t" + nextValue );
                   }
               }
               System.out.println("");
           }
       }

       public void run() {
           /* Connect to standard port on local host */
           String hostname = "localhost";
           int portnum = 389;

           /* We want to be notified when any Richard is added to
              any part of the directory under "o=Airius.com".
              We're not interested in any Richards already there.
              We also don't care for any return change controls.
              We only want to do this search if the server
              supports persistent search, so set isCritical to
              true.
              When a Richard is added, we want to know his email
              address. */
           String filter = "givenname=Richard";
           String searchbase = "o=Airius.com";
           int scope = LDAPConnection.SCOPE_SUB;
           String[] attrs = {"mail"};
           int op = LDAPPersistSearchControl.ADD;
           boolean changesOnly = true;
           boolean returnControls = false;
           boolean isCritical = true;

           try {
               /* Connect */
               LDAPConnection ld = new LDAPConnection();
               ld.connect(hostname, portnum);

               LDAPSearchConstraints cons = ld.getSearchConstraints();
               cons.setBatchSize(1);
               LDAPPersistSearchControl control =
                 new LDAPPersistSearchControl( op, changesOnly,
                                               returnControls,
                                               isCritical );
               cons.setServerControls( control );

               /* The call to search will return almost immediately */
               LDAPSearchResults res = ld.search( searchbase,
                                                  scope,
                                                  filter, attrs,
                                                  false, cons );
               printResults("Persistent Search ", res);
           } catch (Exception e) {

Expires 5/98                                                 [Page 17]


JAVA LDAP CONTROLS                                        November, 1997


               System.out.println(e.toString());
           }
       }
   }

















































Expires 5/98                                                 [Page 18]