pjs/directory/docs/ldapjdk/jdk-controls.sgm

941 строка
48 KiB
Plaintext

<!--
Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
Portions copyright 1999 Netscape Communications Corporation. All
Rights Reserved.
The contents of this document are subject to the terms of the
Creative Commons Attribution-ShareAlike 2.5 license or any later
version (the "License"). You may not use this document except in
compliance with the License.
See the License for the specific language governing
permissions and limitations under the License. You can obtain
a copy of the License at
http://creativecommons.org/licenses/by-sa/2.5/legalcode.
-->
<chapter id="controls"><title>LDAP Controls With &DirectorySDKForJava;</title>
<highlights>
<para>This chapter explains how LDAP controls work and how to use the LDAP
controls.</para>
<itemizedlist>
<para>This chapter covers the following topics:</para>
<listitem><para><olink targetptr="controls-overview">How LDAP Controls Work
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-using">Using Controls in the LDAP
Java Classes</olink></para></listitem>
<listitem><para><olink targetptr="controls-server">Determining the Controls Supported the Server With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-sort">Using the Server-Side Sorting
Control With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-psearch">Using the Persistent Search
Control With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-entry-change">Using the Entry Change
Notification Control With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-vlv">Using the Virtual List Control With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-managedsait">Using the Manage DSA
IT Control With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-pwp">Using Password Policy Controls
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="controls-proxyauth">Using the Proxied Authorization
Control With Directory SDK for Java</olink></para></listitem>
</itemizedlist>
</highlights>
<sect1 id="controls-overview"><title>How LDAP Controls Work With &DirectorySDKForJava;</title>
<para>LDAP v3 allows clients and servers to use controls as a mechanism for
extending an LDAP operation. A control is a way to specify additional information
as part of a request and a response.</para>
<para>For example, a client can send a control to a server as part of a search
request. The control indicates that the server should sort the search results
before sending the results back to the client.</para>
<para>Servers can also send controls back to clients. For example, the server
can send a control back to a client during the authentication process. The
control can indicate that the client password has expired. The control can
alternatively indicate that the client password is going to expire.</para>
<itemizedlist>
<para>A control specifies the following information:</para>
<listitem><para>A unique object identifier (OID)</para></listitem>
<listitem><para>An indication of whether the control is critical to the operation
</para></listitem>
<listitem><para>Optional data related to the control, such as the server-side
sorting control, where attributes used for sorting search results are needed</para>
</listitem>
</itemizedlist>
<para>The OID identifies the control. If you plan to use a control, you need
to make sure that the server supports the control. Refer to <olink targetptr="controls-server">Determining the Controls Supported the Server With Directory SDK for Java</olink> for instructions.</para>
<itemizedlist>
<para>When your client includes a control in a request for an LDAP operation,
the server can respond in one of the following ways:</para>
<listitem><para>If the server supports this control and if the control is
appropriate, the server should use the control when performing the operation.</para>
</listitem>
<listitem>
<itemizedlist>
<para>If the server does not support the control type or if the control is
not appropriate, the server should do one of the following:</para>
<listitem><para>If the control is marked as critical to the operation, the
server should not perform the operation. Instead, the server should send an <firstterm>
unavailable critical extension</firstterm> result code. When receiving this
result code, your client returns an <classname>LDAPException</classname> with
the result code <returnvalue>LDAPException.UNAVAILABLE_CRITICAL_EXTENSION</returnvalue>.
</para></listitem>
<listitem><para>If the control is marked as not critical to the operation,
the server should ignore the control. The server should proceed to perform
the operation.</para></listitem>
</itemizedlist>
</listitem>
</itemizedlist>
<para>Servers can also send controls back to clients.</para>
<itemizedlist>
<para>Two types of controls exist:</para>
<listitem><para>Server controls can be included in requests sent by clients
and in responses sent by servers.</para></listitem>
<listitem><para>Client controls affect the behavior of the LDAP Java classes
only and are never sent to the server.</para><para>&DirectorySDKForJava; does
not support client controls.</para></listitem>
</itemizedlist>
</sect1>
<sect1 id="controls-using"><title>Using Controls in the LDAP Java Classes</title>
<para>This section describes how controls are implemented in the LDAP Java
classes. This section also describes which methods to use to create, send,
and parse data from LDAP controls.</para>
<para>In the LDAP Java classes, a control is represented by an object of the <classname>
LDAPControl</classname> class.</para>
<orderedlist>
<para>To include a control in a request, do the following:</para>
<listitem><para>Invoke the <literal>getSearchConstraints</literal> method
of the <classname>LDAPConnection</classname> object to get a clone of <classname>
LDAPSearchConstraints</classname> for this connection.</para></listitem>
<listitem><para>Invoke the <literal>setServerControls</literal> method of
the cloned constraints object, passing in the <classname>LDAPControl</classname> object
that represents the control to include.</para></listitem>
<listitem><para>Invoke the appropriate method to perform the LDAP operation,
passing in the constraints object.</para><para>For example, if you are performing
a search, invoke the <literal>search</literal> method. Pass the search constraints
as an argument.</para></listitem></orderedlist>
<para>You can also include controls by invoking the <literal>setServerControls</literal> method
for the default set of search constraints. Alternatively, invoke the <literal>setOption
</literal> method to set the <constant>LDAPv3.SERVERCONTROLS</constant> option.
These controls are sent to the server with every request, however. In general,
controls tend to be specific to a type of operation. Include a control in
a request only for the operation to which the control applies.</para>
<para>You can then retrieve data from the returned controls through accessor
methods in the <classname>LDAPControl</classname> object.</para></sect1>
<sect1 id="controls-server"><title>Determining the Controls Supported the
Server With &DirectorySDKForJava;</title>
<para>LDAP v3 states that servers should list any controls that servers support
in the <literal>supportedControl</literal> attribute in the root DSE. The
following OIDs have corresponding constants that are defined in the LDAP Java
classes.</para>
<variablelist termlength="wholeline">
<varlistentry><term><literal>1.2.840.113556.1.4.473</literal></term><term><constant>
netscape.ldap.controls.LDAPSortControl.SORTREQUEST</constant></term>
<listitem><para>For instructions on using this control, refer to <olink targetptr="controls-sort">Using the Server-Side Sorting Control With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.2</literal></term><term><constant>
netscape.ldap.LDAPControl.MANAGEDSAIT</constant></term>
<listitem><para>For instructions on using this control, refer to <olink type="auto-generated" targetptr="controls-managedsait">Using the Manage DSA
IT Control With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.3</literal></term><term><constant>
netscape.ldap.controls.LDAPPersistSearchControl.PERSISTENTSEARCH</constant></term>
<listitem><para>For instructions on using this control, refer to <olink type="auto-generated" targetptr="controls-psearch">Using the Persistent Search
Control With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.4</literal></term><term><constant>
netscape.ldap.LDAPControl.PWEXPIRED</constant></term>
<listitem><para>For instructions on using this control, refer to <olink type="auto-generated" targetptr="controls-pwp">Using Password Policy Controls
With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.5</literal></term><term><constant>
netscape.ldap.LDAPControl.PWEXPIRING</constant></term>
<listitem><para>For instructions on using this control, refer to <olink type="auto-generated" targetptr="controls-pwp">Using Password Policy Controls
With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.9</literal></term><term><constant>
netscape.ldap.controls.LDAPVirtualListControl.VIRTUALLIST</constant></term>
<listitem><para>For instructions on using this control, refer to <olink type="auto-generated" targetptr="controls-vlv">Using the Virtual List Control With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>2.16.840.1.113730.3.4.12</literal></term><term><constant>
netscape.ldap.controls.LDAPProxiedAuthControl.PROXIEDAUTHREQUEST</constant></term>
<listitem><para>For instructions on using this control, refer to <olink targetptr="controls-proxyauth">Using the Proxied Authorization Control With Directory SDK for Java</olink>.</para>
</listitem>
</varlistentry>
</variablelist>
<para>The following example searches for the root DSE. Then the example prints
the values of the <literal>supportedControl</literal> attribute.</para>
<programlisting>import netscape.ldap.*;
import netscape.ldap.controls.*;
import java.util.*;
public class ListCtrl {
public static void main(String[] args) {
/* Hashtable mapping OIDs of controls to a description */
Hashtable knownControls = new Hashtable();
knownControls.put(LDAPSortControl.SORTREQUEST,
"Sort control");
knownControls.put(LDAPControl.MANAGEDSAIT,
"ManageDsaIT control");
knownControls.put(LDAPPersistSearchControl.PERSISTENTSEARCH,
"Persistent Search control");
knownControls.put(LDAPControl.PWEXPIRED,
"Password Expiration Notification control");
knownControls.put(LDAPControl.PWEXPIRING,
"Password Expiration Warning control");
knownControls.put(LDAPVirtualListControl.VIRTUALLIST,
"Virtual List View control");
knownControls.put(LDAPProxiedAuthControl.PROXIEDAUTHREQUEST,
"Proxied Authorization control");
try {
UserArgs userArgs = new UserArgs("ListCtrl", args, false);
LDAPConnection ld = new LDAPConnection();
ld.connect(userArgs.getHost(), userArgs.getPort());
/* Retreive the list of supported controls from the DSE. */
String getAttrs[] = {"supportedControl"};
LDAPSearchResults res = ld.search("", LDAPv3.SCOPE_BASE,
"(objectclass=*)", getAttrs, false);
LDAPEntry DSE = (LDAPEntry)res.nextElement();
LDAPAttributeSet findAttrs = DSE.getAttributeSet();
Enumeration enumAttrs = findAttrs.getAttributes();
while (enumAttrs.hasMoreElements()) {
LDAPAttribute anAttr =
(LDAPAttribute)enumAttrs.nextElement();
String attrName = anAttr.getName();
System.out.println(attrName);
Enumeration enumVals = anAttr.getStringValues();
if (enumVals == null) {
System.out.println("\tNo values.");
continue;
}
while (enumVals.hasMoreElements()) {
String aVal = (String)enumVals.nextElement();
/*
* Each value should be the OID of a control.
* Look up its description in the hash table.
*/
String aDesc = (String)knownControls.get(aVal);
if (aDesc != null) {
System.out.println("\t" + aDesc+ " (" + aVal + ")");
} else {
System.out.println("\t" + aVal);
}
}
}
ld.disconnect();
} catch(LDAPException e) {
System.out.println("Error: " + e.toString());
}
}
}</programlisting>
</sect1>
<sect1 id="controls-sort"><title>Using the Server-Side Sorting Control With &DirectorySDKForJava;</title>
<indexterm>
<primary>search results</primary>
<secondary>sorting</secondary>
</indexterm><indexterm>
<primary>sorting search results</primary>
</indexterm>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.LDAPControl.SORTREQUEST</constant> is a server-side sorting
control. The server-side sorting control is defined in <ulink
url="http://www.ietf.org/rfc/rfc2891.txt" type="text_url">RFC 2891</ulink>.
When you send a search request with this control to the server, the server
should sort the results before sending them back to you.</para>
<sect2 id="controls-sort-order"><title>Specifying the Server-Side Sort Order
With Directory SDK for Java</title>
<para>To specify the sort order of the results, construct one or more <classname>
LDAPSortKey</classname> objects. Each object represents a sort key that is
generated from a string in the following format:</para>
<programlisting>[-]<replaceable>attrName</replaceable>[:<replaceable>matchingRuleOID
</replaceable>]</programlisting>
<para>Here, <replaceable>attrName</replaceable> represents the name of the
attribute to use for sorting. <replaceable>matchingRuleOID</replaceable> represents
the optional OID of the matching rule to use for sorting. The optional minus
sign (<literal>-</literal>) indicates that the results should be sorted in
reverse order for that attribute.</para>
<para>For example, the following string specifies that results should be sorted
by first name (<literal>givenname</literal>) in descending order:</para>
<programlisting>-givenname</programlisting>
<para>Pass this string to the <classname>LDAPSortKey</classname> constructor
to create a sort key:</para>
<programlisting>LDAPSortKey reverseSortByFirstName = new LDAPSortKey("-givenname");
</programlisting>
<para>To sort by more than one attribute, construct more than one <classname>LDAPSortKey
</classname> object and create an array of the objects.</para>
<para>For example, suppose you want to sort the result by last name (<literal>sn</literal>)
in ascending order. If two or more entries have the same last name, you want
to sort the result by first name (<literal>givenname</literal>) in ascending
order. To specify this sort order, you construct two <classname>LDAPSortKey</classname> objects.
Then create an array:</para>
<programlisting>LDAPSortKey sortByLastName = new LDAPSortKey("sn");
LDAPSortKey sortByFirstName = new LDAPSortKey("givenname");
LDAPSortKey[] sortOrder = { sortByLastName, sortByFirstName };</programlisting>
</sect2>
<sect2 id="controls-sort-create"><title>Creating the Server-Side Sorting Control
With &DirectorySDKForJava;</title>
<para>Next, to create the server-side sorting control, construct a new <classname>
LDAPSortControl</classname> object. Pass the <classname>LDAPSortKey</classname> object,
or the array of <classname>LDAPSortKey</classname> objects, to the <literal>LDAPSortControl
</literal> constructor.</para>
<para>In the constructor, you can also specify whether or not the control
is critical to the search operation. The control can be marked as critical,
but the server cannot sort the results. If the situation occurs, the server
should not send back any entries.</para>
<para>For example, the following section of code creates a server-side sorting
control. Also, the code specifies that the control is critical to the search
operation:</para>
<programlisting>LDAPSortKey sortOrder = new LDAPSortKey("-givenname");
LDAPSortControl sortCtrl = new LDAPSortControl(sortOrder, true);</programlisting>
</sect2>
<sect2 id="controls-sort-search"><title>Performing the Server&mdash;Side Sort
With &DirectorySDKForJava;</title>
<orderedlist>
<para>To specify that you want the server to sort the results, do the following:</para>
<listitem><para>Get a clone of <classname>LDAPSearchConstraints</classname> for
the current connection by invoking the <literal>getSearchConstraints</literal> method
of the <classname>LDAPConnection</classname> object.</para></listitem>
<listitem><para>Invoke the <literal>setServerControls</literal> method for
the copied <classname>LDAPSearchConstraints</classname> object, and pass in
the <classname>LDAPSortControl</classname> object that you have constructed.</para>
</listitem>
<listitem><para>Invoke the <literal>search</literal> method of the <classname>LDAPConnection
</classname> object, passing in the <classname>LDAPSearchConstraints</classname> object.
</para><para>The server returns a result for the search operation and a response
control. The response control indicates the success or failure of the sorting.</para>
</listitem>
<listitem><para>Invoke the <literal>getResponseControls</literal> method of
the <classname>LDAPSearchResults</classname> object to retrieve any controls
sent back by the server in response to the search.</para><para>Response controls
are passed back as an array of <classname>LDAPControl</classname> objects.</para>
</listitem>
<listitem><para>Examine the type of each returned control. </para></listitem>
</orderedlist>
<para>If a control is an instance of <classname>LDAPSortControl</classname>,
you can read the result code for the sorting operation with the <literal>getResultCode
</literal> method.</para>
<para>If the sorting operation failed, the server can also return the name
of the attribute that caused the failure. You can read the name of this attribute
with the <literal>getFailedAttribute</literal> method.</para>
<para>The server can return the following result codes that apply to the sorting
operation.</para>
<variablelist>
<varlistentry><term><returnvalue>LDAPException.ADMIN_LIMIT_EXCEEDED</returnvalue></term>
<listitem><para>Too many entries exist for the server to sort.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.BUSY</returnvalue></term>
<listitem><para>The server is too busy to sort the results.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.INAPPROPRIATE_MATCHING</returnvalue></term>
<listitem><para>The sort key list specifies a matching rule that is not recognized
or appropriate.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.INSUFFICIENT_ACCESS_RIGHTS</returnvalue></term>
<listitem><para>The server did not send the sorted results because the client
has insufficient access rights.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.NO_SUCH_ATTRIBUTE</returnvalue></term>
<listitem><para>The sort key list specifies an attribute that does not exist.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.OPERATION_ERROR</returnvalue></term>
<listitem><para>An internal server error occurred.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.OTHER</returnvalue></term>
<listitem><para>This general result code indicates that the server failed
to sort the results for a reason other than the results listed here.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.STRONG_AUTH_REQUIRED</returnvalue></term>
<listitem><para>The server refused to send back the sorted search results
because the server requires that you use a stronger authentication method.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.SUCCESS</returnvalue></term>
<listitem><para>The results were sorted successfully.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.TIME_LIMIT_EXCEEDED</returnvalue></term>
<listitem><para>The maximum time allowed for a search was exceeded before
the server finished sorting the results.</para>
</listitem>
</varlistentry>
<varlistentry><term><returnvalue>LDAPException.UNWILLING_TO_PERFORM</returnvalue></term>
<listitem><para>The server is unable to sort the results.</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="controls-sort-results"><title>Interpreting the Server-Side Sort
Results With &DirectorySDKForJava;</title>
<para>The following table shows the kinds of results to expect from the LDAP
server under different conditions.</para>
<table frame="topbot" pgwide="1" id="controls-sort-results-interpret"><title>Server
Responses to Sort Controls</title>
<tgroup cols="4" colsep="0" rowsep="0"><colspec colwidth="25*"><colspec
colwidth="25*"><colspec colwidth="25*"><colspec colwidth="25*">
<thead>
<row rowsep="1">
<entry colsep="0">
<para>Supports sort control?</para></entry>
<entry colsep="0">
<para>Sort control marked critical?</para></entry>
<entry colsep="0">
<para>Any other conditions?</para></entry>
<entry colsep="0">
<para>Results From LDAP Server</para></entry>
</row>
</thead>
<tbody>
<row>
<entry colsep="0">
<para>Server does not support sort control.</para></entry>
<entry colsep="0">
<para>Control is not marked as critical.</para></entry>
<entry colsep="0">
<para>Not applicable</para></entry>
<entry colsep="0">
<para>The server ignores the sorting control. The server returns the entries
unsorted.</para></entry>
</row>
<row>
<entry colsep="0">
<para>Server does not support sort control.</para></entry>
<entry colsep="0">
<para>Control is marked as critical.</para></entry>
<entry colsep="0">
<para>Not applicable</para></entry>
<entry colsep="0">
<para>The server does not send back any entries.</para></entry>
</row>
<row>
<entry colsep="0">
<para>Server does support sort control.</para></entry>
<entry colsep="0">
<para>Control is not marked as critical.</para></entry>
<entry colsep="0">
<para>The server cannot sort the results with the specified sort key list.</para>
</entry>
<entry colsep="0">
<itemizedlist>
<listitem><para>The server returns the entries unsorted.</para></listitem>
<listitem><para>The server sends back the sorting response control. The response
control specifies the result code of the sort attempt and, optionally, the
attribute type that caused the error.</para></listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry colsep="0">
<para>Server does support sort control.</para></entry>
<entry colsep="0">
<para>Control is marked as critical.</para></entry>
<entry colsep="0">
<para>The server cannot sort the results with the specified sort key list.</para>
</entry>
<entry colsep="0">
<itemizedlist>
<listitem><para>The server does not return any entries.</para></listitem>
<listitem><para>The server sends back the sorting response control. The response
control specifies the result code of the sort attempt and, optionally, the
attribute type that caused the error.</para></listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry colsep="0">
<para>Server does support sort control.</para></entry>
<entry colsep="0">
<para>has no effect on results</para></entry>
<entry colsep="0">
<para>The search failed.</para></entry>
<entry colsep="0">
<itemizedlist>
<listitem><para>The server returns a result code for the search operation.</para>
</listitem>
<listitem><para>The server does not send back the sorting response control.</para>
</listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry colsep="0">
<para>Server does support sort control.</para></entry>
<entry colsep="0">
<para>has no effect on results</para></entry>
<entry colsep="0">
<para>The server successfully sorted the entries.</para></entry>
<entry colsep="0">
<itemizedlist>
<listitem><para>The server returns the entries in sorted order.</para>
</listitem>
<listitem><para>The server sends back the sorting response control, which
specifies the result code of the sort attempt as <returnvalue>LDAPException.SUCCESS
</returnvalue>.</para></listitem>
</itemizedlist>
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect2>
<sect2 id="controls-sort-example"><title>Example of Using Server-Side Sorting
With &DirectorySDKForJava;</title>
<para>The following sample program uses the server-side sorting control to
get a list of all users in the directory. The list is sorted in ascending
order by last name, then in descending order by first name.</para>
<programlisting>import netscape.ldap.*;
import netscape.ldap.controls.*;
import java.util.*;
public class SrchSort {
public static void main(String[] args) {
try {
UserArgs userArgs = new UserArgs("SrchSort", args, false);
LDAPConnection ld = new LDAPConnection();
ld.connect(userArgs.getHost(), userArgs.getPort());
ld.authenticate(3, null, null); // Anonymous bind, LDAP v3
String filter = "sn=Jen*";
String baseDN = "ou=People,dc=example,dc=com";
String[] attrs = {"sn", "givenname"};
/* Sort by last name, then in reverse by first name. */
LDAPSortKey sortByLastName = new LDAPSortKey("sn");
LDAPSortKey sortByFirstName = new LDAPSortKey("-givenname");
LDAPSortKey[] sortOrder = {sortByLastName, sortByFirstName};
LDAPSortControl sortCtrl = new LDAPSortControl(sortOrder, true);
LDAPSearchConstraints cons = ld.getSearchConstraints();
cons.setServerControls(sortCtrl);
/* Perform the search using the control. */
LDAPSearchResults res = ld.search(baseDN, LDAPv3.SCOPE_SUB,
filter, attrs, false, cons);
/* Display the results. */
System.out.println("Sorted results from server");
System.out.println("==========================");
while (res.hasMoreElements()) {
LDAPEntry resEntry = null;
try {
resEntry = res.next();
} catch (LDAPReferralException e) {
continue; // Skip referrals for now.
} catch (LDAPException e) {
System.err.println("Error:" + e.toString());
continue;
}
LDAPAttributeSet resAttrs = resEntry.getAttributeSet();
Enumeration enumAttrs = resAttrs.getAttributes();
while (enumAttrs.hasMoreElements()) {
LDAPAttribute attr =
(LDAPAttribute)enumAttrs.nextElement();
Enumeration enumVals = attr.getStringValues();
if (enumVals == null) {
System.out.println("\tNo values.");
continue;
}
while (enumVals.hasMoreElements()) {
String val = (String)enumVals.nextElement();
System.out.print(val);
}
System.out.print(", ");
}
System.out.println("");
}
/* Check whether the server sent back a control. */
LDAPControl[] returnedControls = res.getResponseControls();
if (returnedControls != null) {
for (int i = 0; i &lt; returnedControls.length; ++i) {
if (!(returnedControls[i] instanceof LDAPSortControl)) {
continue;
}
LDAPSortControl sortRsp =
(LDAPSortControl)returnedControls[i];
int resultCode = sortRsp.getResultCode();
/* Check the result for errors. */
if (resultCode != 0) {
System.err.println("Result code: " + resultCode);
System.err.println(
LDAPException.errorCodeToString(resultCode));
/* Report attribute that caused the failure. */
String failedAttr = sortRsp.getFailedAttribute();
if (failedAttr != null) {
System.err.println("Failed on: " + failedAttr);
} else {
System.err.println("Server did not indicate " +
"which attribute caused sorting to fail.");
}
}
}
}
ld.disconnect();
} catch(LDAPException e) {
System.out.println("Error: " + e.toString());
}
}
}</programlisting>
</sect2>
</sect1>
<sect1 id="controls-psearch"><title>Using the Persistent Search Control With &DirectorySDKForJava;</title>
<indexterm>
<primary>persistent searches</primary>
</indexterm>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.LDAPControl.PERSISTENTSEARCH</constant> is a persistent search
control. A <firstterm>persistent search</firstterm>, an ongoing search operation,
allows your LDAP client to get notification of changes to the directory.</para>
<para>To use persistent searching for change notification, you create a persistent
search control that specifies the types of changes that you want to track.
You include the control in a search request. If an entry in the directory
is changed, the server determines if the entry matches the search criteria
in your request. The server also determines if the change is the type of change
that you are tracking. If both of the conditions are true, the server sends
the entry to your client.</para>
<para>You can use this control in conjunction with an entry change notification
control, as described in <olink type="auto-generated" targetptr="controls-entry-change">Using the Entry Change Notification Control
With Directory SDK for Java</olink>.</para>
<sect2 id="controls-psearch-create"><title>Creating the Persistent Search
Control With Directory SDK for Java</title>
<itemizedlist>
<para>To create a persistent search control, you construct a new <classname>LDAPPersistSearchControl
</classname> object. When invoking the <literal>LDAPPersistSearchControl</literal> constructor,
you can specify the following information:</para>
<listitem>
<itemizedlist>
<para>The type of change you want to track. You can specify any of the following,
or any combination of the following, using a bitwise OR (<literal>|</literal>)
operator:</para>
<listitem><para><literal>ADD</literal> indicates that you want to track added
entries.</para></listitem>
<listitem><para><literal>DELETE</literal> indicates that you want to track
deleted entries.</para></listitem>
<listitem><para><literal>MODDN</literal> indicates that you want to track
renamed entries.</para></listitem>
<listitem><para><literal>MODIFY</literal> indicates that you want to track
modified entries.</para></listitem>
</itemizedlist>
</listitem>
<listitem><para>A preference indicating whether you want the server to return
all entries that initially matched the search criteria</para></listitem>
<listitem><para>A preference indicating whether or not you want entry change
notification controls included with every modified entry returned by the server</para>
</listitem>
</itemizedlist>
<para>This code excerpt shows an example of creating a persistent search control.
</para>
<programlisting>/* Track all types of changes. */
int op = LDAPPersistSearchControl.ADD |
LDAPPersistSearchControl.MODIFY |
LDAPPersistSearchControl.DELETE |
LDAPPersistSearchControl.MODDN;
/* Return only entries that have changed. */
boolean changesOnly = true;
/* Return an entry change notification control. */
boolean returnControls = true;
/* Mark the control as critical. */
boolean isCritical = true;
/* Create the control. */
LDAPPersistSearchControl persistCtrl =
new LDAPPersistSearchControl(
op, changesOnly, returnControls, isCritical);</programlisting>
</sect2>
<sect2 id="controls-psearch-search"><title>Performing the Persistent Search
With Directory SDK for Java</title>
<orderedlist>
<para>To specify that you want to start a persistent search, do the following:</para>
<listitem><para>Get a clone of <classname>LDAPSearchConstraints</classname> for
the current connection by invoking the <literal>getSearchConstraints</literal> method
of the <classname>LDAPConnection</classname> object.</para></listitem>
<listitem><para>Invoke the <literal>setServerControls</literal> method for
the cloned <classname>LDAPSearchConstraints</classname> object, and pass in
the <classname>LDAPPersistSearchControl</classname> object that you have constructed.
</para></listitem>
<listitem><para>Invoke the search method of the <classname>LDAPConnection</classname> object,
passing in the <classname>LDAPSearchConstraints</classname> object.</para><para>The
server returns entries that match as the entries change. If you specified
that you wanted an entry change notification control included with each entry,
you can get these controls from the server's results. For instructions, refer
to <olink type="auto-generated" targetptr="controls-entry-change">Using the
Entry Change Notification Control With Directory SDK for Java</olink>.</para>
</listitem></orderedlist>
<para>To end the persistent search, invoke the <literal>abandon</literal> method
of the <classname>LDAPConnection</classname> object. Alternatively, invoke
the <literal>disconnect</literal> method to disconnect from the server.</para>
</sect2>
<sect2 id="controls-psearch-example"><title>Example of Using the Persistent
Search Control With Directory SDK for Java</title>
<para>The following example performs a persistent search. The example receives
entry change notification controls from the server.</para>
<programlisting>import netscape.ldap.*;
import netscape.ldap.controls.*;
import java.util.*;
public class SrchPrst implements Runnable {
private static String [] cliArgs;
public static void main(String[] args) {
cliArgs = args;
/* Start up a new thread. */
Thread th = new Thread(new SrchPrst(), "mainConn");
th.start();
System.out.println("Main thread started.");
}
public void run() {
try {
UserArgs userArgs = new UserArgs("SrchPrst", cliArgs, true);
LDAPConnection ld = new LDAPConnection();
ld.connect(userArgs.getHost(), userArgs.getPort());
ld.authenticate(3, userArgs.getBindDN(),userArgs.getPassword());
/* Create the control and search constraints. */
int op = LDAPPersistSearchControl.ADD |
LDAPPersistSearchControl.MODIFY |
LDAPPersistSearchControl.DELETE |
LDAPPersistSearchControl.MODDN;
boolean changesOnly = true;
boolean returnControls = true;
boolean isCritical = true;
LDAPPersistSearchControl persistCtrl =
new LDAPPersistSearchControl(op, changesOnly,
returnControls, isCritical);
LDAPSearchConstraints cons = ld.getSearchConstraints();
cons.setServerControls(persistCtrl);
/* Track changes on all entries. */
String filter = "(objectclass=*)";
String baseDN = "dc=example,dc=com";
/* Start the search. */
LDAPSearchResults res =
ld.search(
baseDN, LDAPv3.SCOPE_SUB, filter, null, false, cons);
/* Loop through the results until finished. */
while (res.hasMoreElements()) {
System.out.println("\n===== Changed Entry =====");
LDAPEntry findEntry = res.next();
LDAPAttributeSet findAttrs = findEntry.getAttributeSet();
Enumeration enumAttrs = findAttrs.getAttributes();
while (enumAttrs.hasMoreElements()) {
LDAPAttribute anAttr =
(LDAPAttribute)enumAttrs.nextElement();
String attrName = anAttr.getName();
System.out.println("\t" + attrName);
Enumeration enumVals = anAttr.getStringValues();
while (enumVals.hasMoreElements()) {
String aVal = (String)enumVals.nextElement();
System.out.println("\t\t" + aVal);
}
}
/* Get any entry change controls. */
LDAPControl[] responseCtrls = res.getResponseControls();
if (responseCtrls != null) {
for (int i=0; i &lt; responseCtrls.length; i++){
if (!(responseCtrls[i] instanceof
LDAPEntryChangeControl)) {
continue;
}
LDAPEntryChangeControl entryCtrl =
(LDAPEntryChangeControl) responseCtrls[i];
/* Get information on the type of change */
int changeType = entryCtrl.getChangeType();
if (changeType != -1) {
System.out.print("Change made: ");
switch (changeType) {
case LDAPPersistSearchControl.ADD:
System.out.println("Added new entry.");
break;
case LDAPPersistSearchControl.MODIFY:
System.out.println("Modified entry.");
break;
case LDAPPersistSearchControl.DELETE:
System.out.println("Deleted entry.");
break;
case LDAPPersistSearchControl.MODDN:
System.out.println("Renamed entry.");
break;
default:
System.out.println(
"Unknown change type.");
break;
}
}
/* Get the change log number, if present */
int changeNumber = entryCtrl.getChangeNumber();
if (changeNumber != -1) {
System.out.println(
"Change log number: " + changeNumber);
}
/* Get the previous DN of the entry, if
a rename operation was performed. */
String oldDN = entryCtrl.getPreviousDN();
if (oldDN != null) {
System.out.println("Previous DN: " + oldDN);
}
}
System.out.println("\n");
}
}
} catch(LDAPException e) {
System.out.println("Error: " + e.toString());
}
}
}</programlisting>
</sect2>
</sect1>
<sect1 id="controls-entry-change"><title>Using the Entry Change Notification
Control With &DirectorySDKForJava;</title>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.LDAPEntryChangeControl.ENTRYCHANGED</constant> is an <firstterm>entry
change notification</firstterm> control. These types of controls can be included
with entries sent back from the server during a persistent search.</para>
<sect2 id="controls-entry-change-get"><title>Getting the Entry Change Notification
Control With Directory SDK for Java</title>
<orderedlist>
<para>To get an entry change notification control that is included with an
entry, do the following:</para>
<listitem><para>As you retrieve each entry, invoke the <literal>getResponseControls
</literal> method of the <classname>LDAPConnection</classname> object to retrieve
any response controls sent back from the server.</para><para>Response controls
are passed back as an array of <classname>LDAPControl</classname> objects. </para>
</listitem>
<listitem><para>Pass this array of <classname>LDAPControl</classname> objects
as an argument to the <literal>LDAPPersistSearchControl.parseResponse</literal> static
method to retrieve the entry change notification control.</para></listitem>
</orderedlist>
<para>An entry change notification control is represented by an object of
the <classname>LDAPEntryChangeControl</classname> class. To get data from
this control, you can invoke the accessor methods, such as <literal>getChangeNumber
</literal>, <literal>getChangeType</literal>, and <literal>getPreviousDN</literal>.
</para></sect2>
<sect2 id="controls-entry-change-work"><title>Working With Change Log Numbers
With Directory SDK for Java</title>
<para>If the directory server is set up to be a supplier, the server is capable
of replicating changes in the directory to other servers. The server therefore
keeps a record of the changes made to the directory in a change log. Each
record of a change has a number that identifies the change in the log.</para>
<para>You can get the change number for a modified entry from the <classname>LDAPEntryChangeControl
</classname> object.</para>
<para>To look up the record for a particular change log number to get more
information about the change that took place, search for the record. With
Directory Server, the change log is represented by an entry in the directory.
Individual change records are represented by entries in a subtree beneath
the change log entry.</para>
<para>To determine the DN for the change log entry, search the root DSE to
retrieve the <literal>changelog</literal> attribute. For example, the value
of this attribute might be <literal>cn=changelog</literal>, which is the DN
for the change log entry.</para>
<para>Each change log record is an entry under the change log entry. The change
log number is the value of the <literal>changenumber</literal> attribute of
the record. To get a specific change log record, search with base DN <literal>cn=changelog
</literal> and filter <literal>changenumber=</literal><replaceable>value</replaceable>.
Here, <replaceable>value</replaceable> is the change number of the record.</para>
</sect2>
</sect1>
<sect1 id="controls-vlv"><title>Using the Virtual List Control With &DirectorySDKForJava;</title>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.controls.LDAPVirtualListControl.VIRTUALLIST</constant> is a <firstterm>
virtual list</firstterm> control. When you send a search request with this
control and with a server-side sorting control to the server, the server should
sort the results and return the specified subset of entries back to your client.</para>
<para>After you set the list size with the <literal>setListSize</literal> method
of the <classname>LDAPVirtualListControl</classname> object, invoke the <literal>
setRange</literal> method. The method recreates the control with the new data. <literal>
setRange</literal> generates the BER-encoded request to be sent to the server. <literal>
setListSize</literal> does not generate the request.</para></sect1>
<sect1 id="controls-managedsait"><title>Using the Manage DSA IT Control With &DirectorySDKForJava;</title>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.controls.LDAPControl.MANAGEDSAIT</constant> is a <firstterm>manage
DSA IT</firstterm> control. The control is for managing search references
in the directory and is defined in <ulink
url="http://www.ietf.org/rfc/rfc3296.txt" type="text_url">RFC
3296</ulink>.</para>
<para>To create this control, construct a new <classname>LDAPControl</classname> object.
In the <literal>LDAPControl</literal> constructor, set the OID of the control
to <constant>netscape.ldap.controls.LDAPControl.MANAGEDSAIT</constant>.</para>
<para>Add this control to the array of <classname>LDAPControl</classname> objects.
When you pass the array to a method that performs an LDAP operation, the server
treats search references as ordinary entries.</para>
<para>Rather than returning a reference to you, the server returns the entry
that contains the reference. This mechanism allows your client application
to manage search references in the directory.</para></sect1>
<sect1 id="controls-pwp"><title>Using Password Policy Controls With &DirectorySDKForJava;</title>
<itemizedlist>
<para>&cnDirectoryServer; uses two server response controls to send information
back to a client after an LDAP bind operation.</para>
<listitem><para>The control represented with the OID that corresponds to the
constant <constant>netscape.ldap.controls.LDAPControl.PWEXPIRED</constant> is
the <firstterm>expired password</firstterm> control.</para><para>This control
is used if the server is configured to require users to change their passwords
on first login. The control is also used whenever the passwords are reset.</para>
<para>The user might be logging in for the first time. The user password might
have been reset. In either circumstance, the server sends the control to indicate
that the client needs to change the password immediately. When receiving this
control, the only operation that the client can perform is to change the user's
password. If the client requests any other LDAP operation, the server sends
back an <returnvalue>LDAP_UNWILLING_TO_PERFORM</returnvalue> result code with
an expired password control.</para></listitem>
<listitem><para>The control represented with the OID that corresponds to the
constant <constant>netscape.ldap.controls.LDAPControl.PWEXPIRING</constant> is
the <firstterm>password expiring warning</firstterm> control.</para><para>This
control is used if the server is configured to expire user passwords after
a certain amount of time.</para><para>The server sends this control back to
the client if the client binds with a password that is soon to expire. If
you invoke the <literal>getValue</literal> method for this <classname>LDAPControl
</classname> object, the method returns the number of seconds before the password
expires.</para></listitem>
</itemizedlist>
<para>To get these server response controls when binding, invoke the <literal>getResponseControls
</literal> method of the <classname>LDAPConnection</classname> object after
you attempt to authenticate to the server.</para></sect1>
<sect1 id="controls-proxyauth"><title>Using the Proxied Authorization Control
With &DirectorySDKForJava;</title>
<para>The control represented with the OID that corresponds to the constant <constant>
netscape.ldap.controls.LDAPProxiedAuthControl.PROXIEDAUTHREQUEST</constant> is
a <firstterm>proxied authorization</firstterm> control. It allows LDAP clients
to use different credentials, without rebinding, when executing LDAP operations.</para>
<para>For example, suppose a messaging server stores its user profiles on
an LDAP server. For certain types of requests the messaging server needs to
use a DN and password other than its own. To use another DN and password without
proxied authorization requires the messaging server to rebind, using the different
credentials, before executing each operation.</para>
<para>If the messaging server uses the proxied authorization control, the
server can act as the user when executing an operation. The messaging server
maintains only its own binding to the LDAP server. This mechanism drastically
improves performance, especially when processing a large number of requests.</para>
</sect1>
</chapter>