<!--

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="searching"><title>Searching the Directory With &DirectorySDKForJava;</title>
<highlights>
<para>This chapter explains how to use the LDAP Java classes to search the
directory to retrieve entries. The chapter also describes how to get attributes
and attribute values from an entry.</para>
<itemizedlist>
<para>This chapter covers the following topics:</para>
<listitem><para><olink targetptr="searching-overview">Searching With the LDAP
Java Classes</olink></para></listitem>
<listitem><para><olink targetptr="searching-request">Sending a Search Request
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-results">Getting the Search Results
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-sorting">Sorting the Search Results
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-abandoning">Abandoning a Search
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-example">Searching the Directory
With Directory SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-read">Reading an Entry With Directory
SDK for Java</olink></para></listitem>
<listitem><para><olink targetptr="searching-child-entries">Listing Child Entries
With Directory SDK for Java</olink></para></listitem>
</itemizedlist>
</highlights>
<sect1 id="searching-overview"><title>Searching With the LDAP Java Classes</title>
<itemizedlist>
<para>In &DirectorySDKForJava;, searches are represented by objects of
the following classes:</para>
<listitem><para>You can send a search request by invoking the <literal>search</literal> method
of the <classname>LDAPConnection</classname> object.</para></listitem>
<listitem><para>You can specify a set of search constraints by using an <classname>
LDAPSearchConstraints</classname> object. The constraints can specify the
maximum number of results to return. The constraints can also specify the
maximum amount of time that is allowed for a search.</para></listitem>
<listitem><para>You can specify different parts of the search criteria in
separate arguments. Alternatively, you can construct an <classname>LDAPUrl</classname> object
to specify the search criteria.</para></listitem>
<listitem><para>You can search for a single entry by invoking the <literal>read</literal> method
of the <classname>LDAPConnection</classname> object.</para></listitem>
<listitem><para>The server returns the search results to the LDAP Java classes,
which represents the results as an <classname>LDAPSearchResults</classname> object.
</para></listitem>
</itemizedlist>
</sect1>
<sect1 id="searching-request"><title>Sending a Search Request With &DirectorySDKForJava;</title>
<para>To search the directory, use the <literal>search</literal> method of
the <classname>LDAPConnection</classname> object. The search results are returned
in the form of an <classname>LDAPSearchResults</classname> object.</para>
<programlisting>public LDAPSearchResults search(String base, int scope, 
&nbsp;&nbsp;&nbsp;&nbsp;String filter, String attrs[], boolean attrsOnly, 
&nbsp;&nbsp;&nbsp;&nbsp;LDAPSearchConstraints cons) throws LDAPException</programlisting>
<para>You need to specify the following parameters as arguments to the <literal>search
</literal> method.</para>
<variablelist>
<varlistentry><term><parameter>base</parameter></term>
<listitem><para>Specifies the base DN, which is the entry on and under which
the search is carried out.</para>
<para>For example, when searching entries with DNs such as <literal>uid=bjensen,ou=People,dc=example,dc=com
</literal>, the <parameter>base</parameter> could be <literal>ou=People,dc=example,dc=com
</literal> or <literal>dc=example,dc=com</literal>.</para>
</listitem>
</varlistentry>
<varlistentry><term><parameter>scope</parameter></term>
<listitem><para>Specifies the scope of the search.</para>
<para>You can adjust the scope of the search to examine only the entry identified
by the <parameter>base</parameter>, only those entries one level down the
tree from the <parameter>base</parameter>, or the entire subtree underneath
the <parameter>base</parameter>.</para>
</listitem>
</varlistentry>
<varlistentry><term><parameter>filter</parameter></term>
<listitem><para>Specifies what to search for.</para>
<para>A search filter specifies what search results to return. The filter
meaning can be simple, such as &ldquo;find entries where the last name is
Jensen&rdquo;. The filter meaning can also be complex, such as &ldquo;find
entries that belong to Dept. #17 and with first names that start with the
letter F.&rdquo;</para>
</listitem>
</varlistentry>
<varlistentry><term><parameter>attrs</parameter></term><term><parameter>attrsOnly
</parameter></term>
<listitem><para>Specify the entry attributes to retrieve.</para>
<para>For example, you can use <parameter>attrs</parameter> to retrieve only
email addresses and phone numbers. Alternatively, you can set up a search
to return all attributes in an entry. You can also specify to return only
the names of attributes, not the values, by setting <parameter>attrsOnly</parameter> to <constant>
true</constant>.</para>
</listitem>
</varlistentry>
<varlistentry><term><parameter>cons</parameter></term>
<listitem><para>Specifies constraints to apply to the search when you do not
want to use the default constraints.</para>
</listitem>
</varlistentry>
</variablelist>
<para>The following figure illustrates how search criteria work.</para>
<figure id="searching-criteria"><title>Criteria for an LDAP Search</title>
<mediaobject>
<imageobject><imagedata entityref="hierovrw"></imageobject>
<textobject><simpara>How search criteria work</simpara></textobject>
</mediaobject>
</figure>
<para>You can also specify the criteria in the form of an LDAP URL. An LDAP
URL allows you to specify the host name and port number of the LDAP server
that you want to search. To search a different LDAP server than the server
you are connected to, you can invoke the search method. You then specify an
LDAP URL in the form of an <classname>LDAPUrl</classname> object. See <olink
targetptr="ldap-urls">Chapter&nbsp;7, LDAP URLs With Directory SDK for Java</olink> for
details.</para>
<sect2 id="searching-specify-base"><title>Specifying the Base DN and Scope</title>
<indexterm>
<primary>base DN</primary>
<secondary>explained</secondary>
</indexterm><indexterm>
<primary>scope</primary>
<secondary>explained</secondary>
</indexterm>
<para>When sending a search request, you need to specify the base DN and scope
of the search to identify the entries that you want searched.</para>
<para>The base DN is the DN of the entry that serves as the starting point
of the search.</para>
<itemizedlist>
<para>To specify the scope of the search, you pass one of the following values
as the scope parameter:</para>
<listitem><para><constant>LDAPv3.SCOPE_SUB</constant> &mdash; Search the base
entry and all entries at all levels under the base entry.</para>
<mediaobject>
<imageobject><imagedata entityref="hiersrch"></imageobject>
<textobject><simpara>Subtree scope applies to everything below the base DN.</simpara>
</textobject>
</mediaobject>
</listitem>
<listitem><para><constant>LDAPv3.SCOPE_ONE</constant> &mdash; Search all entries
at one level under the base entry.</para>
<mediaobject>
<imageobject><imagedata entityref="hier1lvl"></imageobject>
<textobject><simpara>One level scope applies to all entries just below the
base DN.</simpara></textobject>
</mediaobject>
<para>The base entry is not included in the search. Use this setting if you
just want a list of the entries under a given entry.</para></listitem>
<listitem><para><constant>LDAPv3.SCOPE_BASE</constant> &mdash; Search only
the base entry.</para>
<mediaobject>
<imageobject><imagedata entityref="hierbase"></imageobject>
<textobject><simpara>Base scope applies only to the base DN entry.</simpara>
</textobject>
</mediaobject>
<para>Use this setting if you want to read the attributes of only the base
entry.</para></listitem>
</itemizedlist>
</sect2>
<sect2 id="searching-filter"><title>Specifying a Search Filter</title>
<indexterm>
<primary>filters</primary>
</indexterm><indexterm>
<primary>search filters</primary>
<secondary>specifying</secondary>
</indexterm>
<para>When you search the directory, you use a search filter to define the
search. Here is the basic syntax for a search filter:</para>
<programlisting>(<replaceable>attribute</replaceable> <replaceable>operator</replaceable> <replaceable>
value</replaceable>)</programlisting>
<para>Here is a simple example of a search filter:</para>
<programlisting>(cn=Barbara Jensen)</programlisting>
<para>In this example, <literal>cn</literal> is the attribute. <literal>=</literal> is
the operator. <literal>Barbara Jensen</literal> is the value. The filter finds
entries with the common name <literal>Barbara Jensen</literal>.</para>
<para>Valid attributes that you can use in your search filter are provided
in the documentation for the LDAP server.</para>
<para>Following are descriptions of valid operators for search filters, and
example filters that use the operators.</para>
<variablelist>
<varlistentry><term><literal>=</literal></term>
<listitem><para>Return entries whose attributes are equal to the value provided.</para>
<para>For example, the following filter matches Barbara Jensen's entry:</para>
<programlisting>(cn=Barbara Jensen)</programlisting>
</listitem>
</varlistentry>
<varlistentry><term><literal>>=</literal></term>
<listitem><para>Return entries whose attributes are greater than or equal
to the value provided.</para>
<para>For example, the following filter matches Barbara Jensen's entry and
entries for people with surnames following Jensen in alphabetic order, such
as entries with <literal>sn=Seuss</literal> and <literal>sn=Zhivago</literal>:</para>
<programlisting>(sn>=jensen)</programlisting>
</listitem>
</varlistentry>
<varlistentry><term><literal>&lt;=</literal></term>
<listitem><para>Return entries whose attributes are less than or equal to
the value provided.</para>
<para>For example, the following filter matches Barbara Jensen's entry and
entries for people with surnames that precede Jensen in alphabetic order,
such as entries with <literal>sn=Anderson</literal> and <literal>sn=Cubbins</literal>:
</para>
<programlisting>(sn&lt;=jensen)</programlisting>
</listitem>
</varlistentry>
<varlistentry><term><literal>=*</literal></term>
<listitem><para>Return entries that have a value set for the attribute (presence).
</para>
<para>For example, the following filter matches all entries that have a value
for the surname:</para>
<programlisting>(sn=*)</programlisting>
</listitem>
</varlistentry>
<varlistentry><term><literal>~=</literal></term>
<listitem><para>Return entries whose attribute value approximately matches
the specified value, such as the value sounds like the specified value.</para>
<para>For example, the following filter matches all entries with values for
surname that sound like Jensen, such as Barbara Jensen's entry, but also Emanuel
Johnson's entry:</para>
<programlisting>(sn~=jensen)</programlisting>
</listitem>
</varlistentry>
</variablelist>
<para>With Boolean operators and with parentheses, you can combine different
sets of conditions. Here is the syntax for combining search filters:</para>
<programlisting>(<replaceable>boolean</replaceable>(<replaceable>filter1</replaceable>)(<replaceable>
filter2</replaceable>)(&hellip;))</programlisting>
<para>Following are descriptions of the valid boolean operators.</para>
<variablelist>
<varlistentry><term><literal>&amp;</literal></term>
<listitem><para>Return entries that match all specified filters.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>|</literal></term>
<listitem><para>Return entries that match one or more of the specified filters.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>!</literal></term>
<listitem><para>Return entries that do not match the specified filter.</para>
<para>This operator is unary because you can apply the operator only to a
single set of results. In other words, to specify &ldquo;entries that match
neither <replaceable>filter1</replaceable> nor <replaceable>filter2</replaceable>,&rdquo;
use the syntax:</para>
<programlisting>(!(|(<replaceable>filter1</replaceable>)(<replaceable>filter2</replaceable>)))
</programlisting>
</listitem>
</varlistentry>
</variablelist>
<para>You can also include wildcard characters to search for entries that
start with, contain, or end with a given value. For example, you can use the
following filter to search for all entries with first names that begin with
the letter <literal>F</literal>:</para>
<programlisting>(givenName=F*)</programlisting>
</sect2>
<sect2 id="searching-specify-attrs"><title>Specifying the Attributes to Retrieve</title>
<indexterm>
<primary>attributes</primary>
<secondary>operational</secondary>
</indexterm><indexterm>
<primary>attributes</primary>
<secondary>retrieving in a search</secondary>
</indexterm><indexterm>
<primary>operational attributes</primary>
</indexterm>
<itemizedlist>
<para>With the <parameter>attrs</parameter> parameter, you can retrieve all
attributes in entries returned by the search. Alternatively, you can specify
the attributes that you want returned in the search results. For example,
you can specify to return the attributes in one of the following ways:</para>
<listitem><para>To return selected attributes, pass an array of the attribute
names as the <parameter>attrs</parameter> parameter. For example, to return
only email addresses and phone numbers, pass the array <literal>{"mail", "telephoneNumber"}
</literal> as the <parameter>attrs</parameter> parameter.</para></listitem>
<listitem><para>To return all attributes in an entry, pass <constant>null</constant> as
the <parameter>attrs</parameter> parameter.</para></listitem>
<listitem><para>To return no attributes from an entry, pass <constant>LDAPv3.NO_ATTRS
</constant> as the <parameter>attrs</parameter> parameter.</para></listitem>
</itemizedlist>
<para>You might plan to sort the results on your client as described in <olink
targetptr="searching-sorting">Sorting the Search Results With Directory SDK
for Java</olink>. Return the attributes that you plan to use for sorting.
For example, if you plan to sort by email address, make sure that the mail
attribute is returned in the search results.</para>
<para>Some attributes are used by servers for administering the directory.
For example, the <literal>creatorsName</literal> attribute specifies the DN
of the user who added the entry. These attributes are called <firstterm>operational
attributes</firstterm>.</para>
<para>Servers do not normally return operational attributes in search results
unless you specify the attributes by name. For example, if you pass <constant>null
</constant> as the <parameter>attrs</parameter> parameter to retrieve all
of the attributes in entries found by the search, the operational attribute <literal>
creatorsName</literal> is not returned to your client. You need to explicitly
specify the <literal>creatorsName</literal> attribute in the <parameter>attrs</parameter> parameter.
</para>
<para>To return all attributes in an entry with selected operational attributes,
pass a string array containing <constant>LDAPv3.ALL_USER_ATTRS</constant>,
and also the names of the operational attributes as the <parameter>attrs</parameter> parameter.
Following are a few operational attributes and a description of what each
attribute contains.</para>
<variablelist>
<varlistentry><term><literal>createTimestamp</literal></term>
<listitem><para>The time when the entry was added to the directory.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>modifyTimestamp</literal></term>
<listitem><para>The time when the entry was last modified.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>creatorsName</literal></term>
<listitem><para>Distinguished name (DN) of the user who added the entry to
the directory.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>modifiersName</literal></term>
<listitem><para>DN of the user who last modified the entry.</para>
</listitem>
</varlistentry>
<varlistentry><term><literal>subschemaSubentry</literal></term>
<listitem><para>DN of the subschema entry, that controls the schema for this
entry.</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="searching-set-prefs"><title>Setting Search Preferences</title>
<para>For a given search, you can apply a set of preferences that determine
how the search is performed. For example, you can specify the maximum number
of results to be returned or the maximum amount of time to wait for a search.</para>
<para>The <classname>LDAPSearchConstraints</classname> class represents a
set of search constraints. The methods of this class allow you to get and
set the constraints.</para>
<sect3 id="searching-set-prefs-all"><title>Setting Preferences for All Searches</title>
<indexterm>
<primary>search results</primary>
<secondary>setting preferences</secondary>
</indexterm>
<para>The <classname>LDAPConnection</classname> object, which represents a
connection to the LDAP server, is associated with a default set of search
constraints. These constraints apply to all searches that you perform over
the connection.</para>
<itemizedlist>
<listitem><para>To get the default set of search constraints for the connection,
you can use the <literal>getSearchConstraints</literal> method.</para>
</listitem>
<listitem><para>To get or set any of the search constraints individually,
you can use the <literal>getOption</literal> method and the <literal>setOption</literal> method.
</para></listitem>
</itemizedlist>
<para>For example, if you want to specify the maximum number of results returned,
you can set this constraint for the connection:</para>
<programlisting>LDAPConnection ld = new LDAPConnection();
ld.connect("ldap.example.com", LDAPv3.DEFAULT_PORT); 
ld.setOption(LDAPv3.SIZELIMIT, new Integer(100));</programlisting>
</sect3>
<sect3 id="searching-set-prefs-override"><title>Overriding Preferences for
Individual Searches</title>
<para>To override the default set of search constraints for a given search
request, construct your own <classname>LDAPSearchConstraints</classname> object.
Pass the object to the <literal>search</literal> method of the <classname>LDAPConnection
</classname> object.</para>
<para>You can also modify a copy of the existing search constraints. Pass
the modified set of constraints to the <literal>search</literal> method. Invoke
the <literal>getSearchConstraints</literal> method of the <classname>LDAPConnection
</classname> object to get the default set of constraints for that connection.
Then invoke the clone method of the <classname>LDAPSearchConstraints</classname> object
to make a copy of the set that you can then modify.</para></sect3>
<sect3 id="searching-set-prefs-wait"><title>Configuring the Search to Wait
for All Results</title>
<indexterm>
<primary>search results</primary>
<secondary>waiting for all</secondary>
</indexterm>
<para>By default, the <literal>search</literal> method of the <classname>LDAPConnection
</classname> object does not block until all results are received. Instead,
the <literal>search</literal> method returns as soon as one of the results
has been received.</para>
<itemizedlist>
<para>If you want the <literal>search</literal> method to block until all
results are received, you can do one of the following:</para>
<listitem><para>Use the <literal>setOption</literal> method of the <classname>LDAPConnection
</classname> object to set the <constant>LDAPv3.BATCHSIZE</constant> preference
to <literal>0</literal>.</para></listitem>
<listitem><para>Pass a <literal>0</literal> to the <literal>setBatchSize</literal> method
of the <classname>LDAPSearchConstraints</classname> object to change the behavior
for a particular set of search constraints.</para></listitem>
</itemizedlist>
<para>Whether waiting for one or all results of the <literal>search</literal> method,
you still need to invoke the <literal>next</literal> method of the returned <classname>
LDAPSearchResults</classname> object to retrieve each individual result.</para>
</sect3>
<sect3 id="searching-set-prefs-size"><title>Setting Size and Time Limits</title>
<indexterm>
<primary>search results</primary>
<secondary>setting size limits</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>setting time limits</secondary>
</indexterm>
<para>By default, when you search the directory from a client that you built
with &DirectorySDKForJava;, the maximum number of entries to return is
set to <literal>1000</literal>. No maximum time limit is set for waiting on
an operation to complete.</para>
<itemizedlist>
<para>To change these default values, you can do one of the following:</para>
<listitem><para>Use the <literal>setOption</literal> method of the <classname>LDAPConnection
</classname> object to set the <constant>LDAPv3.SIZELIMIT</constant> and <constant>
LDAPv3.TIMELIMIT</constant> preferences.</para></listitem>
<listitem><para>Use the <literal>setMaxResults</literal> method and the <literal>
setTimeLimit</literal> method of the <classname>LDAPSearchConstraints</classname> object
to change the behavior for a particular set of search constraints.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<para>When you set the size limit or time limit, you might cause an <classname>LDAPException
</classname> to be returned. The exception is returned when the limit is exceeded.
</para>
<listitem><para>If the size limit is exceeded, the server returns an <constant>LDAPException.SIZE_LIMIT_EXCEEDED
</constant> result code.</para></listitem>
<listitem><para>If the time limit is exceeded, the server returns an <constant>LDAPException.TIME_LIMIT_EXCEEDED
</constant> result code.</para></listitem>
</itemizedlist>
</sect3>
</sect2>
<sect2 id="searching-example-request"><title>Search Request Example</title>
<para>The following section of code searches for all entries with surname <literal>
Jensen</literal>. The search retrieves the names and values of the <literal>cn</literal>, <literal>
mail</literal>, and <literal>telephoneNumber</literal> attributes.</para>
<programlisting>LDAPConnection ld = null;
try {
    /* Create a new LDAPConnection object. */
    ld = new LDAPConnection();

    /* Connect and bind to the server. */
    String HOSTNAME = "localhost";
    ld.connect(HOSTNAME, LDAPv3.DEFAULT_PORT, null, null);

    /* Specify the search criteria. */
    String baseDN = "dc=example,dc=com";
    int searchScope = LDAPv3.SCOPE_SUB;
    String searchFilter = "(sn=Jensen)";
    String getAttrs[] = {"cn", "mail", "telephoneNumber"};

    /* Send the search request. */
    LDAPSearchResults res = ld.search(baseDN, searchScope,
        searchFilter, getAttrs, false);
} catch(LDAPException e) {
    System.out.println("Error: " + e.toString);
}</programlisting>
</sect2>
</sect1>
<sect1 id="searching-results"><title>Getting the Search Results With &DirectorySDKForJava;</title>
<indexterm>
<primary>search results</primary>
<secondary>getting</secondary>
</indexterm>
<para>When you invoke the <literal>search</literal> method of an <classname>LDAPConnection
</classname> object to search the directory, the method returns the search
results in the form of an <classname>LDAPSearchResults</classname> object.</para>
<para>The search results consist of an enumeration of entries, which are represented
by <classname>LDAPEntry</classname> objects. The search results can also include
smart referrals, also known as <firstterm>search references</firstterm>, and
exceptions.</para>
<para>Each entry contains a set of attributes, which are represented by <classname>
LDAPAttributeSet</classname> objects. Individual attributes are represented
by <classname>LDAPAttribute</classname> objects. Each attribute has a set
of values that you can get.</para>
<para>The following figure illustrates the relationship between entries, attributes,
values, and search results.</para>
<figure id="searching-result-set"><title>Entries, Attributes, and Values in
Search Results</title>
<mediaobject>
<imageobject><imagedata entityref="entries"></imageobject>
<textobject><simpara>Relationship between entries, attributes, values, and
search results</simpara></textobject>
</mediaobject>
</figure>
<sect2 id="searching-results-entries"><title>Getting Entries</title>
<indexterm>
<primary>entries</primary>
<secondary>getting from search results</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>getting entries</secondary>
</indexterm>
<para>The <classname>LDAPSearchResults</classname> object represents the results
of the search. These results can include entries found by the search, search
references, and result codes. Your LDAP client can receive an <returnvalue>ADMIN_LIMIT_EXCEEDED
</returnvalue>, <returnvalue>TIME_LIMIT_EXCEEDED</returnvalue>, or <returnvalue>SIZE_LIMIT_EXCEEDED
</returnvalue> result code from the server. When the result code is received, &DirectorySDKForJava; adds
an exception for this result code to the search results.</para>
<para>To get entries from the <classname>LDAPSearchResults</classname> object,
you can either invoke the <literal>next</literal> method or the <literal>nextElement
</literal> method.</para>
<itemizedlist>
<listitem><para>When you invoke the <literal>next</literal> method, if the
next item in the search results is an entry, the method returns an <classname>LDAPEntry
</classname> object.</para>
<itemizedlist>
<para><indexterm>
<primary>referrals</primary>
<secondary><classname>LDAPReferralException</classname> and</secondary>
</indexterm><indexterm>
<primary>referrals</primary>
<secondary>getting from search results</secondary>
</indexterm>If the next item is a search reference, one of the following can
occur:</para>
<listitem><para>If referrals are not followed automatically, an <classname>LDAPReferralException
</classname> is returned. The exception is also returned if the referral hop
limit is exceeded.</para></listitem>
<listitem><para>The LDAP Java classes follow the referral when two conditions
are fulfilled. Referrals must be followed automatically, the referral hop
limit must not be exceeded. </para></listitem>
</itemizedlist>
<para>The classes also retrieve the entry for you. The method creates a new
connection to the server that is specified in the referral and attempts to
retrieve the entry from that server.</para><para>See <olink
targetptr="handling-referrals">Handling Referrals With Directory SDK for Java</olink> for
more information about referrals and search references.</para><para>If the
next item is an LDAP result code such as <returnvalue>ADMIN_LIMIT_EXCEEDED</returnvalue>, <returnvalue>
TIME_LIMIT_EXCEEDED</returnvalue>, or <returnvalue>SIZE_LIMIT_EXCEEDED</returnvalue>,
the LDAP Java classes return an <classname>LDAPException</classname>.</para>
</listitem>
<listitem><para>When you invoke the <literal>nextElement</literal> method,
the method returns an object that you must cast. The object is an <classname>LDAPEntry
</classname> object, an <classname>LDAPReferralException</classname>, or an <classname>
LDAPException</classname>.</para></listitem>
</itemizedlist>
<para>As you iterate through the search results, you can invoke the <literal>hasMoreElements
</literal> method to determine if you have reached the end of the search results.
</para>
<programlisting>LDAPConnection ld = null;
try {
    /* Create a new LDAPConnection object. */
    ld = new LDAPConnection();

    /* Set up parameters for the search request... */

    /* Send the search request. */
    LDAPSearchResults res = ld.search(baseDN, searchScope,
        searchFilter, getAttrs, false);

    /* Iterate through the results until finished. */
    while (res.hasMoreElements()) {

        /* Get the next entry in the results. */
        LDAPEntry findEntry = null;
        try {
            findEntry = res.next();

        /* If it is a referral, print the LDAP URLs. */
        } catch (LDAPReferralException e) {
            System.out.println("Search references: ");
            LDAPUrl refUrls[] = e.getURLs();
            for (int i=0; i &lt; refUrls.length; i++) {
                System.out.println("\t" + refUrls[i].getUrl());
            }
            continue;
        } catch (LDAPException e) {
            System.out.println("Error: " + e.toString());
            continue;
        }
        /* Do something with the entry... */
    }
} catch (LDAPException e) {
    /* Handle exceptions arising outside the search... */
}</programlisting>
</sect2>
<sect2 id="searching-results-dns"><title>Getting Distinguished Names</title>
<indexterm>
<primary>distinguished names</primary>
<secondary>getting from search results</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>getting distinguished names</secondary>
</indexterm>
<para>To get the distinguished name of an <classname>LDAPEntry</classname> object,
invoke the <literal>getDN</literal> method. This method returns a <classname>String
</classname>.</para>
<programlisting>LDAPEntry nextEntry = res.next();
String nextDN = nextEntry.getDN();</programlisting>
<para>Although the <literal>netscape.ldap</literal> package includes an <classname>
LDAPDN</classname> class, you typically do not construct objects of this class
to represent DNs. The <classname>LDAPDN</classname> class is mainly a utility
class that provides methods for manipulating string DNs.</para></sect2>
<sect2 id="searching-results-attrs"><title>Getting Attributes</title>
<indexterm>
<primary>attributes</primary>
<secondary>getting from search results</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>getting attributes</secondary>
</indexterm>
<para>To get the set of attributes in an <classname>LDAPEntry</classname> object,
invoke the <literal>getAttributeSet</literal> method. This method returns
an <classname>LDAPAttributeSet</classname> object.</para>
<programlisting>LDAPEntry nextEntry = res.next();
LDAPAttributeSet entryAttrs = nextEntry.getAttributeSet();</programlisting>
<para>To get individual attributes from an <classname>LDAPAttributeSet</classname> object,
invoke the <literal>getAttributes</literal> method. This method returns an
enumeration of attributes. You can then iterate through the elements in this
enumeration to retrieve individual <classname>LDAPAttribute</classname> objects.</para>
<programlisting>/* Get the set of attributes for an entry. */
LDAPAttributeSet entryAttrs = nextEntry.getAttributeSet();

/* Get an enumeration of those attribute. */
Enumeration enumAttrs = entryAttrs.getAttributes();

/* Loop through the enumeration to get each attribute. */
while (enumAttrs.hasMoreElements()) {
    LDAPAttribute attr = (LDAPAttribute)enumAttrs.nextElement();
    System.out.println("Attribute type: " + attr.getName());
}</programlisting>
<para>To determine the number of attributes in the <classname>LDAPAttributeSet</classname> object,
invoke the <literal>size</literal> method.</para>
<itemizedlist>
<para>You can also retrieve a specific attribute from the entry or from the
attribute set.</para>
<listitem><para>To get a specific attribute from an <classname>LDAPEntry</classname> object,
invoke the <literal>getAttribute</literal> method.</para></listitem>
<listitem><para>To get a specific attribute from an <classname>LDAPAttributeSet</classname> object,
invoke the <literal>getAttribute</literal> method.</para></listitem>
</itemizedlist>
<para>Both methods return an <classname>LDAPAttribute</classname> object.</para>
<programlisting>LDAPEntry nextEntry = res.next();
LDAPAttribute anAttr = nextEntry.getAttribute("cn");</programlisting>
</sect2>
<sect2 id="searching-results-attr-types-values"><title>Getting Attribute Types
and Values</title>
<indexterm>
<primary>attributes</primary>
<secondary>getting from search results</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>getting attribute types</secondary>
</indexterm><indexterm>
<primary>search results</primary>
<secondary>getting attribute values</secondary>
</indexterm>
<para>To get the name of an <classname>LDAPAttribute</classname> object, invoke
the <literal>getName</literal> method.</para>
<programlisting>LDAPAttribute nextAttr = (LDAPAttribute)enumAttrs.nextElement();
String attrName = nextAttr.getName();</programlisting>
<itemizedlist>
<para>To get the values in an <classname>LDAPAttribute</classname> object,
you can use the following methods:</para>
<listitem><para>To get the <classname>String</classname> values, invoke the <literal>
getStringValues</literal> method.</para></listitem>
<listitem><para>To get the binary values as byte arrays, invoke the <literal>getByteValues
</literal> method.</para></listitem>
</itemizedlist>
<para>Both methods return an enumeration that you can iterate through to retrieve
individual results. For example, if an error occurs when you invoke <literal>getStringValues
</literal>, although the values are binary data, the methods return <constant>null
</constant>.</para>
<para>You can also count the number of values in an attribute by invoking
the <literal>size</literal> method of the <classname>LDAPAttribute</classname> object.
</para>
<programlisting>LDAPAttribute nextAttr = (LDAPAttribute)enumAttrs.nextElement();

/* Get and print the attribute name. */
String attrName = nextAttr.getName();
System.out.println("\t" + attrName + ":");

/* Iterate through the attribute's values. */
Enumeration enumVals = nextAttr.getStringValues();
if (enumVals != null) {
    while (enumVals.hasMoreElements()) {
        String nextValue = (String)enumVals.nextElement();
        System.out.println("\t\t" + nextValue);
    }
}</programlisting>
</sect2>
</sect1>
<sect1 id="searching-sorting"><title>Sorting the Search Results With &DirectorySDKForJava;</title>
<indexterm>
<primary>search results</primary>
<secondary>sorting</secondary>
</indexterm><indexterm>
<primary>sorting search results</primary>
</indexterm>
<itemizedlist>
<para>With &DirectorySDKForJava;, you can sort the search results in two
ways.</para>
<listitem><para>You can specify that the LDAP server should sort the results
before returning the results to your client.</para><para>Send a server-side
sort control to the server as described in <olink targetptr="controls">Chapter&nbsp;10,
LDAP Controls With Directory SDK for Java</olink>. Server-side sorting might
work best if you specify a filter that uses an indexed attribute.</para>
</listitem>
<listitem><para>After you receive the results from the server, you can sort
the results on your client.</para><para>Specify the names of the attributes
that you want to use for sorting. You also need to specify whether or not
the sorting is done in ascending or descending order.</para></listitem>
</itemizedlist>
<para>You can sort the results on the client by invoking the sort method of
the <classname>LDAPSearchResults</classname> object.</para>
<para><indexterm>
<primary><classname>LDAPEntryComparator</classname> interface</primary>
<secondary>example of</secondary>
</indexterm><indexterm>
<primary><classname>LDAPEntryCompareAttrNames</classname> class</primary>
<secondary>example of</secondary>
</indexterm>When invoking this method, you need to pass a comparator object,
which is an object of a class that implements the <classname>LDAPEntryComparator</classname> interface. &DirectorySDKForJava; includes
an <classname>LDAPCompareAttrNames</classname> class that implements this
interface. This class specifies how entries are compared with each other and
sorted.</para>
<para>To construct an <classname>LDAPCompareAttrNames</classname> object,
you need to specify the attributes that you want to use for sorting and, optionally,
the sort order. When sorting on the client side, the attributes used for sorting
must be returned in the search results. If you are returning only a subset
of attributes in the search results, include the attributes that you specify
in the <classname>LDAPCompareAttrNames</classname> constructor. For example,
the following section of code sorts first by surname, <literal>sn</literal>,
and then by common name, <literal>cn</literal>, in ascending order:</para>
<programlisting>LDAPConnection ld = new LDAPConnection();
ld.connect("localhost", LDAPv3.DEFAULT_PORT);
LDAPSearchResults res = ld.search("dc=example,dc=com", LDAPv3.SCOPE_SUB,
    "(objectclass=inetOrgPerson)", null, false);
String[]  sortAttrs = {"sn", "cn"};
boolean[] ascending = {true, true};
res.sort(new LDAPCompareAttrNames(sortAttrs, ascending));</programlisting>
<para>If all search results have not yet been returned, the <literal>sort</literal> method
blocks until all results have been received.</para></sect1>
<sect1 id="searching-abandoning"><title>Abandoning a Search With &DirectorySDKForJava;</title>
<indexterm>
<primary>abandoning a search</primary>
</indexterm><indexterm>
<primary>searching the directory</primary>
<secondary>abandoning a search</secondary>
</indexterm>
<para>At any point during a search operation, you can send a request to the
server to abandon (cancel) the search. To abandon the search, use the <literal>abandon
</literal> method of the <classname>LDAPConnection</classname> object. Pass
in the <classname>LDAPSearchResults</classname> object that was returned to
you when you first invoked the <literal>search</literal> method.</para></sect1>
<sect1 id="searching-example"><title>Searching the Directory With &DirectorySDKForJava;</title>
<indexterm>
<primary>searching the directory</primary>
<secondary>example of</secondary>
</indexterm>
<para>The following example prints the values of all attributes in the entries
returned by a search.</para>
<example id="searching-example-code"><title>Searching for a Specific Entry</title>
<programlisting>import netscape.ldap.*;
import java.util.*;
 
public class Search {
    public static void main(String[] args) {
        try {
            UserArgs userArgs = new UserArgs("Search", args, false);
            LDAPConnection ld = new LDAPConnection();
            ld.connect(userArgs.getHost(), userArgs.getPort());
 
            /* search for all entries with surname of Jensen */
            String MY_FILTER = "sn=Jensen";
            String MY_SEARCHBASE = "dc=example,dc=com";
 
            LDAPSearchConstraints cons = ld.getSearchConstraints();
            /* Setting the batchSize to one will cause the result
               enumeration below to block on one result at a time,
               enabling an update of a list or other things as
               results come in. */
            /* This could be set  to 0 in order to get all
               results and to block until then. */
            cons.setBatchSize(1);
            LDAPSearchResults res = ld.search(MY_SEARCHBASE,
                LDAPConnection.SCOPE_SUB, MY_FILTER, null, false, cons);
 
            /* Loop on results until finished */
            while (res.hasMoreElements()) {
                LDAPEntry findEntry = null;
                try {
                    findEntry = res.next();
                } catch (LDAPReferralException e) {
                    System.out.println("Search reference: ");
                    LDAPUrl refUrls[] = e.getURLs();
                    for (int i=0; i&lt;refUrls.length; i++) {
                        System.out.println("\t" + refUrls[i].getUrl());
                    }
                    continue;
                } catch (LDAPException e) {
                    System.out.println("Error: " + e.toString());
                    continue;
                }
                System.out.println(findEntry.getDN());
 
                /* Get the attributes of the entry */
                LDAPAttributeSet findAttrs = findEntry.getAttributeSet();
                Enumeration enumAttrs = findAttrs.getAttributes();
                System.out.println("\tAttributes: ");
 
                /* Loop on attributes */
                while (enumAttrs.hasMoreElements()) {
                    LDAPAttribute anAttr =
                        (LDAPAttribute)enumAttrs.nextElement();
                    String attrName = anAttr.getName();
                    System.out.println("\t\t" + attrName);
 
                    /* Loop on values for this attribute */
                    Enumeration enumVals = anAttr.getStringValues();
                    if (enumVals != null) {
                        while (enumVals.hasMoreElements()) {
                            String aVal = (String)enumVals.nextElement();
                            System.out.println("\t\t\t" + aVal);
                        }
                    }
                }
            }
            ld.disconnect();
        } catch(LDAPException e) {
            System.out.println("Error: " + e.toString());
        }
    }
}</programlisting>
</example>
</sect1>
<sect1 id="searching-read"><title>Reading an Entry With &DirectorySDKForJava;</title>
<indexterm>
<primary>entries</primary>
<secondary>reading from directory</secondary>
</indexterm><indexterm>
<primary>reading an entry from the directory</primary>
</indexterm>
<para>To get a single entry from the directory, use the <literal>read</literal> method
of the <classname>LDAPConnection</classname> object. You can specify the DN
of the entry with the attributes that you want to retrieve, instead of retrieving
all attributes of the entry. You can also specify an LDAP URL that identifies
the entry that you want to retrieve.</para>
<para>To retrieve data from the entry, you can use the same classes with their
methods, as described in <olink targetptr="searching-results-attrs">Getting
Attributes</olink> and in <olink targetptr="searching-results-attr-types-values">
Getting Attribute Types and Values</olink>.</para>
<para>The following example retrieves an entry and prints the values of its
attributes.</para>
<example id="searching-example-code-read"><title>Retrieving a Specific Entry</title>
<programlisting>import netscape.ldap.*;
import java.util.*;
 
public class RdEntry {
    public static void main(String[] args) {
        try {
            UserArgs userArgs = new UserArgs("PasswordPolicy", args, false);
            LDAPConnection ld = new LDAPConnection();
            ld.connect(userArgs.getHost(), userArgs.getPort());
                         
            String ENTRYDN = "uid=bjensen, ou=People, dc=example,dc=com";
 
            /* Read all attributes */
            LDAPEntry findEntry = ld.read(ENTRYDN);
            System.out.println(findEntry.getDN());
 
            /* Get the attributes of the entry */
            LDAPAttributeSet findAttrs = findEntry.getAttributeSet();
            Enumeration enumAttrs = findAttrs.getAttributes();
            System.out.println("\tAttributes: ");
 
            /* Loop on attributes */
            while (enumAttrs.hasMoreElements()) {
                LDAPAttribute anAttr =
                    (LDAPAttribute)enumAttrs.nextElement();
                String attrName = anAttr.getName();
                System.out.println("\t\t" + attrName);
 
                /* Loop on values for this attribute */
                Enumeration enumVals = anAttr.getStringValues();
                if (enumVals != null) {
                    while (enumVals.hasMoreElements()) {
                        String aVal = (String)enumVals.nextElement();
                        System.out.println("\t\t\t" + aVal);
                    }
                }
            }
            ld.disconnect();
        } catch(LDAPException e) {
            System.out.println("Error: " + e.toString());
        }
    }
}</programlisting>
</example>
</sect1>
<sect1 id="searching-child-entries"><title>Listing Child Entries With &DirectorySDKForJava;</title>
<indexterm>
<primary>entries</primary>
<secondary>retrieving child entries of</secondary>
</indexterm>
<para>To retrieve the entries directly beneath a particular entry, set the
starting point of the search to the entry. Also, set the scope of the search
to <constant>LDAPv3.SCOPE_ONE</constant>.</para>
<figure id="EWAUN"><title>Using Scope of One to Retrieve Child Entries</title>
<mediaobject>
<imageobject><imagedata entityref="hier1lvl" width="100"></imageobject>
<textobject><simpara>Search for child entries</simpara></textobject>
</mediaobject>
</figure>
<para>The following code performs a one-level search:</para>
<programlisting>LDAPConnection ld = null;
try {
    ld = new LDAPConnection();
    ld.connect("localhost", LDAPv3.DEFAULT_PORT);

    LDAPSearchResults res = ld.search("dc=example,dc=com", LDAPv3.SCOPE_ONE,
        "(objectclass=*)", null, false );

    /* Loop on results until finished */
    while (res.hasMoreElements()) {
        LDAPEntry findEntry = null;
        try {
            findEntry = res.next();

        /* If the next result is a referral, print the LDAP URLs. */
        } catch (LDAPReferralException e) {
            System.out.println("Search references: ");
            LDAPUrl refUrls[] = e.getURLs();
            for (int i=0; i &lt; refUrls.length; i++) {
                System.out.println("\t" + refUrls[i].getUrl());
            }
            continue;
        } catch ( LDAPException e ) {
            System.out.println("Error: " + e.toString());
            continue;
        }

        /* Print the DN of the entry. */
        System.out.println(findEntry.getDN());

        /* Get the attributes of the entry */
        LDAPAttributeSet findAttrs = findEntry.getAttributeSet();
        Enumeration enumAttrs = findAttrs.getAttributes();
        System.out.println("\tAttributes: ");
        /* Loop on attributes */
        while (enumAttrs.hasMoreElements()) {
            LDAPAttribute anAttr = (LDAPAttribute)enumAttrs.nextElement();
            String attrName = anAttr.getName();
            System.out.println("\t\t" + attrName);

            /* Loop on values for this attribute */
            Enumeration enumVals = anAttr.getStringValues();
            if (enumVals != null) {
                while (enumVals.hasMoreElements()) {
                    String aVal = (String)enumVals.nextElement();
                    System.out.println("\t\t\t" + aVal);
                }
            }
        }
    }
} catch( LDAPException e ) {
    System.out.println("Error: " + e.toString());
}

/* Done, so disconnect. */
if ((ld != null) &amp;&amp; ld.isConnected()) {
    try {
        ld.disconnect();
    } catch (LDAPException e) {
    System.out.println("Error: " + e.toString());
}</programlisting>
</sect1>
</chapter>
