001     /*
002     * Copyright 2007-2017 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2017 UnboundID Corp.
007     *
008     * This program is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU General Public License (GPLv2 only)
010     * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011     * as published by the Free Software Foundation.
012     *
013     * This program is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program; if not, see <http://www.gnu.org/licenses>.
020     */
021    package com.unboundid.ldap.sdk;
022    
023    
024    
025    import java.io.Closeable;
026    import java.net.InetAddress;
027    import java.net.Socket;
028    import java.util.Collection;
029    import java.util.HashMap;
030    import java.util.List;
031    import java.util.Map;
032    import java.util.Timer;
033    import java.util.concurrent.atomic.AtomicBoolean;
034    import java.util.concurrent.atomic.AtomicLong;
035    import java.util.concurrent.atomic.AtomicReference;
036    import java.util.logging.Level;
037    import javax.net.SocketFactory;
038    import javax.net.ssl.SSLSession;
039    import javax.net.ssl.SSLSocket;
040    import javax.net.ssl.SSLSocketFactory;
041    import javax.security.sasl.SaslClient;
042    
043    import com.unboundid.asn1.ASN1OctetString;
044    import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
045    import com.unboundid.ldap.protocol.LDAPMessage;
046    import com.unboundid.ldap.protocol.LDAPResponse;
047    import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
048    import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
049    import com.unboundid.ldap.sdk.schema.Schema;
050    import com.unboundid.ldif.LDIFException;
051    import com.unboundid.util.DebugType;
052    import com.unboundid.util.SynchronizedSocketFactory;
053    import com.unboundid.util.SynchronizedSSLSocketFactory;
054    import com.unboundid.util.ThreadSafety;
055    import com.unboundid.util.ThreadSafetyLevel;
056    import com.unboundid.util.WeakHashSet;
057    
058    import static com.unboundid.ldap.sdk.LDAPMessages.*;
059    import static com.unboundid.util.Debug.*;
060    import static com.unboundid.util.StaticUtils.*;
061    import static com.unboundid.util.Validator.*;
062    
063    
064    
065    /**
066     * This class provides a facility for interacting with an LDAPv3 directory
067     * server.  It provides a means of establishing a connection to the server,
068     * sending requests, and reading responses.  See
069     * <A HREF="http://www.ietf.org/rfc/rfc4511.txt">RFC 4511</A> for the LDAPv3
070     * protocol specification and more information about the types of operations
071     * defined in LDAP.
072     * <BR><BR>
073     * <H2>Creating, Establishing, and Authenticating Connections</H2>
074     * An LDAP connection can be established either at the time that the object is
075     * created or as a separate step.  Similarly, authentication can be performed on
076     * the connection at the time it is created, at the time it is established, or
077     * as a separate process.  For example:
078     * <BR><BR>
079     * <PRE>
080     *   // Create a new, unestablished connection.  Then connect and perform a
081     *   // simple bind as separate operations.
082     *   LDAPConnection c = new LDAPConnection();
083     *   c.connect(address, port);
084     *   BindResult bindResult = c.bind(bindDN, password);
085     *
086     *   // Create a new connection that is established at creation time, and then
087     *   // authenticate separately using simple authentication.
088     *   LDAPConnection c = new LDAPConnection(address, port);
089     *   BindResult bindResult = c.bind(bindDN, password);
090     *
091     *   // Create a new connection that is established and bound using simple
092     *   // authentication all in one step.
093     *   LDAPConnection c = new LDAPConnection(address, port, bindDN, password);
094     * </PRE>
095     * <BR><BR>
096     * When authentication is performed at the time that the connection is
097     * established, it is only possible to perform a simple bind and it is not
098     * possible to include controls in the bind request, nor is it possible to
099     * receive response controls if the bind was successful.  Therefore, it is
100     * recommended that authentication be performed as a separate step if the server
101     * may return response controls even in the event of a successful authentication
102     * (e.g., a control that may indicate that the user's password will soon
103     * expire).  See the {@link BindRequest} class for more information about
104     * authentication in the UnboundID LDAP SDK for Java.
105     * <BR><BR>
106     * By default, connections will use standard unencrypted network sockets.
107     * However, it may be desirable to create connections that use SSL/TLS to
108     * encrypt communication.  This can be done by specifying a
109     * {@link javax.net.SocketFactory} that should be used to create the socket to
110     * use to communicate with the directory server.  The
111     * {@link javax.net.ssl.SSLSocketFactory#getDefault} method or the
112     * {@link javax.net.ssl.SSLContext#getSocketFactory} method may be used to
113     * obtain a socket factory for performing SSL communication.  See the
114     * <A HREF=
115     * "http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
116     * JSSE Reference Guide</A> for more information on using these classes.
117     * Alternately, you may use the {@link com.unboundid.util.ssl.SSLUtil} class to
118     * simplify the process.
119     * <BR><BR>
120     * Whenever the connection is no longer needed, it may be terminated using the
121     * {@link LDAPConnection#close} method.
122     * <BR><BR>
123     * <H2>Processing LDAP Operations</H2>
124     * This class provides a number of methods for processing the different types of
125     * operations.  The types of operations that can be processed include:
126     * <UL>
127     *   <LI>Abandon -- This may be used to request that the server stop processing
128     *      on an operation that has been invoked asynchronously.</LI>
129     *   <LI>Add -- This may be used to add a new entry to the directory
130     *       server.  See the {@link AddRequest} class for more information about
131     *       processing add operations.</LI>
132     *   <LI>Bind -- This may be used to authenticate to the directory server.  See
133     *       the {@link BindRequest} class for more information about processing
134     *       bind operations.</LI>
135     *   <LI>Compare -- This may be used to determine whether a specified entry has
136     *       a given attribute value.  See the {@link CompareRequest} class for more
137     *       information about processing compare operations.</LI>
138     *   <LI>Delete -- This may be used to remove an entry from the directory
139     *       server.  See the {@link DeleteRequest} class for more information about
140     *       processing delete operations.</LI>
141     *   <LI>Extended -- This may be used to process an operation which is not
142     *       part of the core LDAP protocol but is a custom extension supported by
143     *       the directory server.  See the {@link ExtendedRequest} class for more
144     *       information about processing extended operations.</LI>
145     *   <LI>Modify -- This may be used to alter an entry in the directory
146     *       server.  See the {@link ModifyRequest} class for more information about
147     *       processing modify operations.</LI>
148     *   <LI>Modify DN -- This may be used to rename an entry or subtree and/or move
149     *       that entry or subtree below a new parent in the directory server.  See
150     *       the {@link ModifyDNRequest} class for more information about processing
151     *       modify DN operations.</LI>
152     *   <LI>Search -- This may be used to retrieve a set of entries in the server
153     *       that match a given set of criteria.  See the {@link SearchRequest}
154     *       class for more information about processing search operations.</LI>
155     * </UL>
156     * <BR><BR>
157     * Most of the methods in this class used to process operations operate in a
158     * synchronous manner.  In these cases, the SDK will send a request to the
159     * server and wait for a response to arrive before returning to the caller.  In
160     * these cases, the value returned will include the contents of that response,
161     * including the result code, diagnostic message, matched DN, referral URLs, and
162     * any controls that may have been included.  However, it also possible to
163     * process operations asynchronously, in which case the SDK will return control
164     * back to the caller after the request has been sent to the server but before
165     * the response has been received.  In this case, the SDK will return an
166     * {@link AsyncRequestID} object which may be used to later abandon or cancel
167     * that operation if necessary, and will notify the client when the response
168     * arrives via a listener interface.
169     * <BR><BR>
170     * This class is mostly threadsafe.  It is possible to process multiple
171     * concurrent operations over the same connection as long as the methods being
172     * invoked will not change the state of the connection in a way that might
173     * impact other operations in progress in unexpected ways.  In particular, the
174     * following should not be attempted while any other operations may be in
175     * progress on this connection:
176     * <UL>
177     *   <LI>
178     *     Using one of the {@code connect} methods to re-establish the connection.
179     *   </LI>
180     *   <LI>
181     *     Using one of the {@code close} methods to terminate the connection.
182     *   </LI>
183     *   <LI>
184     *     Using one of the {@code bind} methods to attempt to authenticate the
185     *     connection (unless you are certain that the bind will not impact the
186     *     identity of the associated connection, for example by including the
187     *     retain identity request control in the bind request if using the
188     *     Commercial Edition of the LDAP SDK in conjunction with a Ping Identity,
189     *     UnboundID, or Alcatel-Lucent 8661 Directory Server).
190     *   </LI>
191     *   <LI>
192     *     Attempting to make a change to the way that the underlying communication
193     *     is processed (e.g., by using the StartTLS extended operation to convert
194     *     an insecure connection into a secure one).
195     *   </LI>
196     * </UL>
197     */
198    @ThreadSafety(level=ThreadSafetyLevel.MOSTLY_THREADSAFE)
199    public final class LDAPConnection
200           implements LDAPInterface, ReferralConnector, Closeable
201    {
202      /**
203       * The counter that will be used when assigning connection IDs to connections.
204       */
205      private static final AtomicLong NEXT_CONNECTION_ID = new AtomicLong(0L);
206    
207    
208    
209      /**
210       * The default socket factory that will be used if no alternate factory is
211       * provided.
212       */
213      private static final SocketFactory DEFAULT_SOCKET_FACTORY =
214                                              SocketFactory.getDefault();
215    
216    
217    
218      /**
219       * A set of weak references to schema objects that can be shared across
220       * connections if they are identical.
221       */
222      private static final WeakHashSet<Schema> SCHEMA_SET =
223           new WeakHashSet<Schema>();
224    
225    
226    
227      // The connection pool with which this connection is associated, if
228      // applicable.
229      private AbstractConnectionPool connectionPool;
230    
231      // Indicates whether to perform a reconnect before the next write.
232      private final AtomicBoolean needsReconnect;
233    
234      // The disconnect information for this connection.
235      private final AtomicReference<DisconnectInfo> disconnectInfo;
236    
237      // The last successful bind request processed on this connection.
238      private volatile BindRequest lastBindRequest;
239    
240      // Indicates whether a request has been made to close this connection.
241      private volatile boolean closeRequested;
242    
243      // Indicates whether an unbind request has been sent over this connection.
244      private volatile boolean unbindRequestSent;
245    
246      // The extended request used to initiate StartTLS on this connection.
247      private volatile ExtendedRequest startTLSRequest;
248    
249      // The port of the server to which a connection should be re-established.
250      private int reconnectPort = -1;
251    
252      // The connection internals used to actually perform the network
253      // communication.
254      private volatile LDAPConnectionInternals connectionInternals;
255    
256      // The set of connection options for this connection.
257      private LDAPConnectionOptions connectionOptions;
258    
259      // The set of statistics for this connection.
260      private final LDAPConnectionStatistics connectionStatistics;
261    
262      // The unique identifier assigned to this connection when it was created.  It
263      // will not change over the life of the connection, even if the connection is
264      // closed and re-established (or even re-established to a different server).
265      private final long connectionID;
266    
267      // The time of the last rebind attempt.
268      private long lastReconnectTime;
269    
270      // The most recent time that an LDAP message was sent or received on this
271      // connection.
272      private volatile long lastCommunicationTime;
273    
274      // A map in which arbitrary attachments may be stored or managed.
275      private Map<String,Object> attachments;
276    
277      // The referral connector that will be used to establish connections to remote
278      // servers when following a referral.
279      private volatile ReferralConnector referralConnector;
280    
281      // The cached schema read from the server.
282      private volatile Schema cachedSchema;
283    
284      // The socket factory used for the last connection attempt.
285      private SocketFactory lastUsedSocketFactory;
286    
287      // The socket factory used to create sockets for subsequent connection
288      // attempts.
289      private volatile SocketFactory socketFactory;
290    
291      // A stack trace of the thread that last established this connection.
292      private StackTraceElement[] connectStackTrace;
293    
294      // The user-friendly name assigned to this connection.
295      private String connectionName;
296    
297      // The user-friendly name assigned to the connection pool with which this
298      // connection is associated.
299      private String connectionPoolName;
300    
301      // A string representation of the host and port to which the last connection
302      // attempt (whether successful or not, and whether it is still established)
303      // was made.
304      private String hostPort;
305    
306      // The address of the server to which a connection should be re-established.
307      private String reconnectAddress;
308    
309      // A timer that may be used to enforce timeouts for asynchronous operations.
310      private Timer timer;
311    
312    
313    
314      /**
315       * Creates a new LDAP connection using the default socket factory and default
316       * set of connection options.  No actual network connection will be
317       * established.
318       */
319      public LDAPConnection()
320      {
321        this(null, null);
322      }
323    
324    
325    
326      /**
327       * Creates a new LDAP connection using the default socket factory and provided
328       * set of connection options.  No actual network connection will be
329       * established.
330       *
331       * @param  connectionOptions  The set of connection options to use for this
332       *                            connection.  If it is {@code null}, then a
333       *                            default set of options will be used.
334       */
335      public LDAPConnection(final LDAPConnectionOptions connectionOptions)
336      {
337        this(null, connectionOptions);
338      }
339    
340    
341    
342      /**
343       * Creates a new LDAP connection using the specified socket factory.  No
344       * actual network connection will be established.
345       *
346       * @param  socketFactory  The socket factory to use when establishing
347       *                        connections.  If it is {@code null}, then a default
348       *                        socket factory will be used.
349       */
350      public LDAPConnection(final SocketFactory socketFactory)
351      {
352        this(socketFactory, null);
353      }
354    
355    
356    
357      /**
358       * Creates a new LDAP connection using the specified socket factory.  No
359       * actual network connection will be established.
360       *
361       * @param  socketFactory      The socket factory to use when establishing
362       *                            connections.  If it is {@code null}, then a
363       *                            default socket factory will be used.
364       * @param  connectionOptions  The set of connection options to use for this
365       *                            connection.  If it is {@code null}, then a
366       *                            default set of options will be used.
367       */
368      public LDAPConnection(final SocketFactory socketFactory,
369                            final LDAPConnectionOptions connectionOptions)
370      {
371        needsReconnect = new AtomicBoolean(false);
372        disconnectInfo = new AtomicReference<DisconnectInfo>();
373        lastCommunicationTime = -1L;
374    
375        connectionID = NEXT_CONNECTION_ID.getAndIncrement();
376    
377        if (connectionOptions == null)
378        {
379          this.connectionOptions = new LDAPConnectionOptions();
380        }
381        else
382        {
383          this.connectionOptions = connectionOptions.duplicate();
384        }
385    
386        final SocketFactory f;
387        if (socketFactory == null)
388        {
389          f = DEFAULT_SOCKET_FACTORY;
390        }
391        else
392        {
393          f = socketFactory;
394        }
395    
396        if (this.connectionOptions.allowConcurrentSocketFactoryUse())
397        {
398          this.socketFactory = f;
399        }
400        else
401        {
402          if (f instanceof SSLSocketFactory)
403          {
404            this.socketFactory =
405                 new SynchronizedSSLSocketFactory((SSLSocketFactory) f);
406          }
407          else
408          {
409            this.socketFactory = new SynchronizedSocketFactory(f);
410          }
411        }
412    
413        attachments          = null;
414        connectionStatistics = new LDAPConnectionStatistics();
415        connectionName       = null;
416        connectionPoolName   = null;
417        cachedSchema         = null;
418        timer                = null;
419    
420        referralConnector = this.connectionOptions.getReferralConnector();
421        if (referralConnector == null)
422        {
423          referralConnector = this;
424        }
425      }
426    
427    
428    
429      /**
430       * Creates a new, unauthenticated LDAP connection that is established to the
431       * specified server.
432       *
433       * @param  host  The string representation of the address of the server to
434       *               which the connection should be established.  It may be a
435       *               resolvable name or an IP address.  It must not be
436       *               {@code null}.
437       * @param  port  The port number of the server to which the connection should
438       *               be established.  It should be a value between 1 and 65535,
439       *               inclusive.
440       *
441       * @throws  LDAPException  If a problem occurs while attempting to connect to
442       *                         the specified server.
443       */
444      public LDAPConnection(final String host, final int port)
445             throws LDAPException
446      {
447        this(null, null, host, port);
448      }
449    
450    
451    
452      /**
453       * Creates a new, unauthenticated LDAP connection that is established to the
454       * specified server.
455       *
456       * @param  connectionOptions  The set of connection options to use for this
457       *                            connection.  If it is {@code null}, then a
458       *                            default set of options will be used.
459       * @param  host               The string representation of the address of the
460       *                            server to which the connection should be
461       *                            established.  It may be a resolvable name or an
462       *                            IP address.  It must not be {@code null}.
463       * @param  port               The port number of the server to which the
464       *                            connection should be established.  It should be
465       *                            a value between 1 and 65535, inclusive.
466       *
467       * @throws  LDAPException  If a problem occurs while attempting to connect to
468       *                         the specified server.
469       */
470      public LDAPConnection(final LDAPConnectionOptions connectionOptions,
471                            final String host, final int port)
472             throws LDAPException
473      {
474        this(null, connectionOptions, host, port);
475      }
476    
477    
478    
479      /**
480       * Creates a new, unauthenticated LDAP connection that is established to the
481       * specified server.
482       *
483       * @param  socketFactory  The socket factory to use when establishing
484       *                        connections.  If it is {@code null}, then a default
485       *                        socket factory will be used.
486       * @param  host           The string representation of the address of the
487       *                        server to which the connection should be
488       *                        established.  It may be a resolvable name or an IP
489       *                        address.  It must not be {@code null}.
490       * @param  port           The port number of the server to which the
491       *                        connection should be established.  It should be a
492       *                        value between 1 and 65535, inclusive.
493       *
494       * @throws  LDAPException  If a problem occurs while attempting to connect to
495       *                         the specified server.
496       */
497      public LDAPConnection(final SocketFactory socketFactory, final String host,
498                            final int port)
499             throws LDAPException
500      {
501        this(socketFactory, null, host, port);
502      }
503    
504    
505    
506      /**
507       * Creates a new, unauthenticated LDAP connection that is established to the
508       * specified server.
509       *
510       * @param  socketFactory      The socket factory to use when establishing
511       *                            connections.  If it is {@code null}, then a
512       *                            default socket factory will be used.
513       * @param  connectionOptions  The set of connection options to use for this
514       *                            connection.  If it is {@code null}, then a
515       *                            default set of options will be used.
516       * @param  host               The string representation of the address of the
517       *                            server to which the connection should be
518       *                            established.  It may be a resolvable name or an
519       *                            IP address.  It must not be {@code null}.
520       * @param  port               The port number of the server to which the
521       *                            connection should be established.  It should be
522       *                            a value between 1 and 65535, inclusive.
523       *
524       * @throws  LDAPException  If a problem occurs while attempting to connect to
525       *                         the specified server.
526       */
527      public LDAPConnection(final SocketFactory socketFactory,
528                            final LDAPConnectionOptions connectionOptions,
529                            final String host, final int port)
530             throws LDAPException
531      {
532        this(socketFactory, connectionOptions);
533    
534        connect(host, port);
535      }
536    
537    
538    
539      /**
540       * Creates a new LDAP connection that is established to the specified server
541       * and is authenticated as the specified user (via LDAP simple
542       * authentication).
543       *
544       * @param  host          The string representation of the address of the
545       *                       server to which the connection should be established.
546       *                       It may be a resolvable name or an IP address.  It
547       *                       must not be {@code null}.
548       * @param  port          The port number of the server to which the
549       *                       connection should be established.  It should be a
550       *                       value between 1 and 65535, inclusive.
551       * @param  bindDN        The DN to use to authenticate to the directory
552       *                       server.
553       * @param  bindPassword  The password to use to authenticate to the directory
554       *                       server.
555       *
556       * @throws  LDAPException  If a problem occurs while attempting to connect to
557       *                         the specified server.
558       */
559      public LDAPConnection(final String host, final int port, final String bindDN,
560                            final String bindPassword)
561             throws LDAPException
562      {
563        this(null, null, host, port, bindDN, bindPassword);
564      }
565    
566    
567    
568      /**
569       * Creates a new LDAP connection that is established to the specified server
570       * and is authenticated as the specified user (via LDAP simple
571       * authentication).
572       *
573       * @param  connectionOptions  The set of connection options to use for this
574       *                            connection.  If it is {@code null}, then a
575       *                            default set of options will be used.
576       * @param  host               The string representation of the address of the
577       *                            server to which the connection should be
578       *                            established.  It may be a resolvable name or an
579       *                            IP address.  It must not be {@code null}.
580       * @param  port               The port number of the server to which the
581       *                            connection should be established.  It should be
582       *                            a value between 1 and 65535, inclusive.
583       * @param  bindDN             The DN to use to authenticate to the directory
584       *                            server.
585       * @param  bindPassword       The password to use to authenticate to the
586       *                            directory server.
587       *
588       * @throws  LDAPException  If a problem occurs while attempting to connect to
589       *                         the specified server.
590       */
591      public LDAPConnection(final LDAPConnectionOptions connectionOptions,
592                            final String host, final int port, final String bindDN,
593                            final String bindPassword)
594             throws LDAPException
595      {
596        this(null, connectionOptions, host, port, bindDN, bindPassword);
597      }
598    
599    
600    
601      /**
602       * Creates a new LDAP connection that is established to the specified server
603       * and is authenticated as the specified user (via LDAP simple
604       * authentication).
605       *
606       * @param  socketFactory  The socket factory to use when establishing
607       *                        connections.  If it is {@code null}, then a default
608       *                        socket factory will be used.
609       * @param  host           The string representation of the address of the
610       *                        server to which the connection should be
611       *                        established.  It may be a resolvable name or an IP
612       *                        address.  It must not be {@code null}.
613       * @param  port           The port number of the server to which the
614       *                        connection should be established.  It should be a
615       *                        value between 1 and 65535, inclusive.
616       * @param  bindDN         The DN to use to authenticate to the directory
617       *                        server.
618       * @param  bindPassword   The password to use to authenticate to the directory
619       *                        server.
620       *
621       * @throws  LDAPException  If a problem occurs while attempting to connect to
622       *                         the specified server.
623       */
624      public LDAPConnection(final SocketFactory socketFactory, final String host,
625                            final int port, final String bindDN,
626                            final String bindPassword)
627             throws LDAPException
628      {
629        this(socketFactory, null, host, port, bindDN, bindPassword);
630      }
631    
632    
633    
634      /**
635       * Creates a new LDAP connection that is established to the specified server
636       * and is authenticated as the specified user (via LDAP simple
637       * authentication).
638       *
639       * @param  socketFactory      The socket factory to use when establishing
640       *                            connections.  If it is {@code null}, then a
641       *                            default socket factory will be used.
642       * @param  connectionOptions  The set of connection options to use for this
643       *                            connection.  If it is {@code null}, then a
644       *                            default set of options will be used.
645       * @param  host               The string representation of the address of the
646       *                            server to which the connection should be
647       *                            established.  It may be a resolvable name or an
648       *                            IP address.  It must not be {@code null}.
649       * @param  port               The port number of the server to which the
650       *                            connection should be established.  It should be
651       *                            a value between 1 and 65535, inclusive.
652       * @param  bindDN             The DN to use to authenticate to the directory
653       *                            server.
654       * @param  bindPassword       The password to use to authenticate to the
655       *                            directory server.
656       *
657       * @throws  LDAPException  If a problem occurs while attempting to connect to
658       *                         the specified server.
659       */
660      public LDAPConnection(final SocketFactory socketFactory,
661                            final LDAPConnectionOptions connectionOptions,
662                            final String host, final int port, final String bindDN,
663                            final String bindPassword)
664             throws LDAPException
665      {
666        this(socketFactory, connectionOptions, host, port);
667    
668        try
669        {
670          bind(new SimpleBindRequest(bindDN, bindPassword));
671        }
672        catch (LDAPException le)
673        {
674          debugException(le);
675          setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
676          close();
677          throw le;
678        }
679      }
680    
681    
682    
683      /**
684       * Establishes an unauthenticated connection to the directory server using the
685       * provided information.  If the connection is already established, then it
686       * will be closed and re-established.
687       * <BR><BR>
688       * If this method is invoked while any operations are in progress on this
689       * connection, then the directory server may or may not abort processing for
690       * those operations, depending on the type of operation and how far along the
691       * server has already gotten while processing that operation.  It is
692       * recommended that all active operations be abandoned, canceled, or allowed
693       * to complete before attempting to re-establish an active connection.
694       *
695       * @param  host  The string representation of the address of the server to
696       *               which the connection should be established.  It may be a
697       *               resolvable name or an IP address.  It must not be
698       *               {@code null}.
699       * @param  port  The port number of the server to which the connection should
700       *               be established.  It should be a value between 1 and 65535,
701       *               inclusive.
702       *
703       * @throws  LDAPException  If an error occurs while attempting to establish
704       *                         the connection.
705       */
706      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
707      public void connect(final String host, final int port)
708             throws LDAPException
709      {
710        connect(host, port, connectionOptions.getConnectTimeoutMillis());
711      }
712    
713    
714    
715      /**
716       * Establishes an unauthenticated connection to the directory server using the
717       * provided information.  If the connection is already established, then it
718       * will be closed and re-established.
719       * <BR><BR>
720       * If this method is invoked while any operations are in progress on this
721       * connection, then the directory server may or may not abort processing for
722       * those operations, depending on the type of operation and how far along the
723       * server has already gotten while processing that operation.  It is
724       * recommended that all active operations be abandoned, canceled, or allowed
725       * to complete before attempting to re-establish an active connection.
726       *
727       * @param  host     The string representation of the address of the server to
728       *                  which the connection should be established.  It may be a
729       *                  resolvable name or an IP address.  It must not be
730       *                  {@code null}.
731       * @param  port     The port number of the server to which the connection
732       *                  should be established.  It should be a value between 1 and
733       *                  65535, inclusive.
734       * @param  timeout  The maximum length of time in milliseconds to wait for the
735       *                  connection to be established before failing, or zero to
736       *                  indicate that no timeout should be enforced (although if
737       *                  the attempt stalls long enough, then the underlying
738       *                  operating system may cause it to timeout).
739       *
740       * @throws  LDAPException  If an error occurs while attempting to establish
741       *                         the connection.
742       */
743      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
744      public void connect(final String host, final int port, final int timeout)
745             throws LDAPException
746      {
747        final InetAddress inetAddress;
748        try
749        {
750          inetAddress = InetAddress.getByName(host);
751        }
752        catch (final Exception e)
753        {
754          debugException(e);
755          throw new LDAPException(ResultCode.CONNECT_ERROR,
756               ERR_CONN_RESOLVE_ERROR.get(host, getExceptionMessage(e)),
757               e);
758        }
759    
760        connect(host, inetAddress, port, timeout);
761      }
762    
763    
764    
765      /**
766       * Establishes an unauthenticated connection to the directory server using the
767       * provided information.  If the connection is already established, then it
768       * will be closed and re-established.
769       * <BR><BR>
770       * If this method is invoked while any operations are in progress on this
771       * connection, then the directory server may or may not abort processing for
772       * those operations, depending on the type of operation and how far along the
773       * server has already gotten while processing that operation.  It is
774       * recommended that all active operations be abandoned, canceled, or allowed
775       * to complete before attempting to re-establish an active connection.
776       *
777       * @param  inetAddress  The inet address of the server to which the connection
778       *                      should be established.  It must not be {@code null}.
779       * @param  port         The port number of the server to which the connection
780       *                      should be established.  It should be a value between 1
781       *                      and 65535, inclusive.
782       * @param  timeout      The maximum length of time in milliseconds to wait for
783       *                      the connection to be established before failing, or
784       *                      zero to indicate that no timeout should be enforced
785       *                      (although if the attempt stalls long enough, then the
786       *                      underlying operating system may cause it to timeout).
787       *
788       * @throws  LDAPException  If an error occurs while attempting to establish
789       *                         the connection.
790       */
791      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
792      public void connect(final InetAddress inetAddress, final int port,
793                          final int timeout)
794             throws LDAPException
795      {
796        connect(inetAddress.getHostName(), inetAddress, port, timeout);
797      }
798    
799    
800    
801      /**
802       * Establishes an unauthenticated connection to the directory server using the
803       * provided information.  If the connection is already established, then it
804       * will be closed and re-established.
805       * <BR><BR>
806       * If this method is invoked while any operations are in progress on this
807       * connection, then the directory server may or may not abort processing for
808       * those operations, depending on the type of operation and how far along the
809       * server has already gotten while processing that operation.  It is
810       * recommended that all active operations be abandoned, canceled, or allowed
811       * to complete before attempting to re-establish an active connection.
812       *
813       * @param  host         The string representation of the address of the server
814       *                      to which the connection should be established.  It may
815       *                      be a resolvable name or an IP address.  It must not be
816       *                      {@code null}.
817       * @param  inetAddress  The inet address of the server to which the connection
818       *                      should be established.  It must not be {@code null}.
819       * @param  port         The port number of the server to which the connection
820       *                      should be established.  It should be a value between 1
821       *                      and 65535, inclusive.
822       * @param  timeout      The maximum length of time in milliseconds to wait for
823       *                      the connection to be established before failing, or
824       *                      zero to indicate that no timeout should be enforced
825       *                      (although if the attempt stalls long enough, then the
826       *                      underlying operating system may cause it to timeout).
827       *
828       * @throws  LDAPException  If an error occurs while attempting to establish
829       *                         the connection.
830       */
831      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
832      public void connect(final String host, final InetAddress inetAddress,
833                          final int port, final int timeout)
834             throws LDAPException
835      {
836        ensureNotNull(host, inetAddress, port);
837    
838        needsReconnect.set(false);
839        hostPort = host + ':' + port;
840        lastCommunicationTime = -1L;
841        startTLSRequest = null;
842    
843        if (isConnected())
844        {
845          setDisconnectInfo(DisconnectType.RECONNECT, null, null);
846          close();
847        }
848    
849        lastUsedSocketFactory = socketFactory;
850        reconnectAddress      = host;
851        reconnectPort         = port;
852        cachedSchema          = null;
853        unbindRequestSent     = false;
854    
855        disconnectInfo.set(null);
856    
857        try
858        {
859          connectionStatistics.incrementNumConnects();
860          connectionInternals = new LDAPConnectionInternals(this, connectionOptions,
861               lastUsedSocketFactory, host, inetAddress, port, timeout);
862          connectionInternals.startConnectionReader();
863          lastCommunicationTime = System.currentTimeMillis();
864        }
865        catch (Exception e)
866        {
867          debugException(e);
868          setDisconnectInfo(DisconnectType.LOCAL_ERROR, null, e);
869          connectionInternals = null;
870          throw new LDAPException(ResultCode.CONNECT_ERROR,
871               ERR_CONN_CONNECT_ERROR.get(getHostPort(), String.valueOf(e)), e);
872        }
873    
874        if (connectionOptions.useSchema())
875        {
876          try
877          {
878            cachedSchema = getCachedSchema(this);
879          }
880          catch (Exception e)
881          {
882            debugException(e);
883          }
884        }
885      }
886    
887    
888    
889      /**
890       * Attempts to re-establish a connection to the server and re-authenticate if
891       * appropriate.
892       *
893       * @throws  LDAPException  If a problem occurs while attempting to re-connect
894       *                         or re-authenticate.
895       */
896      public void reconnect()
897             throws LDAPException
898      {
899        needsReconnect.set(false);
900        if ((System.currentTimeMillis() - lastReconnectTime) < 1000L)
901        {
902          // If the last reconnect attempt was less than 1 second ago, then abort.
903          throw new LDAPException(ResultCode.SERVER_DOWN,
904                                  ERR_CONN_MULTIPLE_FAILURES.get());
905        }
906    
907        BindRequest bindRequest = null;
908        if (lastBindRequest != null)
909        {
910          bindRequest = lastBindRequest.getRebindRequest(reconnectAddress,
911                                                         reconnectPort);
912          if (bindRequest == null)
913          {
914            throw new LDAPException(ResultCode.SERVER_DOWN,
915                 ERR_CONN_CANNOT_REAUTHENTICATE.get(getHostPort()));
916          }
917        }
918    
919        final ExtendedRequest startTLSExtendedRequest = startTLSRequest;
920    
921        setDisconnectInfo(DisconnectType.RECONNECT, null, null);
922        terminate(null);
923    
924        try
925        {
926          Thread.sleep(1000L);
927        }
928        catch (final Exception e)
929        {
930          debugException(e);
931    
932          if (e instanceof InterruptedException)
933          {
934            Thread.currentThread().interrupt();
935            throw new LDAPException(ResultCode.LOCAL_ERROR,
936                 ERR_CONN_INTERRUPTED_DURINGR_RECONNECT.get(), e);
937          }
938        }
939    
940        connect(reconnectAddress, reconnectPort);
941    
942        if (startTLSExtendedRequest != null)
943        {
944          try
945          {
946            final ExtendedResult startTLSResult =
947                 processExtendedOperation(startTLSExtendedRequest);
948            if (startTLSResult.getResultCode() != ResultCode.SUCCESS)
949            {
950              throw new LDAPException(startTLSResult);
951            }
952          }
953          catch (final LDAPException le)
954          {
955            debugException(le);
956            setDisconnectInfo(DisconnectType.SECURITY_PROBLEM, null, le);
957            terminate(null);
958    
959            throw le;
960          }
961        }
962    
963        if (bindRequest != null)
964        {
965          try
966          {
967            bind(bindRequest);
968          }
969          catch (final LDAPException le)
970          {
971            debugException(le);
972            setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
973            terminate(null);
974    
975            throw le;
976          }
977        }
978    
979        lastReconnectTime = System.currentTimeMillis();
980      }
981    
982    
983    
984      /**
985       * Sets a flag indicating that the connection should be re-established before
986       * sending the next request.
987       */
988      void setNeedsReconnect()
989      {
990        needsReconnect.set(true);
991      }
992    
993    
994    
995      /**
996       * Indicates whether this connection is currently established.
997       *
998       * @return  {@code true} if this connection is currently established, or
999       *          {@code false} if it is not.
1000       */
1001      public boolean isConnected()
1002      {
1003        final LDAPConnectionInternals internals = connectionInternals;
1004    
1005        if (internals == null)
1006        {
1007          return false;
1008        }
1009    
1010        if (! internals.isConnected())
1011        {
1012          setClosed();
1013          return false;
1014        }
1015    
1016        return (! needsReconnect.get());
1017      }
1018    
1019    
1020    
1021      /**
1022       * Converts this clear-text connection to one that encrypts all communication
1023       * using Transport Layer Security.  This method is intended for use as a
1024       * helper for processing in the course of the StartTLS extended operation and
1025       * should not be used for other purposes.
1026       *
1027       * @param  sslSocketFactory  The SSL socket factory to use to convert an
1028       *                           insecure connection into a secure connection.  It
1029       *                           must not be {@code null}.
1030       *
1031       * @throws  LDAPException  If a problem occurs while converting this
1032       *                         connection to use TLS.
1033       */
1034      void convertToTLS(final SSLSocketFactory sslSocketFactory)
1035           throws LDAPException
1036      {
1037        final LDAPConnectionInternals internals = connectionInternals;
1038        if (internals == null)
1039        {
1040          throw new LDAPException(ResultCode.SERVER_DOWN,
1041                                  ERR_CONN_NOT_ESTABLISHED.get());
1042        }
1043        else
1044        {
1045          internals.convertToTLS(sslSocketFactory);
1046        }
1047      }
1048    
1049    
1050    
1051      /**
1052       * Converts this clear-text connection to one that uses SASL integrity and/or
1053       * confidentiality.
1054       *
1055       * @param  saslClient  The SASL client that will be used to secure the
1056       *                     communication.
1057       *
1058       * @throws  LDAPException  If a problem occurs while attempting to convert the
1059       *                         connection to use SASL QoP.
1060       */
1061      void applySASLQoP(final SaslClient saslClient)
1062           throws LDAPException
1063      {
1064        final LDAPConnectionInternals internals = connectionInternals;
1065        if (internals == null)
1066        {
1067          throw new LDAPException(ResultCode.SERVER_DOWN,
1068               ERR_CONN_NOT_ESTABLISHED.get());
1069        }
1070        else
1071        {
1072          internals.applySASLQoP(saslClient);
1073        }
1074      }
1075    
1076    
1077    
1078      /**
1079       * Retrieves the set of connection options for this connection.  Changes to
1080       * the object that is returned will directly impact this connection.
1081       *
1082       * @return  The set of connection options for this connection.
1083       */
1084      public LDAPConnectionOptions getConnectionOptions()
1085      {
1086        return connectionOptions;
1087      }
1088    
1089    
1090    
1091      /**
1092       * Specifies the set of connection options for this connection.  Some changes
1093       * may not take effect for operations already in progress, and some changes
1094       * may not take effect for a connection that is already established.
1095       *
1096       * @param  connectionOptions  The set of connection options for this
1097       *                            connection.  It may be {@code null} if a default
1098       *                            set of options is to be used.
1099       */
1100      public void setConnectionOptions(
1101                       final LDAPConnectionOptions connectionOptions)
1102      {
1103        if (connectionOptions == null)
1104        {
1105          this.connectionOptions = new LDAPConnectionOptions();
1106        }
1107        else
1108        {
1109          final LDAPConnectionOptions newOptions = connectionOptions.duplicate();
1110          if (debugEnabled(DebugType.LDAP) && newOptions.useSynchronousMode() &&
1111              (! connectionOptions.useSynchronousMode()) && isConnected())
1112          {
1113            debug(Level.WARNING, DebugType.LDAP,
1114                  "A call to LDAPConnection.setConnectionOptions() with " +
1115                  "useSynchronousMode=true will have no effect for this " +
1116                  "connection because it is already established.  The " +
1117                  "useSynchronousMode option must be set before the connection " +
1118                  "is established to have any effect.");
1119          }
1120    
1121          this.connectionOptions = newOptions;
1122        }
1123    
1124        final ReferralConnector rc = this.connectionOptions.getReferralConnector();
1125        if (rc == null)
1126        {
1127          referralConnector = this;
1128        }
1129        else
1130        {
1131          referralConnector = rc;
1132        }
1133      }
1134    
1135    
1136    
1137      /**
1138       * Retrieves the socket factory that was used when creating the socket for the
1139       * last connection attempt (whether successful or unsuccessful) for this LDAP
1140       * connection.
1141       *
1142       * @return  The socket factory that was used when creating the socket for the
1143       *          last connection attempt for this LDAP connection, or {@code null}
1144       *          if no attempt has yet been made to establish this connection.
1145       */
1146      public SocketFactory getLastUsedSocketFactory()
1147      {
1148        return lastUsedSocketFactory;
1149      }
1150    
1151    
1152    
1153      /**
1154       * Retrieves the socket factory to use to create the socket for subsequent
1155       * connection attempts.  This may or may not be the socket factory that was
1156       * used to create the current established connection.
1157       *
1158       * @return  The socket factory to use to create the socket for subsequent
1159       *          connection attempts.
1160       */
1161      public SocketFactory getSocketFactory()
1162      {
1163        return socketFactory;
1164      }
1165    
1166    
1167    
1168      /**
1169       * Specifies the socket factory to use to create the socket for subsequent
1170       * connection attempts.  This will not impact any established connection.
1171       *
1172       * @param  socketFactory  The socket factory to use to create the socket for
1173       *                        subsequent connection attempts.
1174       */
1175      public void setSocketFactory(final SocketFactory socketFactory)
1176      {
1177        if (socketFactory == null)
1178        {
1179          this.socketFactory = DEFAULT_SOCKET_FACTORY;
1180        }
1181        else
1182        {
1183          this.socketFactory = socketFactory;
1184        }
1185      }
1186    
1187    
1188    
1189      /**
1190       * Retrieves the {@code SSLSession} currently being used to secure
1191       * communication on this connection.  This may be available for connections
1192       * that were secured at the time they were created (via an
1193       * {@code SSLSocketFactory}), or for connections secured after their creation
1194       * (via the StartTLS extended operation).  This will not be available for
1195       * unencrypted connections, or connections secured in other ways (e.g., via
1196       * SASL QoP).
1197       *
1198       * @return  The {@code SSLSession} currently being used to secure
1199       *          communication on this connection, or {@code null} if no
1200       *          {@code SSLSession} is available.
1201       */
1202      public SSLSession getSSLSession()
1203      {
1204        final LDAPConnectionInternals internals = connectionInternals;
1205    
1206        if (internals == null)
1207        {
1208          return null;
1209        }
1210    
1211        final Socket socket = internals.getSocket();
1212        if ((socket != null) && (socket instanceof SSLSocket))
1213        {
1214          final SSLSocket sslSocket = (SSLSocket) socket;
1215          return sslSocket.getSession();
1216        }
1217        else
1218        {
1219          return null;
1220        }
1221      }
1222    
1223    
1224    
1225      /**
1226       * Retrieves a value that uniquely identifies this connection within the JVM
1227       * Each {@code LDAPConnection} object will be assigned a different connection
1228       * ID, and that connection ID will not change over the life of the object,
1229       * even if the connection is closed and re-established (whether re-established
1230       * to the same server or a different server).
1231       *
1232       * @return  A value that uniquely identifies this connection within the JVM.
1233       */
1234      public long getConnectionID()
1235      {
1236        return connectionID;
1237      }
1238    
1239    
1240    
1241      /**
1242       * Retrieves the user-friendly name that has been assigned to this connection.
1243       *
1244       * @return  The user-friendly name that has been assigned to this connection,
1245       *          or {@code null} if none has been assigned.
1246       */
1247      public String getConnectionName()
1248      {
1249        return connectionName;
1250      }
1251    
1252    
1253    
1254      /**
1255       * Specifies the user-friendly name that should be used for this connection.
1256       * This name may be used in debugging to help identify the purpose of this
1257       * connection.  This will have no effect for connections which are part of a
1258       * connection pool.
1259       *
1260       * @param  connectionName  The user-friendly name that should be used for this
1261       *                         connection.
1262       */
1263      public void setConnectionName(final String connectionName)
1264      {
1265        if (connectionPool == null)
1266        {
1267          this.connectionName = connectionName;
1268          if (connectionInternals != null)
1269          {
1270            final LDAPConnectionReader reader =
1271                 connectionInternals.getConnectionReader();
1272            reader.updateThreadName();
1273          }
1274        }
1275      }
1276    
1277    
1278    
1279      /**
1280       * Retrieves the connection pool with which this connection is associated, if
1281       * any.
1282       *
1283       * @return  The connection pool with which this connection is associated, or
1284       *          {@code null} if it is not associated with any connection pool.
1285       */
1286      public AbstractConnectionPool getConnectionPool()
1287      {
1288        return connectionPool;
1289      }
1290    
1291    
1292    
1293      /**
1294       * Retrieves the user-friendly name that has been assigned to the connection
1295       * pool with which this connection is associated.
1296       *
1297       * @return  The user-friendly name that has been assigned to the connection
1298       *          pool with which this connection is associated, or {@code null} if
1299       *          none has been assigned or this connection is not associated with a
1300       *          connection pool.
1301       */
1302      public String getConnectionPoolName()
1303      {
1304        return connectionPoolName;
1305      }
1306    
1307    
1308    
1309      /**
1310       * Specifies the user-friendly name that should be used for the connection
1311       * pool with which this connection is associated.
1312       *
1313       * @param  connectionPoolName  The user-friendly name that should be used for
1314       *                             the connection pool with which this connection
1315       *                             is associated.
1316       */
1317      void setConnectionPoolName(final String connectionPoolName)
1318      {
1319        this.connectionPoolName = connectionPoolName;
1320        if (connectionInternals != null)
1321        {
1322          final LDAPConnectionReader reader =
1323               connectionInternals.getConnectionReader();
1324          reader.updateThreadName();
1325        }
1326      }
1327    
1328    
1329    
1330      /**
1331       * Retrieves a string representation of the host and port for the server to
1332       * to which the last connection attempt was made.  It does not matter whether
1333       * the connection attempt was successful, nor does it matter whether it is
1334       * still established.  This is primarily intended for internal use in error
1335       * messages.
1336       *
1337       * @return  A string representation of the host and port for the server to
1338       *          which the last connection attempt was made, or an empty string if
1339       *          no connection attempt has yet been made on this connection.
1340       */
1341      public String getHostPort()
1342      {
1343        if (hostPort == null)
1344        {
1345          return "";
1346        }
1347        else
1348        {
1349          return hostPort;
1350        }
1351      }
1352    
1353    
1354    
1355      /**
1356       * Retrieves the address of the directory server to which this connection is
1357       * currently established.
1358       *
1359       * @return  The address of the directory server to which this connection is
1360       *          currently established, or {@code null} if the connection is not
1361       *          established.
1362       */
1363      public String getConnectedAddress()
1364      {
1365        final LDAPConnectionInternals internals = connectionInternals;
1366        if (internals == null)
1367        {
1368          return null;
1369        }
1370        else
1371        {
1372          return internals.getHost();
1373        }
1374      }
1375    
1376    
1377    
1378      /**
1379       * Retrieves the string representation of the IP address to which this
1380       * connection is currently established.
1381       *
1382       * @return  The string representation of the IP address to which this
1383       *          connection is currently established, or {@code null} if the
1384       *          connection is not established.
1385       */
1386      public String getConnectedIPAddress()
1387      {
1388        final LDAPConnectionInternals internals = connectionInternals;
1389        if (internals == null)
1390        {
1391          return null;
1392        }
1393        else
1394        {
1395          return internals.getInetAddress().getHostAddress();
1396        }
1397      }
1398    
1399    
1400    
1401      /**
1402       * Retrieves an {@code InetAddress} object that represents the address of the
1403       * server to which this  connection is currently established.
1404       *
1405       * @return  An {@code InetAddress} that represents the address of the server
1406       *          to which this connection is currently established, or {@code null}
1407       *          if the connection is not established.
1408       */
1409      public InetAddress getConnectedInetAddress()
1410      {
1411        final LDAPConnectionInternals internals = connectionInternals;
1412        if (internals == null)
1413        {
1414          return null;
1415        }
1416        else
1417        {
1418          return internals.getInetAddress();
1419        }
1420      }
1421    
1422    
1423    
1424      /**
1425       * Retrieves the port of the directory server to which this connection is
1426       * currently established.
1427       *
1428       * @return  The port of the directory server to which this connection is
1429       *          currently established, or -1 if the connection is not established.
1430       */
1431      public int getConnectedPort()
1432      {
1433        final LDAPConnectionInternals internals = connectionInternals;
1434        if (internals == null)
1435        {
1436          return -1;
1437        }
1438        else
1439        {
1440          return internals.getPort();
1441        }
1442      }
1443    
1444    
1445    
1446      /**
1447       * Retrieves a stack trace of the thread that last attempted to establish this
1448       * connection.  Note that this will only be available if an attempt has been
1449       * made to establish this connection and the
1450       * {@link LDAPConnectionOptions#captureConnectStackTrace()} method for the
1451       * associated connection options returns {@code true}.
1452       *
1453       * @return  A stack trace of the thread that last attempted to establish this
1454       *          connection, or {@code null} connect stack traces are not enabled,
1455       *          or if no attempt has been made to establish this connection.
1456       */
1457      public StackTraceElement[] getConnectStackTrace()
1458      {
1459        return connectStackTrace;
1460      }
1461    
1462    
1463    
1464      /**
1465       * Provides a stack trace for the thread that last attempted to establish this
1466       * connection.
1467       *
1468       * @param  connectStackTrace  A stack trace for the thread that last attempted
1469       *                            to establish this connection.
1470       */
1471      void setConnectStackTrace(final StackTraceElement[] connectStackTrace)
1472      {
1473        this.connectStackTrace = connectStackTrace;
1474      }
1475    
1476    
1477    
1478      /**
1479       * Unbinds from the server and closes the connection.
1480       * <BR><BR>
1481       * If this method is invoked while any operations are in progress on this
1482       * connection, then the directory server may or may not abort processing for
1483       * those operations, depending on the type of operation and how far along the
1484       * server has already gotten while processing that operation.  It is
1485       * recommended that all active operations be abandoned, canceled, or allowed
1486       * to complete before attempting to close an active connection.
1487       */
1488      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
1489      public void close()
1490      {
1491        close(NO_CONTROLS);
1492      }
1493    
1494    
1495    
1496      /**
1497       * Unbinds from the server and closes the connection, optionally including
1498       * the provided set of controls in the unbind request.
1499       * <BR><BR>
1500       * If this method is invoked while any operations are in progress on this
1501       * connection, then the directory server may or may not abort processing for
1502       * those operations, depending on the type of operation and how far along the
1503       * server has already gotten while processing that operation.  It is
1504       * recommended that all active operations be abandoned, canceled, or allowed
1505       * to complete before attempting to close an active connection.
1506       *
1507       * @param  controls  The set of controls to include in the unbind request.  It
1508       *                   may be {@code null} if there are not to be any controls
1509       *                   sent in the unbind request.
1510       */
1511      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
1512      public void close(final Control[] controls)
1513      {
1514        closeRequested = true;
1515        setDisconnectInfo(DisconnectType.UNBIND, null, null);
1516    
1517        if (connectionPool == null)
1518        {
1519          terminate(controls);
1520        }
1521        else
1522        {
1523          connectionPool.releaseDefunctConnection(this);
1524        }
1525      }
1526    
1527    
1528    
1529      /**
1530       * Unbinds from the server and closes the connection, optionally including the
1531       * provided set of controls in the unbind request.  This method is only
1532       * intended for internal use, since it does not make any attempt to release
1533       * the connection back to its associated connection pool, if there is one.
1534       *
1535       * @param  controls  The set of controls to include in the unbind request.  It
1536       *                   may be {@code null} if there are not to be any controls
1537       *                   sent in the unbind request.
1538       */
1539      void terminate(final Control[] controls)
1540      {
1541        if (isConnected() && (! unbindRequestSent))
1542        {
1543          try
1544          {
1545            unbindRequestSent = true;
1546            setDisconnectInfo(DisconnectType.UNBIND, null, null);
1547            if (debugEnabled(DebugType.LDAP))
1548            {
1549              debug(Level.INFO, DebugType.LDAP, "Sending LDAP unbind request.");
1550            }
1551    
1552            connectionStatistics.incrementNumUnbindRequests();
1553            sendMessage(new LDAPMessage(nextMessageID(),
1554                 new UnbindRequestProtocolOp(), controls));
1555          }
1556          catch (Exception e)
1557          {
1558            debugException(e);
1559          }
1560        }
1561    
1562        setClosed();
1563      }
1564    
1565    
1566    
1567      /**
1568       * Indicates whether a request has been made to close this connection.
1569       *
1570       * @return  {@code true} if a request has been made to close this connection,
1571       *          or {@code false} if not.
1572       */
1573      boolean closeRequested()
1574      {
1575        return closeRequested;
1576      }
1577    
1578    
1579    
1580      /**
1581       * Indicates whether an unbind request has been sent over this connection.
1582       *
1583       * @return  {@code true} if an unbind request has been sent over this
1584       *          connection, or {@code false} if not.
1585       */
1586      boolean unbindRequestSent()
1587      {
1588        return unbindRequestSent;
1589      }
1590    
1591    
1592    
1593      /**
1594       * Indicates that this LDAP connection is part of the specified
1595       * connection pool.
1596       *
1597       * @param  connectionPool  The connection pool with which this LDAP connection
1598       *                         is associated.
1599       */
1600      void setConnectionPool(final AbstractConnectionPool connectionPool)
1601      {
1602        this.connectionPool = connectionPool;
1603      }
1604    
1605    
1606    
1607      /**
1608       * Retrieves the directory server root DSE, which provides information about
1609       * the directory server, including the capabilities that it provides and the
1610       * type of data that it is configured to handle.
1611       *
1612       * @return  The directory server root DSE, or {@code null} if it is not
1613       *          available.
1614       *
1615       * @throws  LDAPException  If a problem occurs while attempting to retrieve
1616       *                         the server root DSE.
1617       */
1618      public RootDSE getRootDSE()
1619             throws LDAPException
1620      {
1621        return RootDSE.getRootDSE(this);
1622      }
1623    
1624    
1625    
1626      /**
1627       * Retrieves the directory server schema definitions, using the subschema
1628       * subentry DN contained in the server's root DSE.  For directory servers
1629       * containing a single schema, this should be sufficient for all purposes.
1630       * For servers with multiple schemas, it may be necessary to specify the DN
1631       * of the target entry for which to obtain the associated schema.
1632       *
1633       * @return  The directory server schema definitions, or {@code null} if the
1634       *          schema information could not be retrieved (e.g, the client does
1635       *          not have permission to read the server schema).
1636       *
1637       * @throws  LDAPException  If a problem occurs while attempting to retrieve
1638       *                         the server schema.
1639       */
1640      public Schema getSchema()
1641             throws LDAPException
1642      {
1643        return Schema.getSchema(this, "");
1644      }
1645    
1646    
1647    
1648      /**
1649       * Retrieves the directory server schema definitions that govern the specified
1650       * entry.  The subschemaSubentry attribute will be retrieved from the target
1651       * entry, and then the appropriate schema definitions will be loaded from the
1652       * entry referenced by that attribute.  This may be necessary to ensure
1653       * correct behavior in servers that support multiple schemas.
1654       *
1655       * @param  entryDN  The DN of the entry for which to retrieve the associated
1656       *                  schema definitions.  It may be {@code null} or an empty
1657       *                  string if the subschemaSubentry attribute should be
1658       *                  retrieved from the server's root DSE.
1659       *
1660       * @return  The directory server schema definitions, or {@code null} if the
1661       *          schema information could not be retrieved (e.g, the client does
1662       *          not have permission to read the server schema).
1663       *
1664       * @throws  LDAPException  If a problem occurs while attempting to retrieve
1665       *                         the server schema.
1666       */
1667      public Schema getSchema(final String entryDN)
1668             throws LDAPException
1669      {
1670        return Schema.getSchema(this, entryDN);
1671      }
1672    
1673    
1674    
1675      /**
1676       * Retrieves the entry with the specified DN.  All user attributes will be
1677       * requested in the entry to return.
1678       *
1679       * @param  dn  The DN of the entry to retrieve.  It must not be {@code null}.
1680       *
1681       * @return  The requested entry, or {@code null} if the target entry does not
1682       *          exist or no entry was returned (e.g., if the authenticated user
1683       *          does not have permission to read the target entry).
1684       *
1685       * @throws  LDAPException  If a problem occurs while sending the request or
1686       *                         reading the response.
1687       */
1688      public SearchResultEntry getEntry(final String dn)
1689             throws LDAPException
1690      {
1691        return getEntry(dn, (String[]) null);
1692      }
1693    
1694    
1695    
1696      /**
1697       * Retrieves the entry with the specified DN.
1698       *
1699       * @param  dn          The DN of the entry to retrieve.  It must not be
1700       *                     {@code null}.
1701       * @param  attributes  The set of attributes to request for the target entry.
1702       *                     If it is {@code null}, then all user attributes will be
1703       *                     requested.
1704       *
1705       * @return  The requested entry, or {@code null} if the target entry does not
1706       *          exist or no entry was returned (e.g., if the authenticated user
1707       *          does not have permission to read the target entry).
1708       *
1709       * @throws  LDAPException  If a problem occurs while sending the request or
1710       *                         reading the response.
1711       */
1712      public SearchResultEntry getEntry(final String dn, final String... attributes)
1713             throws LDAPException
1714      {
1715        final Filter filter = Filter.createPresenceFilter("objectClass");
1716    
1717        final SearchResult result;
1718        try
1719        {
1720          final SearchRequest searchRequest =
1721               new SearchRequest(dn, SearchScope.BASE, DereferencePolicy.NEVER, 1,
1722                                 0, false, filter, attributes);
1723          result = search(searchRequest);
1724        }
1725        catch (LDAPException le)
1726        {
1727          if (le.getResultCode().equals(ResultCode.NO_SUCH_OBJECT))
1728          {
1729            return null;
1730          }
1731          else
1732          {
1733            throw le;
1734          }
1735        }
1736    
1737        if (! result.getResultCode().equals(ResultCode.SUCCESS))
1738        {
1739          throw new LDAPException(result);
1740        }
1741    
1742        final List<SearchResultEntry> entryList = result.getSearchEntries();
1743        if (entryList.isEmpty())
1744        {
1745          return null;
1746        }
1747        else
1748        {
1749          return entryList.get(0);
1750        }
1751      }
1752    
1753    
1754    
1755      /**
1756       * Processes an abandon request with the provided information.
1757       *
1758       * @param  requestID  The async request ID for the request to abandon.
1759       *
1760       * @throws  LDAPException  If a problem occurs while sending the request to
1761       *                         the server.
1762       */
1763      public void abandon(final AsyncRequestID requestID)
1764             throws LDAPException
1765      {
1766        abandon(requestID, null);
1767      }
1768    
1769    
1770    
1771      /**
1772       * Processes an abandon request with the provided information.
1773       *
1774       * @param  requestID  The async request ID for the request to abandon.
1775       * @param  controls   The set of controls to include in the abandon request.
1776       *                    It may be {@code null} or empty if there are no
1777       *                    controls.
1778       *
1779       * @throws  LDAPException  If a problem occurs while sending the request to
1780       *                         the server.
1781       */
1782      public void abandon(final AsyncRequestID requestID, final Control[] controls)
1783             throws LDAPException
1784      {
1785        if (debugEnabled(DebugType.LDAP))
1786        {
1787          debug(Level.INFO, DebugType.LDAP,
1788                "Sending LDAP abandon request for message ID " + requestID);
1789        }
1790    
1791        if (synchronousMode())
1792        {
1793          throw new LDAPException(ResultCode.NOT_SUPPORTED,
1794               ERR_ABANDON_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
1795        }
1796    
1797        final int messageID = requestID.getMessageID();
1798        try
1799        {
1800          connectionInternals.getConnectionReader().deregisterResponseAcceptor(
1801               messageID);
1802        }
1803        catch (final Exception e)
1804        {
1805          debugException(e);
1806        }
1807    
1808        connectionStatistics.incrementNumAbandonRequests();
1809        sendMessage(new LDAPMessage(nextMessageID(),
1810             new AbandonRequestProtocolOp(messageID), controls));
1811      }
1812    
1813    
1814    
1815      /**
1816       * Sends an abandon request with the provided information.
1817       *
1818       * @param  messageID  The message ID for the request to abandon.
1819       * @param  controls   The set of controls to include in the abandon request.
1820       *                    It may be {@code null} or empty if there are no
1821       *                    controls.
1822       *
1823       * @throws  LDAPException  If a problem occurs while sending the request to
1824       *                         the server.
1825       */
1826      void abandon(final int messageID, final Control... controls)
1827           throws LDAPException
1828      {
1829        if (debugEnabled(DebugType.LDAP))
1830        {
1831          debug(Level.INFO, DebugType.LDAP,
1832                "Sending LDAP abandon request for message ID " + messageID);
1833        }
1834    
1835        try
1836        {
1837          connectionInternals.getConnectionReader().deregisterResponseAcceptor(
1838               messageID);
1839        }
1840        catch (final Exception e)
1841        {
1842          debugException(e);
1843        }
1844    
1845        connectionStatistics.incrementNumAbandonRequests();
1846        sendMessage(new LDAPMessage(nextMessageID(),
1847             new AbandonRequestProtocolOp(messageID), controls));
1848      }
1849    
1850    
1851    
1852      /**
1853       * Processes an add operation with the provided information.
1854       *
1855       * @param  dn          The DN of the entry to add.  It must not be
1856       *                     {@code null}.
1857       * @param  attributes  The set of attributes to include in the entry to add.
1858       *                     It must not be {@code null}.
1859       *
1860       * @return  The result of processing the add operation.
1861       *
1862       * @throws  LDAPException  If the server rejects the add request, or if a
1863       *                         problem is encountered while sending the request or
1864       *                         reading the response.
1865       */
1866      public LDAPResult add(final String dn, final Attribute... attributes)
1867             throws LDAPException
1868      {
1869        ensureNotNull(dn, attributes);
1870    
1871        return add(new AddRequest(dn, attributes));
1872      }
1873    
1874    
1875    
1876      /**
1877       * Processes an add operation with the provided information.
1878       *
1879       * @param  dn          The DN of the entry to add.  It must not be
1880       *                     {@code null}.
1881       * @param  attributes  The set of attributes to include in the entry to add.
1882       *                     It must not be {@code null}.
1883       *
1884       * @return  The result of processing the add operation.
1885       *
1886       * @throws  LDAPException  If the server rejects the add request, or if a
1887       *                         problem is encountered while sending the request or
1888       *                         reading the response.
1889       */
1890      public LDAPResult add(final String dn, final Collection<Attribute> attributes)
1891             throws LDAPException
1892      {
1893        ensureNotNull(dn, attributes);
1894    
1895        return add(new AddRequest(dn, attributes));
1896      }
1897    
1898    
1899    
1900      /**
1901       * Processes an add operation with the provided information.
1902       *
1903       * @param  entry  The entry to add.  It must not be {@code null}.
1904       *
1905       * @return  The result of processing the add operation.
1906       *
1907       * @throws  LDAPException  If the server rejects the add request, or if a
1908       *                         problem is encountered while sending the request or
1909       *                         reading the response.
1910       */
1911      public LDAPResult add(final Entry entry)
1912             throws LDAPException
1913      {
1914        ensureNotNull(entry);
1915    
1916        return add(new AddRequest(entry));
1917      }
1918    
1919    
1920    
1921      /**
1922       * Processes an add operation with the provided information.
1923       *
1924       * @param  ldifLines  The lines that comprise an LDIF representation of the
1925       *                    entry to add.  It must not be empty or {@code null}.
1926       *
1927       * @return  The result of processing the add operation.
1928       *
1929       * @throws  LDIFException  If the provided entry lines cannot be decoded as an
1930       *                         entry in LDIF form.
1931       *
1932       * @throws  LDAPException  If the server rejects the add request, or if a
1933       *                         problem is encountered while sending the request or
1934       *                         reading the response.
1935       */
1936      public LDAPResult add(final String... ldifLines)
1937             throws LDIFException, LDAPException
1938      {
1939        return add(new AddRequest(ldifLines));
1940      }
1941    
1942    
1943    
1944      /**
1945       * Processes the provided add request.
1946       *
1947       * @param  addRequest  The add request to be processed.  It must not be
1948       *                     {@code null}.
1949       *
1950       * @return  The result of processing the add operation.
1951       *
1952       * @throws  LDAPException  If the server rejects the add request, or if a
1953       *                         problem is encountered while sending the request or
1954       *                         reading the response.
1955       */
1956      public LDAPResult add(final AddRequest addRequest)
1957             throws LDAPException
1958      {
1959        ensureNotNull(addRequest);
1960    
1961        final LDAPResult ldapResult = addRequest.process(this, 1);
1962    
1963        switch (ldapResult.getResultCode().intValue())
1964        {
1965          case ResultCode.SUCCESS_INT_VALUE:
1966          case ResultCode.NO_OPERATION_INT_VALUE:
1967            return ldapResult;
1968    
1969          default:
1970            throw new LDAPException(ldapResult);
1971        }
1972      }
1973    
1974    
1975    
1976      /**
1977       * Processes the provided add request.
1978       *
1979       * @param  addRequest  The add request to be processed.  It must not be
1980       *                     {@code null}.
1981       *
1982       * @return  The result of processing the add operation.
1983       *
1984       * @throws  LDAPException  If the server rejects the add request, or if a
1985       *                         problem is encountered while sending the request or
1986       *                         reading the response.
1987       */
1988      public LDAPResult add(final ReadOnlyAddRequest addRequest)
1989             throws LDAPException
1990      {
1991        return add((AddRequest) addRequest);
1992      }
1993    
1994    
1995    
1996      /**
1997       * Processes the provided add request as an asynchronous operation.
1998       *
1999       * @param  addRequest      The add request to be processed.  It must not be
2000       *                         {@code null}.
2001       * @param  resultListener  The async result listener to use to handle the
2002       *                         response for the add operation.  It may be
2003       *                         {@code null} if the result is going to be obtained
2004       *                         from the returned {@code AsyncRequestID} object via
2005       *                         the {@code Future} API.
2006       *
2007       * @return  An async request ID that may be used to reference the operation.
2008       *
2009       * @throws  LDAPException  If a problem occurs while sending the request.
2010       */
2011      public AsyncRequestID asyncAdd(final AddRequest addRequest,
2012                                     final AsyncResultListener resultListener)
2013             throws LDAPException
2014      {
2015        ensureNotNull(addRequest);
2016    
2017        if (synchronousMode())
2018        {
2019          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2020               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2021        }
2022    
2023        final AsyncResultListener listener;
2024        if (resultListener == null)
2025        {
2026          listener = DiscardAsyncListener.getInstance();
2027        }
2028        else
2029        {
2030          listener = resultListener;
2031        }
2032    
2033        return addRequest.processAsync(this, listener);
2034      }
2035    
2036    
2037    
2038      /**
2039       * Processes the provided add request as an asynchronous operation.
2040       *
2041       * @param  addRequest      The add request to be processed.  It must not be
2042       *                         {@code null}.
2043       * @param  resultListener  The async result listener to use to handle the
2044       *                         response for the add operation.  It may be
2045       *                         {@code null} if the result is going to be obtained
2046       *                         from the returned {@code AsyncRequestID} object via
2047       *                         the {@code Future} API.
2048       *
2049       * @return  An async request ID that may be used to reference the operation.
2050       *
2051       * @throws  LDAPException  If a problem occurs while sending the request.
2052       */
2053      public AsyncRequestID asyncAdd(final ReadOnlyAddRequest addRequest,
2054                                     final AsyncResultListener resultListener)
2055             throws LDAPException
2056      {
2057        if (synchronousMode())
2058        {
2059          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2060               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2061        }
2062    
2063        return asyncAdd((AddRequest) addRequest, resultListener);
2064      }
2065    
2066    
2067    
2068      /**
2069       * Processes a simple bind request with the provided DN and password.
2070       * <BR><BR>
2071       * The LDAP protocol specification forbids clients from attempting to perform
2072       * a bind on a connection in which one or more other operations are already in
2073       * progress.  If a bind is attempted while any operations are in progress,
2074       * then the directory server may or may not abort processing for those
2075       * operations, depending on the type of operation and how far along the
2076       * server has already gotten while processing that operation (unless the bind
2077       * request is one that will not cause the server to attempt to change the
2078       * identity of this connection, for example by including the retain identity
2079       * request control in the bind request if using the Commercial Edition of the
2080       * LDAP SDK in conjunction with a Ping Identity, UnboundID, or Alcatel-Lucent
2081       * 8661 Directory Server).  It is recommended that all active operations be
2082       * abandoned, canceled, or allowed to complete before attempting to perform a
2083       * bind on an active connection.
2084       *
2085       * @param  bindDN    The bind DN for the bind operation.
2086       * @param  password  The password for the simple bind operation.
2087       *
2088       * @return  The result of processing the bind operation.
2089       *
2090       * @throws  LDAPException  If the server rejects the bind request, or if a
2091       *                         problem occurs while sending the request or reading
2092       *                         the response.
2093       */
2094      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
2095      public BindResult bind(final String bindDN, final String password)
2096             throws LDAPException
2097      {
2098        return bind(new SimpleBindRequest(bindDN, password));
2099      }
2100    
2101    
2102    
2103      /**
2104       * Processes the provided bind request.
2105       * <BR><BR>
2106       * The LDAP protocol specification forbids clients from attempting to perform
2107       * a bind on a connection in which one or more other operations are already in
2108       * progress.  If a bind is attempted while any operations are in progress,
2109       * then the directory server may or may not abort processing for those
2110       * operations, depending on the type of operation and how far along the
2111       * server has already gotten while processing that operation (unless the bind
2112       * request is one that will not cause the server to attempt to change the
2113       * identity of this connection, for example by including the retain identity
2114       * request control in the bind request if using the Commercial Edition of the
2115       * LDAP SDK in conjunction with a Ping Identity, UnboundID, or Alcatel-Lucent
2116       * 8661 Directory Server).  It is recommended that all active operations be
2117       * abandoned, canceled, or allowed to complete before attempting to perform a
2118       * bind on an active connection.
2119       *
2120       * @param  bindRequest  The bind request to be processed.  It must not be
2121       *                      {@code null}.
2122       *
2123       * @return  The result of processing the bind operation.
2124       *
2125       * @throws  LDAPException  If the server rejects the bind request, or if a
2126       *                         problem occurs while sending the request or reading
2127       *                         the response.
2128       */
2129      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
2130      public BindResult bind(final BindRequest bindRequest)
2131             throws LDAPException
2132      {
2133        ensureNotNull(bindRequest);
2134    
2135        // We don't want to update the last bind request or update the cached
2136        // schema for this connection if it included the retain identity control.
2137        // However, that's only available in the Commercial Edition, so just
2138        // reference it by OID here.
2139        boolean hasRetainIdentityControl = false;
2140        for (final Control c : bindRequest.getControls())
2141        {
2142          if (c.getOID().equals("1.3.6.1.4.1.30221.2.5.3"))
2143          {
2144            hasRetainIdentityControl = true;
2145            break;
2146          }
2147        }
2148    
2149        if (! hasRetainIdentityControl)
2150        {
2151          lastBindRequest = null;
2152        }
2153    
2154        final BindResult bindResult = bindRequest.process(this, 1);
2155        if (bindResult.getResultCode().equals(ResultCode.SUCCESS))
2156        {
2157          if (! hasRetainIdentityControl)
2158          {
2159            lastBindRequest = bindRequest;
2160            if (connectionOptions.useSchema())
2161            {
2162              try
2163              {
2164                cachedSchema = getCachedSchema(this);
2165              }
2166              catch (Exception e)
2167              {
2168                debugException(e);
2169              }
2170            }
2171          }
2172    
2173          return bindResult;
2174        }
2175    
2176        if (bindResult.getResultCode().equals(ResultCode.SASL_BIND_IN_PROGRESS))
2177        {
2178          throw new SASLBindInProgressException(bindResult);
2179        }
2180        else
2181        {
2182          throw new LDAPBindException(bindResult);
2183        }
2184      }
2185    
2186    
2187    
2188      /**
2189       * Processes a compare operation with the provided information.
2190       *
2191       * @param  dn              The DN of the entry in which to make the
2192       *                         comparison.  It must not be {@code null}.
2193       * @param  attributeName   The attribute name for which to make the
2194       *                         comparison.  It must not be {@code null}.
2195       * @param  assertionValue  The assertion value to verify in the target entry.
2196       *                         It must not be {@code null}.
2197       *
2198       * @return  The result of processing the compare operation.
2199       *
2200       * @throws  LDAPException  If the server rejects the compare request, or if a
2201       *                         problem is encountered while sending the request or
2202       *                         reading the response.
2203       */
2204      public CompareResult compare(final String dn, final String attributeName,
2205                                   final String assertionValue)
2206             throws LDAPException
2207      {
2208        ensureNotNull(dn, attributeName, assertionValue);
2209    
2210        return compare(new CompareRequest(dn, attributeName, assertionValue));
2211      }
2212    
2213    
2214    
2215      /**
2216       * Processes the provided compare request.
2217       *
2218       * @param  compareRequest  The compare request to be processed.  It must not
2219       *                         be {@code null}.
2220       *
2221       * @return  The result of processing the compare operation.
2222       *
2223       * @throws  LDAPException  If the server rejects the compare request, or if a
2224       *                         problem is encountered while sending the request or
2225       *                         reading the response.
2226       */
2227      public CompareResult compare(final CompareRequest compareRequest)
2228             throws LDAPException
2229      {
2230        ensureNotNull(compareRequest);
2231    
2232        final LDAPResult result = compareRequest.process(this, 1);
2233        switch (result.getResultCode().intValue())
2234        {
2235          case ResultCode.COMPARE_FALSE_INT_VALUE:
2236          case ResultCode.COMPARE_TRUE_INT_VALUE:
2237            return new CompareResult(result);
2238    
2239          default:
2240            throw new LDAPException(result);
2241        }
2242      }
2243    
2244    
2245    
2246      /**
2247       * Processes the provided compare request.
2248       *
2249       * @param  compareRequest  The compare request to be processed.  It must not
2250       *                         be {@code null}.
2251       *
2252       * @return  The result of processing the compare operation.
2253       *
2254       * @throws  LDAPException  If the server rejects the compare request, or if a
2255       *                         problem is encountered while sending the request or
2256       *                         reading the response.
2257       */
2258      public CompareResult compare(final ReadOnlyCompareRequest compareRequest)
2259             throws LDAPException
2260      {
2261        return compare((CompareRequest) compareRequest);
2262      }
2263    
2264    
2265    
2266      /**
2267       * Processes the provided compare request as an asynchronous operation.
2268       *
2269       * @param  compareRequest  The compare request to be processed.  It must not
2270       *                         be {@code null}.
2271       * @param  resultListener  The async result listener to use to handle the
2272       *                         response for the compare operation.  It may be
2273       *                         {@code null} if the result is going to be obtained
2274       *                         from the returned {@code AsyncRequestID} object via
2275       *                         the {@code Future} API.
2276       *
2277       * @return  An async request ID that may be used to reference the operation.
2278       *
2279       * @throws  LDAPException  If a problem occurs while sending the request.
2280       */
2281      public AsyncRequestID asyncCompare(final CompareRequest compareRequest,
2282                                 final AsyncCompareResultListener resultListener)
2283             throws LDAPException
2284      {
2285        ensureNotNull(compareRequest);
2286    
2287        if (synchronousMode())
2288        {
2289          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2290               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2291        }
2292    
2293        final AsyncCompareResultListener listener;
2294        if (resultListener == null)
2295        {
2296          listener = DiscardAsyncListener.getInstance();
2297        }
2298        else
2299        {
2300          listener = resultListener;
2301        }
2302    
2303        return compareRequest.processAsync(this, listener);
2304      }
2305    
2306    
2307    
2308      /**
2309       * Processes the provided compare request as an asynchronous operation.
2310       *
2311       * @param  compareRequest  The compare request to be processed.  It must not
2312       *                         be {@code null}.
2313       * @param  resultListener  The async result listener to use to handle the
2314       *                         response for the compare operation.  It may be
2315       *                         {@code null} if the result is going to be obtained
2316       *                         from the returned {@code AsyncRequestID} object via
2317       *                         the {@code Future} API.
2318       *
2319       * @return  An async request ID that may be used to reference the operation.
2320       *
2321       * @throws  LDAPException  If a problem occurs while sending the request.
2322       */
2323      public AsyncRequestID asyncCompare(
2324                                 final ReadOnlyCompareRequest compareRequest,
2325                                 final AsyncCompareResultListener resultListener)
2326             throws LDAPException
2327      {
2328        if (synchronousMode())
2329        {
2330          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2331               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2332        }
2333    
2334        return asyncCompare((CompareRequest) compareRequest, resultListener);
2335      }
2336    
2337    
2338    
2339      /**
2340       * Deletes the entry with the specified DN.
2341       *
2342       * @param  dn  The DN of the entry to delete.  It must not be {@code null}.
2343       *
2344       * @return  The result of processing the delete operation.
2345       *
2346       * @throws  LDAPException  If the server rejects the delete request, or if a
2347       *                         problem is encountered while sending the request or
2348       *                         reading the response.
2349       */
2350      public LDAPResult delete(final String dn)
2351             throws LDAPException
2352      {
2353        return delete(new DeleteRequest(dn));
2354      }
2355    
2356    
2357    
2358      /**
2359       * Processes the provided delete request.
2360       *
2361       * @param  deleteRequest  The delete request to be processed.  It must not be
2362       *                        {@code null}.
2363       *
2364       * @return  The result of processing the delete operation.
2365       *
2366       * @throws  LDAPException  If the server rejects the delete request, or if a
2367       *                         problem is encountered while sending the request or
2368       *                         reading the response.
2369       */
2370      public LDAPResult delete(final DeleteRequest deleteRequest)
2371             throws LDAPException
2372      {
2373        ensureNotNull(deleteRequest);
2374    
2375        final LDAPResult ldapResult = deleteRequest.process(this, 1);
2376    
2377        switch (ldapResult.getResultCode().intValue())
2378        {
2379          case ResultCode.SUCCESS_INT_VALUE:
2380          case ResultCode.NO_OPERATION_INT_VALUE:
2381            return ldapResult;
2382    
2383          default:
2384            throw new LDAPException(ldapResult);
2385        }
2386      }
2387    
2388    
2389    
2390      /**
2391       * Processes the provided delete request.
2392       *
2393       * @param  deleteRequest  The delete request to be processed.  It must not be
2394       *                        {@code null}.
2395       *
2396       * @return  The result of processing the delete operation.
2397       *
2398       * @throws  LDAPException  If the server rejects the delete request, or if a
2399       *                         problem is encountered while sending the request or
2400       *                         reading the response.
2401       */
2402      public LDAPResult delete(final ReadOnlyDeleteRequest deleteRequest)
2403             throws LDAPException
2404      {
2405        return delete((DeleteRequest) deleteRequest);
2406      }
2407    
2408    
2409    
2410      /**
2411       * Processes the provided delete request as an asynchronous operation.
2412       *
2413       * @param  deleteRequest   The delete request to be processed.  It must not be
2414       *                         {@code null}.
2415       * @param  resultListener  The async result listener to use to handle the
2416       *                         response for the delete operation.  It may be
2417       *                         {@code null} if the result is going to be obtained
2418       *                         from the returned {@code AsyncRequestID} object via
2419       *                         the {@code Future} API.
2420       *
2421       * @return  An async request ID that may be used to reference the operation.
2422       *
2423       * @throws  LDAPException  If a problem occurs while sending the request.
2424       */
2425      public AsyncRequestID asyncDelete(final DeleteRequest deleteRequest,
2426                                 final AsyncResultListener resultListener)
2427             throws LDAPException
2428      {
2429        ensureNotNull(deleteRequest);
2430    
2431        if (synchronousMode())
2432        {
2433          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2434               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2435        }
2436    
2437        final AsyncResultListener listener;
2438        if (resultListener == null)
2439        {
2440          listener = DiscardAsyncListener.getInstance();
2441        }
2442        else
2443        {
2444          listener = resultListener;
2445        }
2446    
2447        return deleteRequest.processAsync(this, listener);
2448      }
2449    
2450    
2451    
2452      /**
2453       * Processes the provided delete request as an asynchronous operation.
2454       *
2455       * @param  deleteRequest   The delete request to be processed.  It must not be
2456       *                         {@code null}.
2457       * @param  resultListener  The async result listener to use to handle the
2458       *                         response for the delete operation.  It may be
2459       *                         {@code null} if the result is going to be obtained
2460       *                         from the returned {@code AsyncRequestID} object via
2461       *                         the {@code Future} API.
2462       *
2463       * @return  An async request ID that may be used to reference the operation.
2464       *
2465       * @throws  LDAPException  If a problem occurs while sending the request.
2466       */
2467      public AsyncRequestID asyncDelete(final ReadOnlyDeleteRequest deleteRequest,
2468                                 final AsyncResultListener resultListener)
2469             throws LDAPException
2470      {
2471        if (synchronousMode())
2472        {
2473          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2474               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2475        }
2476    
2477        return asyncDelete((DeleteRequest) deleteRequest, resultListener);
2478      }
2479    
2480    
2481    
2482      /**
2483       * Processes an extended request with the provided request OID.  Note that
2484       * because some types of extended operations return unusual result codes under
2485       * "normal" conditions, the server may not always throw an exception for a
2486       * failed extended operation like it does for other types of operations.  It
2487       * will throw an exception under conditions where there appears to be a
2488       * problem with the connection or the server to which the connection is
2489       * established, but there may be many circumstances in which an extended
2490       * operation is not processed correctly but this method does not throw an
2491       * exception.  In the event that no exception is thrown, it is the
2492       * responsibility of the caller to interpret the result to determine whether
2493       * the operation was processed as expected.
2494       * <BR><BR>
2495       * Note that extended operations which may change the state of this connection
2496       * (e.g., the StartTLS extended operation, which will add encryption to a
2497       * previously-unencrypted connection) should not be invoked while any other
2498       * operations are active on the connection.  It is recommended that all active
2499       * operations be abandoned, canceled, or allowed to complete before attempting
2500       * to process an extended operation that may change the state of this
2501       * connection.
2502       *
2503       * @param  requestOID  The OID for the extended request to process.  It must
2504       *                     not be {@code null}.
2505       *
2506       * @return  The extended result object that provides information about the
2507       *          result of the request processing.  It may or may not indicate that
2508       *          the operation was successful.
2509       *
2510       * @throws  LDAPException  If a problem occurs while sending the request or
2511       *                         reading the response.
2512       */
2513      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
2514      public ExtendedResult processExtendedOperation(final String requestOID)
2515             throws LDAPException
2516      {
2517        ensureNotNull(requestOID);
2518    
2519        return processExtendedOperation(new ExtendedRequest(requestOID));
2520      }
2521    
2522    
2523    
2524      /**
2525       * Processes an extended request with the provided request OID and value.
2526       * Note that because some types of extended operations return unusual result
2527       * codes under "normal" conditions, the server may not always throw an
2528       * exception for a failed extended operation like it does for other types of
2529       * operations.  It will throw an exception under conditions where there
2530       * appears to be a problem with the connection or the server to which the
2531       * connection is established, but there may be many circumstances in which an
2532       * extended operation is not processed correctly but this method does not
2533       * throw an exception.  In the event that no exception is thrown, it is the
2534       * responsibility of the caller to interpret the result to determine whether
2535       * the operation was processed as expected.
2536       * <BR><BR>
2537       * Note that extended operations which may change the state of this connection
2538       * (e.g., the StartTLS extended operation, which will add encryption to a
2539       * previously-unencrypted connection) should not be invoked while any other
2540       * operations are active on the connection.  It is recommended that all active
2541       * operations be abandoned, canceled, or allowed to complete before attempting
2542       * to process an extended operation that may change the state of this
2543       * connection.
2544       *
2545       * @param  requestOID    The OID for the extended request to process.  It must
2546       *                       not be {@code null}.
2547       * @param  requestValue  The encoded value for the extended request to
2548       *                       process.  It may be {@code null} if there does not
2549       *                       need to be a value for the requested operation.
2550       *
2551       * @return  The extended result object that provides information about the
2552       *          result of the request processing.  It may or may not indicate that
2553       *          the operation was successful.
2554       *
2555       * @throws  LDAPException  If a problem occurs while sending the request or
2556       *                         reading the response.
2557       */
2558      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
2559      public ExtendedResult processExtendedOperation(final String requestOID,
2560                                 final ASN1OctetString requestValue)
2561             throws LDAPException
2562      {
2563        ensureNotNull(requestOID);
2564    
2565        return processExtendedOperation(new ExtendedRequest(requestOID,
2566                                                            requestValue));
2567      }
2568    
2569    
2570    
2571      /**
2572       * Processes the provided extended request.  Note that because some types of
2573       * extended operations return unusual result codes under "normal" conditions,
2574       * the server may not always throw an exception for a failed extended
2575       * operation like it does for other types of operations.  It will throw an
2576       * exception under conditions where there appears to be a problem with the
2577       * connection or the server to which the connection is established, but there
2578       * may be many circumstances in which an extended operation is not processed
2579       * correctly but this method does not throw an exception.  In the event that
2580       * no exception is thrown, it is the responsibility of the caller to interpret
2581       * the result to determine whether the operation was processed as expected.
2582       * <BR><BR>
2583       * Note that extended operations which may change the state of this connection
2584       * (e.g., the StartTLS extended operation, which will add encryption to a
2585       * previously-unencrypted connection) should not be invoked while any other
2586       * operations are active on the connection.  It is recommended that all active
2587       * operations be abandoned, canceled, or allowed to complete before attempting
2588       * to process an extended operation that may change the state of this
2589       * connection.
2590       *
2591       * @param  extendedRequest  The extended request to be processed.  It must not
2592       *                          be {@code null}.
2593       *
2594       * @return  The extended result object that provides information about the
2595       *          result of the request processing.  It may or may not indicate that
2596       *          the operation was successful.
2597       *
2598       * @throws  LDAPException  If a problem occurs while sending the request or
2599       *                         reading the response.
2600       */
2601      @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE)
2602      public ExtendedResult processExtendedOperation(
2603                                   final ExtendedRequest extendedRequest)
2604             throws LDAPException
2605      {
2606        ensureNotNull(extendedRequest);
2607    
2608        final ExtendedResult extendedResult = extendedRequest.process(this, 1);
2609    
2610        if ((extendedResult.getOID() == null) &&
2611            (extendedResult.getValue() == null))
2612        {
2613          switch (extendedResult.getResultCode().intValue())
2614          {
2615            case ResultCode.OPERATIONS_ERROR_INT_VALUE:
2616            case ResultCode.PROTOCOL_ERROR_INT_VALUE:
2617            case ResultCode.BUSY_INT_VALUE:
2618            case ResultCode.UNAVAILABLE_INT_VALUE:
2619            case ResultCode.OTHER_INT_VALUE:
2620            case ResultCode.SERVER_DOWN_INT_VALUE:
2621            case ResultCode.LOCAL_ERROR_INT_VALUE:
2622            case ResultCode.ENCODING_ERROR_INT_VALUE:
2623            case ResultCode.DECODING_ERROR_INT_VALUE:
2624            case ResultCode.TIMEOUT_INT_VALUE:
2625            case ResultCode.NO_MEMORY_INT_VALUE:
2626            case ResultCode.CONNECT_ERROR_INT_VALUE:
2627              throw new LDAPException(extendedResult);
2628          }
2629        }
2630    
2631        if ((extendedResult.getResultCode() == ResultCode.SUCCESS) &&
2632             extendedRequest.getOID().equals(
2633                  StartTLSExtendedRequest.STARTTLS_REQUEST_OID))
2634        {
2635          startTLSRequest = extendedRequest.duplicate();
2636        }
2637    
2638        return extendedResult;
2639      }
2640    
2641    
2642    
2643      /**
2644       * Applies the provided modification to the specified entry.
2645       *
2646       * @param  dn   The DN of the entry to modify.  It must not be {@code null}.
2647       * @param  mod  The modification to apply to the target entry.  It must not
2648       *              be {@code null}.
2649       *
2650       * @return  The result of processing the modify operation.
2651       *
2652       * @throws  LDAPException  If the server rejects the modify request, or if a
2653       *                         problem is encountered while sending the request or
2654       *                         reading the response.
2655       */
2656      public LDAPResult modify(final String dn, final Modification mod)
2657             throws LDAPException
2658      {
2659        ensureNotNull(dn, mod);
2660    
2661        return modify(new ModifyRequest(dn, mod));
2662      }
2663    
2664    
2665    
2666      /**
2667       * Applies the provided set of modifications to the specified entry.
2668       *
2669       * @param  dn    The DN of the entry to modify.  It must not be {@code null}.
2670       * @param  mods  The set of modifications to apply to the target entry.  It
2671       *               must not be {@code null} or empty.  *
2672       * @return  The result of processing the modify operation.
2673       *
2674       * @throws  LDAPException  If the server rejects the modify request, or if a
2675       *                         problem is encountered while sending the request or
2676       *                         reading the response.
2677       */
2678      public LDAPResult modify(final String dn, final Modification... mods)
2679             throws LDAPException
2680      {
2681        ensureNotNull(dn, mods);
2682    
2683        return modify(new ModifyRequest(dn, mods));
2684      }
2685    
2686    
2687    
2688      /**
2689       * Applies the provided set of modifications to the specified entry.
2690       *
2691       * @param  dn    The DN of the entry to modify.  It must not be {@code null}.
2692       * @param  mods  The set of modifications to apply to the target entry.  It
2693       *               must not be {@code null} or empty.
2694       *
2695       * @return  The result of processing the modify operation.
2696       *
2697       * @throws  LDAPException  If the server rejects the modify request, or if a
2698       *                         problem is encountered while sending the request or
2699       *                         reading the response.
2700       */
2701      public LDAPResult modify(final String dn, final List<Modification> mods)
2702             throws LDAPException
2703      {
2704        ensureNotNull(dn, mods);
2705    
2706        return modify(new ModifyRequest(dn, mods));
2707      }
2708    
2709    
2710    
2711      /**
2712       * Processes a modify request from the provided LDIF representation of the
2713       * changes.
2714       *
2715       * @param  ldifModificationLines  The lines that comprise an LDIF
2716       *                                representation of a modify change record.
2717       *                                It must not be {@code null} or empty.
2718       *
2719       * @return  The result of processing the modify operation.
2720       *
2721       * @throws  LDIFException  If the provided set of lines cannot be parsed as an
2722       *                         LDIF modify change record.
2723       *
2724       * @throws  LDAPException  If the server rejects the modify request, or if a
2725       *                         problem is encountered while sending the request or
2726       *                         reading the response.
2727       *
2728       */
2729      public LDAPResult modify(final String... ldifModificationLines)
2730             throws LDIFException, LDAPException
2731      {
2732        ensureNotNull(ldifModificationLines);
2733    
2734        return modify(new ModifyRequest(ldifModificationLines));
2735      }
2736    
2737    
2738    
2739      /**
2740       * Processes the provided modify request.
2741       *
2742       * @param  modifyRequest  The modify request to be processed.  It must not be
2743       *                        {@code null}.
2744       *
2745       * @return  The result of processing the modify operation.
2746       *
2747       * @throws  LDAPException  If the server rejects the modify request, or if a
2748       *                         problem is encountered while sending the request or
2749       *                         reading the response.
2750       */
2751      public LDAPResult modify(final ModifyRequest modifyRequest)
2752             throws LDAPException
2753      {
2754        ensureNotNull(modifyRequest);
2755    
2756        final LDAPResult ldapResult = modifyRequest.process(this, 1);
2757    
2758        switch (ldapResult.getResultCode().intValue())
2759        {
2760          case ResultCode.SUCCESS_INT_VALUE:
2761          case ResultCode.NO_OPERATION_INT_VALUE:
2762            return ldapResult;
2763    
2764          default:
2765            throw new LDAPException(ldapResult);
2766        }
2767      }
2768    
2769    
2770    
2771      /**
2772       * Processes the provided modify request.
2773       *
2774       * @param  modifyRequest  The modify request to be processed.  It must not be
2775       *                        {@code null}.
2776       *
2777       * @return  The result of processing the modify operation.
2778       *
2779       * @throws  LDAPException  If the server rejects the modify request, or if a
2780       *                         problem is encountered while sending the request or
2781       *                         reading the response.
2782       */
2783      public LDAPResult modify(final ReadOnlyModifyRequest modifyRequest)
2784             throws LDAPException
2785      {
2786        return modify((ModifyRequest) modifyRequest);
2787      }
2788    
2789    
2790    
2791      /**
2792       * Processes the provided modify request as an asynchronous operation.
2793       *
2794       * @param  modifyRequest   The modify request to be processed.  It must not be
2795       *                         {@code null}.
2796       * @param  resultListener  The async result listener to use to handle the
2797       *                         response for the modify operation.  It may be
2798       *                         {@code null} if the result is going to be obtained
2799       *                         from the returned {@code AsyncRequestID} object via
2800       *                         the {@code Future} API.
2801       *
2802       * @return  An async request ID that may be used to reference the operation.
2803       *
2804       * @throws  LDAPException  If a problem occurs while sending the request.
2805       */
2806      public AsyncRequestID asyncModify(final ModifyRequest modifyRequest,
2807                                 final AsyncResultListener resultListener)
2808             throws LDAPException
2809      {
2810        ensureNotNull(modifyRequest);
2811    
2812        if (synchronousMode())
2813        {
2814          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2815               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2816        }
2817    
2818        final AsyncResultListener listener;
2819        if (resultListener == null)
2820        {
2821          listener = DiscardAsyncListener.getInstance();
2822        }
2823        else
2824        {
2825          listener = resultListener;
2826        }
2827    
2828        return modifyRequest.processAsync(this, listener);
2829      }
2830    
2831    
2832    
2833      /**
2834       * Processes the provided modify request as an asynchronous operation.
2835       *
2836       * @param  modifyRequest   The modify request to be processed.  It must not be
2837       *                         {@code null}.
2838       * @param  resultListener  The async result listener to use to handle the
2839       *                         response for the modify operation.  It may be
2840       *                         {@code null} if the result is going to be obtained
2841       *                         from the returned {@code AsyncRequestID} object via
2842       *                         the {@code Future} API.
2843       *
2844       * @return  An async request ID that may be used to reference the operation.
2845       *
2846       * @throws  LDAPException  If a problem occurs while sending the request.
2847       */
2848      public AsyncRequestID asyncModify(final ReadOnlyModifyRequest modifyRequest,
2849                                 final AsyncResultListener resultListener)
2850             throws LDAPException
2851      {
2852        if (synchronousMode())
2853        {
2854          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2855               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2856        }
2857    
2858        return asyncModify((ModifyRequest) modifyRequest, resultListener);
2859      }
2860    
2861    
2862    
2863      /**
2864       * Performs a modify DN operation with the provided information.
2865       *
2866       * @param  dn            The current DN for the entry to rename.  It must not
2867       *                       be {@code null}.
2868       * @param  newRDN        The new RDN to use for the entry.  It must not be
2869       *                       {@code null}.
2870       * @param  deleteOldRDN  Indicates whether to delete the current RDN value
2871       *                       from the entry.
2872       *
2873       * @return  The result of processing the modify DN operation.
2874       *
2875       * @throws  LDAPException  If the server rejects the modify DN request, or if
2876       *                         a problem is encountered while sending the request
2877       *                         or reading the response.
2878       */
2879      public LDAPResult modifyDN(final String dn, final String newRDN,
2880                                 final boolean deleteOldRDN)
2881             throws LDAPException
2882      {
2883        ensureNotNull(dn, newRDN);
2884    
2885        return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
2886      }
2887    
2888    
2889    
2890      /**
2891       * Performs a modify DN operation with the provided information.
2892       *
2893       * @param  dn             The current DN for the entry to rename.  It must not
2894       *                        be {@code null}.
2895       * @param  newRDN         The new RDN to use for the entry.  It must not be
2896       *                        {@code null}.
2897       * @param  deleteOldRDN   Indicates whether to delete the current RDN value
2898       *                        from the entry.
2899       * @param  newSuperiorDN  The new superior DN for the entry.  It may be
2900       *                        {@code null} if the entry is not to be moved below a
2901       *                        new parent.
2902       *
2903       * @return  The result of processing the modify DN operation.
2904       *
2905       * @throws  LDAPException  If the server rejects the modify DN request, or if
2906       *                         a problem is encountered while sending the request
2907       *                         or reading the response.
2908       */
2909      public LDAPResult modifyDN(final String dn, final String newRDN,
2910                                 final boolean deleteOldRDN,
2911                                 final String newSuperiorDN)
2912             throws LDAPException
2913      {
2914        ensureNotNull(dn, newRDN);
2915    
2916        return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
2917                                            newSuperiorDN));
2918      }
2919    
2920    
2921    
2922      /**
2923       * Processes the provided modify DN request.
2924       *
2925       * @param  modifyDNRequest  The modify DN request to be processed.  It must
2926       *                          not be {@code null}.
2927       *
2928       * @return  The result of processing the modify DN operation.
2929       *
2930       * @throws  LDAPException  If the server rejects the modify DN request, or if
2931       *                         a problem is encountered while sending the request
2932       *                         or reading the response.
2933       */
2934      public LDAPResult modifyDN(final ModifyDNRequest modifyDNRequest)
2935             throws LDAPException
2936      {
2937        ensureNotNull(modifyDNRequest);
2938    
2939        final LDAPResult ldapResult = modifyDNRequest.process(this, 1);
2940    
2941        switch (ldapResult.getResultCode().intValue())
2942        {
2943          case ResultCode.SUCCESS_INT_VALUE:
2944          case ResultCode.NO_OPERATION_INT_VALUE:
2945            return ldapResult;
2946    
2947          default:
2948            throw new LDAPException(ldapResult);
2949        }
2950      }
2951    
2952    
2953    
2954      /**
2955       * Processes the provided modify DN request.
2956       *
2957       * @param  modifyDNRequest  The modify DN request to be processed.  It must
2958       *                          not be {@code null}.
2959       *
2960       * @return  The result of processing the modify DN operation.
2961       *
2962       * @throws  LDAPException  If the server rejects the modify DN request, or if
2963       *                         a problem is encountered while sending the request
2964       *                         or reading the response.
2965       */
2966      public LDAPResult modifyDN(final ReadOnlyModifyDNRequest modifyDNRequest)
2967             throws LDAPException
2968      {
2969        return modifyDN((ModifyDNRequest) modifyDNRequest);
2970      }
2971    
2972    
2973    
2974      /**
2975       * Processes the provided modify DN request as an asynchronous operation.
2976       *
2977       * @param  modifyDNRequest  The modify DN request to be processed.  It must
2978       *                          not be {@code null}.
2979       * @param  resultListener  The async result listener to use to handle the
2980       *                         response for the modify DN operation.  It may be
2981       *                         {@code null} if the result is going to be obtained
2982       *                         from the returned {@code AsyncRequestID} object via
2983       *                         the {@code Future} API.
2984       *
2985       * @return  An async request ID that may be used to reference the operation.
2986       *
2987       * @throws  LDAPException  If a problem occurs while sending the request.
2988       */
2989      public AsyncRequestID asyncModifyDN(final ModifyDNRequest modifyDNRequest,
2990                                 final AsyncResultListener resultListener)
2991             throws LDAPException
2992      {
2993        ensureNotNull(modifyDNRequest);
2994    
2995        if (synchronousMode())
2996        {
2997          throw new LDAPException(ResultCode.NOT_SUPPORTED,
2998               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2999        }
3000    
3001        final AsyncResultListener listener;
3002        if (resultListener == null)
3003        {
3004          listener = DiscardAsyncListener.getInstance();
3005        }
3006        else
3007        {
3008          listener = resultListener;
3009        }
3010    
3011        return modifyDNRequest.processAsync(this, listener);
3012      }
3013    
3014    
3015    
3016      /**
3017       * Processes the provided modify DN request as an asynchronous operation.
3018       *
3019       * @param  modifyDNRequest  The modify DN request to be processed.  It must
3020       *                          not be {@code null}.
3021       * @param  resultListener  The async result listener to use to handle the
3022       *                         response for the modify DN operation.  It may be
3023       *                         {@code null} if the result is going to be obtained
3024       *                         from the returned {@code AsyncRequestID} object via
3025       *                         the {@code Future} API.
3026       *
3027       * @return  An async request ID that may be used to reference the operation.
3028       *
3029       * @throws  LDAPException  If a problem occurs while sending the request.
3030       */
3031      public AsyncRequestID asyncModifyDN(
3032                                 final ReadOnlyModifyDNRequest modifyDNRequest,
3033                                 final AsyncResultListener resultListener)
3034             throws LDAPException
3035      {
3036        if (synchronousMode())
3037        {
3038          throw new LDAPException(ResultCode.NOT_SUPPORTED,
3039               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
3040        }
3041    
3042        return asyncModifyDN((ModifyDNRequest) modifyDNRequest, resultListener);
3043      }
3044    
3045    
3046    
3047      /**
3048       * Processes a search operation with the provided information.  The search
3049       * result entries and references will be collected internally and included in
3050       * the {@code SearchResult} object that is returned.
3051       * <BR><BR>
3052       * Note that if the search does not complete successfully, an
3053       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3054       * search result entries or references may have been returned before the
3055       * failure response is received.  In this case, the
3056       * {@code LDAPSearchException} methods like {@code getEntryCount},
3057       * {@code getSearchEntries}, {@code getReferenceCount}, and
3058       * {@code getSearchReferences} may be used to obtain information about those
3059       * entries and references.
3060       *
3061       * @param  baseDN      The base DN for the search request.  It must not be
3062       *                     {@code null}.
3063       * @param  scope       The scope that specifies the range of entries that
3064       *                     should be examined for the search.
3065       * @param  filter      The string representation of the filter to use to
3066       *                     identify matching entries.  It must not be
3067       *                     {@code null}.
3068       * @param  attributes  The set of attributes that should be returned in
3069       *                     matching entries.  It may be {@code null} or empty if
3070       *                     the default attribute set (all user attributes) is to
3071       *                     be requested.
3072       *
3073       * @return  A search result object that provides information about the
3074       *          processing of the search, including the set of matching entries
3075       *          and search references returned by the server.
3076       *
3077       * @throws  LDAPSearchException  If the search does not complete successfully,
3078       *                               or if a problem is encountered while parsing
3079       *                               the provided filter string, sending the
3080       *                               request, or reading the response.  If one
3081       *                               or more entries or references were returned
3082       *                               before the failure was encountered, then the
3083       *                               {@code LDAPSearchException} object may be
3084       *                               examined to obtain information about those
3085       *                               entries and/or references.
3086       */
3087      public SearchResult search(final String baseDN, final SearchScope scope,
3088                                 final String filter, final String... attributes)
3089             throws LDAPSearchException
3090      {
3091        ensureNotNull(baseDN, filter);
3092    
3093        try
3094        {
3095          return search(new SearchRequest(baseDN, scope, filter, attributes));
3096        }
3097        catch (LDAPSearchException lse)
3098        {
3099          debugException(lse);
3100          throw lse;
3101        }
3102        catch (LDAPException le)
3103        {
3104          debugException(le);
3105          throw new LDAPSearchException(le);
3106        }
3107      }
3108    
3109    
3110    
3111      /**
3112       * Processes a search operation with the provided information.  The search
3113       * result entries and references will be collected internally and included in
3114       * the {@code SearchResult} object that is returned.
3115       * <BR><BR>
3116       * Note that if the search does not complete successfully, an
3117       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3118       * search result entries or references may have been returned before the
3119       * failure response is received.  In this case, the
3120       * {@code LDAPSearchException} methods like {@code getEntryCount},
3121       * {@code getSearchEntries}, {@code getReferenceCount}, and
3122       * {@code getSearchReferences} may be used to obtain information about those
3123       * entries and references.
3124       *
3125       * @param  baseDN      The base DN for the search request.  It must not be
3126       *                     {@code null}.
3127       * @param  scope       The scope that specifies the range of entries that
3128       *                     should be examined for the search.
3129       * @param  filter      The filter to use to identify matching entries.  It
3130       *                     must not be {@code null}.
3131       * @param  attributes  The set of attributes that should be returned in
3132       *                     matching entries.  It may be {@code null} or empty if
3133       *                     the default attribute set (all user attributes) is to
3134       *                     be requested.
3135       *
3136       * @return  A search result object that provides information about the
3137       *          processing of the search, including the set of matching entries
3138       *          and search references returned by the server.
3139       *
3140       * @throws  LDAPSearchException  If the search does not complete successfully,
3141       *                               or if a problem is encountered while sending
3142       *                               the request or reading the response.  If one
3143       *                               or more entries or references were returned
3144       *                               before the failure was encountered, then the
3145       *                               {@code LDAPSearchException} object may be
3146       *                               examined to obtain information about those
3147       *                               entries and/or references.
3148       */
3149      public SearchResult search(final String baseDN, final SearchScope scope,
3150                                 final Filter filter, final String... attributes)
3151             throws LDAPSearchException
3152      {
3153        ensureNotNull(baseDN, filter);
3154    
3155        return search(new SearchRequest(baseDN, scope, filter, attributes));
3156      }
3157    
3158    
3159    
3160      /**
3161       * Processes a search operation with the provided information.
3162       * <BR><BR>
3163       * Note that if the search does not complete successfully, an
3164       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3165       * search result entries or references may have been returned before the
3166       * failure response is received.  In this case, the
3167       * {@code LDAPSearchException} methods like {@code getEntryCount},
3168       * {@code getSearchEntries}, {@code getReferenceCount}, and
3169       * {@code getSearchReferences} may be used to obtain information about those
3170       * entries and references (although if a search result listener was provided,
3171       * then it will have been used to make any entries and references available,
3172       * and they will not be available through the {@code getSearchEntries} and
3173       * {@code getSearchReferences} methods).
3174       *
3175       * @param  searchResultListener  The search result listener that should be
3176       *                               used to return results to the client.  It may
3177       *                               be {@code null} if the search results should
3178       *                               be collected internally and returned in the
3179       *                               {@code SearchResult} object.
3180       * @param  baseDN                The base DN for the search request.  It must
3181       *                               not be {@code null}.
3182       * @param  scope                 The scope that specifies the range of entries
3183       *                               that should be examined for the search.
3184       * @param  filter                The string representation of the filter to
3185       *                               use to identify matching entries.  It must
3186       *                               not be {@code null}.
3187       * @param  attributes            The set of attributes that should be returned
3188       *                               in matching entries.  It may be {@code null}
3189       *                               or empty if the default attribute set (all
3190       *                               user attributes) is to be requested.
3191       *
3192       * @return  A search result object that provides information about the
3193       *          processing of the search, potentially including the set of
3194       *          matching entries and search references returned by the server.
3195       *
3196       * @throws  LDAPSearchException  If the search does not complete successfully,
3197       *                               or if a problem is encountered while parsing
3198       *                               the provided filter string, sending the
3199       *                               request, or reading the response.  If one
3200       *                               or more entries or references were returned
3201       *                               before the failure was encountered, then the
3202       *                               {@code LDAPSearchException} object may be
3203       *                               examined to obtain information about those
3204       *                               entries and/or references.
3205       */
3206      public SearchResult search(final SearchResultListener searchResultListener,
3207                                 final String baseDN, final SearchScope scope,
3208                                 final String filter, final String... attributes)
3209             throws LDAPSearchException
3210      {
3211        ensureNotNull(baseDN, filter);
3212    
3213        try
3214        {
3215          return search(new SearchRequest(searchResultListener, baseDN, scope,
3216                                          filter, attributes));
3217        }
3218        catch (LDAPSearchException lse)
3219        {
3220          debugException(lse);
3221          throw lse;
3222        }
3223        catch (LDAPException le)
3224        {
3225          debugException(le);
3226          throw new LDAPSearchException(le);
3227        }
3228      }
3229    
3230    
3231    
3232      /**
3233       * Processes a search operation with the provided information.
3234       * <BR><BR>
3235       * Note that if the search does not complete successfully, an
3236       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3237       * search result entries or references may have been returned before the
3238       * failure response is received.  In this case, the
3239       * {@code LDAPSearchException} methods like {@code getEntryCount},
3240       * {@code getSearchEntries}, {@code getReferenceCount}, and
3241       * {@code getSearchReferences} may be used to obtain information about those
3242       * entries and references (although if a search result listener was provided,
3243       * then it will have been used to make any entries and references available,
3244       * and they will not be available through the {@code getSearchEntries} and
3245       * {@code getSearchReferences} methods).
3246       *
3247       * @param  searchResultListener  The search result listener that should be
3248       *                               used to return results to the client.  It may
3249       *                               be {@code null} if the search results should
3250       *                               be collected internally and returned in the
3251       *                               {@code SearchResult} object.
3252       * @param  baseDN                The base DN for the search request.  It must
3253       *                               not be {@code null}.
3254       * @param  scope                 The scope that specifies the range of entries
3255       *                               that should be examined for the search.
3256       * @param  filter                The filter to use to identify matching
3257       *                               entries.  It must not be {@code null}.
3258       * @param  attributes            The set of attributes that should be returned
3259       *                               in matching entries.  It may be {@code null}
3260       *                               or empty if the default attribute set (all
3261       *                               user attributes) is to be requested.
3262       *
3263       * @return  A search result object that provides information about the
3264       *          processing of the search, potentially including the set of
3265       *          matching entries and search references returned by the server.
3266       *
3267       * @throws  LDAPSearchException  If the search does not complete successfully,
3268       *                               or if a problem is encountered while sending
3269       *                               the request or reading the response.  If one
3270       *                               or more entries or references were returned
3271       *                               before the failure was encountered, then the
3272       *                               {@code LDAPSearchException} object may be
3273       *                               examined to obtain information about those
3274       *                               entries and/or references.
3275       */
3276      public SearchResult search(final SearchResultListener searchResultListener,
3277                                 final String baseDN, final SearchScope scope,
3278                                 final Filter filter, final String... attributes)
3279             throws LDAPSearchException
3280      {
3281        ensureNotNull(baseDN, filter);
3282    
3283        try
3284        {
3285          return search(new SearchRequest(searchResultListener, baseDN, scope,
3286                                          filter, attributes));
3287        }
3288        catch (LDAPSearchException lse)
3289        {
3290          debugException(lse);
3291          throw lse;
3292        }
3293        catch (LDAPException le)
3294        {
3295          debugException(le);
3296          throw new LDAPSearchException(le);
3297        }
3298      }
3299    
3300    
3301    
3302      /**
3303       * Processes a search operation with the provided information.  The search
3304       * result entries and references will be collected internally and included in
3305       * the {@code SearchResult} object that is returned.
3306       * <BR><BR>
3307       * Note that if the search does not complete successfully, an
3308       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3309       * search result entries or references may have been returned before the
3310       * failure response is received.  In this case, the
3311       * {@code LDAPSearchException} methods like {@code getEntryCount},
3312       * {@code getSearchEntries}, {@code getReferenceCount}, and
3313       * {@code getSearchReferences} may be used to obtain information about those
3314       * entries and references.
3315       *
3316       * @param  baseDN       The base DN for the search request.  It must not be
3317       *                      {@code null}.
3318       * @param  scope        The scope that specifies the range of entries that
3319       *                      should be examined for the search.
3320       * @param  derefPolicy  The dereference policy the server should use for any
3321       *                      aliases encountered while processing the search.
3322       * @param  sizeLimit    The maximum number of entries that the server should
3323       *                      return for the search.  A value of zero indicates that
3324       *                      there should be no limit.
3325       * @param  timeLimit    The maximum length of time in seconds that the server
3326       *                      should spend processing this search request.  A value
3327       *                      of zero indicates that there should be no limit.
3328       * @param  typesOnly    Indicates whether to return only attribute names in
3329       *                      matching entries, or both attribute names and values.
3330       * @param  filter       The string representation of the filter to use to
3331       *                      identify matching entries.  It must not be
3332       *                      {@code null}.
3333       * @param  attributes   The set of attributes that should be returned in
3334       *                      matching entries.  It may be {@code null} or empty if
3335       *                      the default attribute set (all user attributes) is to
3336       *                      be requested.
3337       *
3338       * @return  A search result object that provides information about the
3339       *          processing of the search, including the set of matching entries
3340       *          and search references returned by the server.
3341       *
3342       * @throws  LDAPSearchException  If the search does not complete successfully,
3343       *                               or if a problem is encountered while parsing
3344       *                               the provided filter string, sending the
3345       *                               request, or reading the response.  If one
3346       *                               or more entries or references were returned
3347       *                               before the failure was encountered, then the
3348       *                               {@code LDAPSearchException} object may be
3349       *                               examined to obtain information about those
3350       *                               entries and/or references.
3351       */
3352      public SearchResult search(final String baseDN, final SearchScope scope,
3353                                 final DereferencePolicy derefPolicy,
3354                                 final int sizeLimit, final int timeLimit,
3355                                 final boolean typesOnly, final String filter,
3356                                 final String... attributes)
3357             throws LDAPSearchException
3358      {
3359        ensureNotNull(baseDN, filter);
3360    
3361        try
3362        {
3363          return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
3364                                          timeLimit, typesOnly, filter,
3365                                          attributes));
3366        }
3367        catch (LDAPSearchException lse)
3368        {
3369          debugException(lse);
3370          throw lse;
3371        }
3372        catch (LDAPException le)
3373        {
3374          debugException(le);
3375          throw new LDAPSearchException(le);
3376        }
3377      }
3378    
3379    
3380    
3381      /**
3382       * Processes a search operation with the provided information.  The search
3383       * result entries and references will be collected internally and included in
3384       * the {@code SearchResult} object that is returned.
3385       * <BR><BR>
3386       * Note that if the search does not complete successfully, an
3387       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3388       * search result entries or references may have been returned before the
3389       * failure response is received.  In this case, the
3390       * {@code LDAPSearchException} methods like {@code getEntryCount},
3391       * {@code getSearchEntries}, {@code getReferenceCount}, and
3392       * {@code getSearchReferences} may be used to obtain information about those
3393       * entries and references.
3394       *
3395       * @param  baseDN       The base DN for the search request.  It must not be
3396       *                      {@code null}.
3397       * @param  scope        The scope that specifies the range of entries that
3398       *                      should be examined for the search.
3399       * @param  derefPolicy  The dereference policy the server should use for any
3400       *                      aliases encountered while processing the search.
3401       * @param  sizeLimit    The maximum number of entries that the server should
3402       *                      return for the search.  A value of zero indicates that
3403       *                      there should be no limit.
3404       * @param  timeLimit    The maximum length of time in seconds that the server
3405       *                      should spend processing this search request.  A value
3406       *                      of zero indicates that there should be no limit.
3407       * @param  typesOnly    Indicates whether to return only attribute names in
3408       *                      matching entries, or both attribute names and values.
3409       * @param  filter       The filter to use to identify matching entries.  It
3410       *                      must not be {@code null}.
3411       * @param  attributes   The set of attributes that should be returned in
3412       *                      matching entries.  It may be {@code null} or empty if
3413       *                      the default attribute set (all user attributes) is to
3414       *                      be requested.
3415       *
3416       * @return  A search result object that provides information about the
3417       *          processing of the search, including the set of matching entries
3418       *          and search references returned by the server.
3419       *
3420       * @throws  LDAPSearchException  If the search does not complete successfully,
3421       *                               or if a problem is encountered while sending
3422       *                               the request or reading the response.  If one
3423       *                               or more entries or references were returned
3424       *                               before the failure was encountered, then the
3425       *                               {@code LDAPSearchException} object may be
3426       *                               examined to obtain information about those
3427       *                               entries and/or references.
3428       */
3429      public SearchResult search(final String baseDN, final SearchScope scope,
3430                                 final DereferencePolicy derefPolicy,
3431                                 final int sizeLimit, final int timeLimit,
3432                                 final boolean typesOnly, final Filter filter,
3433                                 final String... attributes)
3434             throws LDAPSearchException
3435      {
3436        ensureNotNull(baseDN, filter);
3437    
3438        return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
3439                                        timeLimit, typesOnly, filter, attributes));
3440      }
3441    
3442    
3443    
3444      /**
3445       * Processes a search operation with the provided information.
3446       * <BR><BR>
3447       * Note that if the search does not complete successfully, an
3448       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3449       * search result entries or references may have been returned before the
3450       * failure response is received.  In this case, the
3451       * {@code LDAPSearchException} methods like {@code getEntryCount},
3452       * {@code getSearchEntries}, {@code getReferenceCount}, and
3453       * {@code getSearchReferences} may be used to obtain information about those
3454       * entries and references (although if a search result listener was provided,
3455       * then it will have been used to make any entries and references available,
3456       * and they will not be available through the {@code getSearchEntries} and
3457       * {@code getSearchReferences} methods).
3458       *
3459       * @param  searchResultListener  The search result listener that should be
3460       *                               used to return results to the client.  It may
3461       *                               be {@code null} if the search results should
3462       *                               be collected internally and returned in the
3463       *                               {@code SearchResult} object.
3464       * @param  baseDN                The base DN for the search request.  It must
3465       *                               not be {@code null}.
3466       * @param  scope                 The scope that specifies the range of entries
3467       *                               that should be examined for the search.
3468       * @param  derefPolicy           The dereference policy the server should use
3469       *                               for any aliases encountered while processing
3470       *                               the search.
3471       * @param  sizeLimit             The maximum number of entries that the server
3472       *                               should return for the search.  A value of
3473       *                               zero indicates that there should be no limit.
3474       * @param  timeLimit             The maximum length of time in seconds that
3475       *                               the server should spend processing this
3476       *                               search request.  A value of zero indicates
3477       *                               that there should be no limit.
3478       * @param  typesOnly             Indicates whether to return only attribute
3479       *                               names in matching entries, or both attribute
3480       *                               names and values.
3481       * @param  filter                The string representation of the filter to
3482       *                               use to identify matching entries.  It must
3483       *                               not be {@code null}.
3484       * @param  attributes            The set of attributes that should be returned
3485       *                               in matching entries.  It may be {@code null}
3486       *                               or empty if the default attribute set (all
3487       *                               user attributes) is to be requested.
3488       *
3489       * @return  A search result object that provides information about the
3490       *          processing of the search, potentially including the set of
3491       *          matching entries and search references returned by the server.
3492       *
3493       * @throws  LDAPSearchException  If the search does not complete successfully,
3494       *                               or if a problem is encountered while parsing
3495       *                               the provided filter string, sending the
3496       *                               request, or reading the response.  If one
3497       *                               or more entries or references were returned
3498       *                               before the failure was encountered, then the
3499       *                               {@code LDAPSearchException} object may be
3500       *                               examined to obtain information about those
3501       *                               entries and/or references.
3502       */
3503      public SearchResult search(final SearchResultListener searchResultListener,
3504                                 final String baseDN, final SearchScope scope,
3505                                 final DereferencePolicy derefPolicy,
3506                                 final int sizeLimit, final int timeLimit,
3507                                 final boolean typesOnly, final String filter,
3508                                 final String... attributes)
3509             throws LDAPSearchException
3510      {
3511        ensureNotNull(baseDN, filter);
3512    
3513        try
3514        {
3515          return search(new SearchRequest(searchResultListener, baseDN, scope,
3516                                          derefPolicy, sizeLimit, timeLimit,
3517                                          typesOnly, filter, attributes));
3518        }
3519        catch (LDAPSearchException lse)
3520        {
3521          debugException(lse);
3522          throw lse;
3523        }
3524        catch (LDAPException le)
3525        {
3526          debugException(le);
3527          throw new LDAPSearchException(le);
3528        }
3529      }
3530    
3531    
3532    
3533      /**
3534       * Processes a search operation with the provided information.
3535       * <BR><BR>
3536       * Note that if the search does not complete successfully, an
3537       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3538       * search result entries or references may have been returned before the
3539       * failure response is received.  In this case, the
3540       * {@code LDAPSearchException} methods like {@code getEntryCount},
3541       * {@code getSearchEntries}, {@code getReferenceCount}, and
3542       * {@code getSearchReferences} may be used to obtain information about those
3543       * entries and references (although if a search result listener was provided,
3544       * then it will have been used to make any entries and references available,
3545       * and they will not be available through the {@code getSearchEntries} and
3546       * {@code getSearchReferences} methods).
3547       *
3548       * @param  searchResultListener  The search result listener that should be
3549       *                               used to return results to the client.  It may
3550       *                               be {@code null} if the search results should
3551       *                               be collected internally and returned in the
3552       *                               {@code SearchResult} object.
3553       * @param  baseDN                The base DN for the search request.  It must
3554       *                               not be {@code null}.
3555       * @param  scope                 The scope that specifies the range of entries
3556       *                               that should be examined for the search.
3557       * @param  derefPolicy           The dereference policy the server should use
3558       *                               for any aliases encountered while processing
3559       *                               the search.
3560       * @param  sizeLimit             The maximum number of entries that the server
3561       *                               should return for the search.  A value of
3562       *                               zero indicates that there should be no limit.
3563       * @param  timeLimit             The maximum length of time in seconds that
3564       *                               the server should spend processing this
3565       *                               search request.  A value of zero indicates
3566       *                               that there should be no limit.
3567       * @param  typesOnly             Indicates whether to return only attribute
3568       *                               names in matching entries, or both attribute
3569       *                               names and values.
3570       * @param  filter                The filter to use to identify matching
3571       *                               entries.  It must not be {@code null}.
3572       * @param  attributes            The set of attributes that should be returned
3573       *                               in matching entries.  It may be {@code null}
3574       *                               or empty if the default attribute set (all
3575       *                               user attributes) is to be requested.
3576       *
3577       * @return  A search result object that provides information about the
3578       *          processing of the search, potentially including the set of
3579       *          matching entries and search references returned by the server.
3580       *
3581       * @throws  LDAPSearchException  If the search does not complete successfully,
3582       *                               or if a problem is encountered while sending
3583       *                               the request or reading the response.  If one
3584       *                               or more entries or references were returned
3585       *                               before the failure was encountered, then the
3586       *                               {@code LDAPSearchException} object may be
3587       *                               examined to obtain information about those
3588       *                               entries and/or references.
3589       */
3590      public SearchResult search(final SearchResultListener searchResultListener,
3591                                 final String baseDN, final SearchScope scope,
3592                                 final DereferencePolicy derefPolicy,
3593                                 final int sizeLimit, final int timeLimit,
3594                                 final boolean typesOnly, final Filter filter,
3595                                 final String... attributes)
3596             throws LDAPSearchException
3597      {
3598        ensureNotNull(baseDN, filter);
3599    
3600        return search(new SearchRequest(searchResultListener, baseDN, scope,
3601                                        derefPolicy, sizeLimit, timeLimit,
3602                                        typesOnly, filter, attributes));
3603      }
3604    
3605    
3606    
3607      /**
3608       * Processes the provided search request.
3609       * <BR><BR>
3610       * Note that if the search does not complete successfully, an
3611       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3612       * search result entries or references may have been returned before the
3613       * failure response is received.  In this case, the
3614       * {@code LDAPSearchException} methods like {@code getEntryCount},
3615       * {@code getSearchEntries}, {@code getReferenceCount}, and
3616       * {@code getSearchReferences} may be used to obtain information about those
3617       * entries and references (although if a search result listener was provided,
3618       * then it will have been used to make any entries and references available,
3619       * and they will not be available through the {@code getSearchEntries} and
3620       * {@code getSearchReferences} methods).
3621       *
3622       * @param  searchRequest  The search request to be processed.  It must not be
3623       *                        {@code null}.
3624       *
3625       * @return  A search result object that provides information about the
3626       *          processing of the search, potentially including the set of
3627       *          matching entries and search references returned by the server.
3628       *
3629       * @throws  LDAPSearchException  If the search does not complete successfully,
3630       *                               or if a problem is encountered while sending
3631       *                               the request or reading the response.  If one
3632       *                               or more entries or references were returned
3633       *                               before the failure was encountered, then the
3634       *                               {@code LDAPSearchException} object may be
3635       *                               examined to obtain information about those
3636       *                               entries and/or references.
3637       */
3638      public SearchResult search(final SearchRequest searchRequest)
3639             throws LDAPSearchException
3640      {
3641        ensureNotNull(searchRequest);
3642    
3643        final SearchResult searchResult;
3644        try
3645        {
3646          searchResult = searchRequest.process(this, 1);
3647        }
3648        catch (LDAPSearchException lse)
3649        {
3650          debugException(lse);
3651          throw lse;
3652        }
3653        catch (LDAPException le)
3654        {
3655          debugException(le);
3656          throw new LDAPSearchException(le);
3657        }
3658    
3659        if (! searchResult.getResultCode().equals(ResultCode.SUCCESS))
3660        {
3661          throw new LDAPSearchException(searchResult);
3662        }
3663    
3664        return searchResult;
3665      }
3666    
3667    
3668    
3669      /**
3670       * Processes the provided search request.
3671       * <BR><BR>
3672       * Note that if the search does not complete successfully, an
3673       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3674       * search result entries or references may have been returned before the
3675       * failure response is received.  In this case, the
3676       * {@code LDAPSearchException} methods like {@code getEntryCount},
3677       * {@code getSearchEntries}, {@code getReferenceCount}, and
3678       * {@code getSearchReferences} may be used to obtain information about those
3679       * entries and references (although if a search result listener was provided,
3680       * then it will have been used to make any entries and references available,
3681       * and they will not be available through the {@code getSearchEntries} and
3682       * {@code getSearchReferences} methods).
3683       *
3684       * @param  searchRequest  The search request to be processed.  It must not be
3685       *                        {@code null}.
3686       *
3687       * @return  A search result object that provides information about the
3688       *          processing of the search, potentially including the set of
3689       *          matching entries and search references returned by the server.
3690       *
3691       * @throws  LDAPSearchException  If the search does not complete successfully,
3692       *                               or if a problem is encountered while sending
3693       *                               the request or reading the response.  If one
3694       *                               or more entries or references were returned
3695       *                               before the failure was encountered, then the
3696       *                               {@code LDAPSearchException} object may be
3697       *                               examined to obtain information about those
3698       *                               entries and/or references.
3699       */
3700      public SearchResult search(final ReadOnlySearchRequest searchRequest)
3701             throws LDAPSearchException
3702      {
3703        return search((SearchRequest) searchRequest);
3704      }
3705    
3706    
3707    
3708      /**
3709       * Processes a search operation with the provided information.  It is expected
3710       * that at most one entry will be returned from the search, and that no
3711       * additional content from the successful search result (e.g., diagnostic
3712       * message or response controls) are needed.
3713       * <BR><BR>
3714       * Note that if the search does not complete successfully, an
3715       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3716       * search result entries or references may have been returned before the
3717       * failure response is received.  In this case, the
3718       * {@code LDAPSearchException} methods like {@code getEntryCount},
3719       * {@code getSearchEntries}, {@code getReferenceCount}, and
3720       * {@code getSearchReferences} may be used to obtain information about those
3721       * entries and references.
3722       *
3723       * @param  baseDN      The base DN for the search request.  It must not be
3724       *                     {@code null}.
3725       * @param  scope       The scope that specifies the range of entries that
3726       *                     should be examined for the search.
3727       * @param  filter      The string representation of the filter to use to
3728       *                     identify matching entries.  It must not be
3729       *                     {@code null}.
3730       * @param  attributes  The set of attributes that should be returned in
3731       *                     matching entries.  It may be {@code null} or empty if
3732       *                     the default attribute set (all user attributes) is to
3733       *                     be requested.
3734       *
3735       * @return  The entry that was returned from the search, or {@code null} if no
3736       *          entry was returned or the base entry does not exist.
3737       *
3738       * @throws  LDAPSearchException  If the search does not complete successfully,
3739       *                               if more than a single entry is returned, or
3740       *                               if a problem is encountered while parsing the
3741       *                               provided filter string, sending the request,
3742       *                               or reading the response.  If one or more
3743       *                               entries or references were returned before
3744       *                               the failure was encountered, then the
3745       *                               {@code LDAPSearchException} object may be
3746       *                               examined to obtain information about those
3747       *                               entries and/or references.
3748       */
3749      public SearchResultEntry searchForEntry(final String baseDN,
3750                                              final SearchScope scope,
3751                                              final String filter,
3752                                              final String... attributes)
3753             throws LDAPSearchException
3754      {
3755        final SearchRequest r;
3756        try
3757        {
3758          r = new SearchRequest(baseDN, scope, DereferencePolicy.NEVER, 1, 0, false,
3759               filter, attributes);
3760        }
3761        catch (final LDAPException le)
3762        {
3763          debugException(le);
3764          throw new LDAPSearchException(le);
3765        }
3766    
3767        return searchForEntry(r);
3768      }
3769    
3770    
3771    
3772      /**
3773       * Processes a search operation with the provided information.  It is expected
3774       * that at most one entry will be returned from the search, and that no
3775       * additional content from the successful search result (e.g., diagnostic
3776       * message or response controls) are needed.
3777       * <BR><BR>
3778       * Note that if the search does not complete successfully, an
3779       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3780       * search result entries or references may have been returned before the
3781       * failure response is received.  In this case, the
3782       * {@code LDAPSearchException} methods like {@code getEntryCount},
3783       * {@code getSearchEntries}, {@code getReferenceCount}, and
3784       * {@code getSearchReferences} may be used to obtain information about those
3785       * entries and references.
3786       *
3787       * @param  baseDN      The base DN for the search request.  It must not be
3788       *                     {@code null}.
3789       * @param  scope       The scope that specifies the range of entries that
3790       *                     should be examined for the search.
3791       * @param  filter      The string representation of the filter to use to
3792       *                     identify matching entries.  It must not be
3793       *                     {@code null}.
3794       * @param  attributes  The set of attributes that should be returned in
3795       *                     matching entries.  It may be {@code null} or empty if
3796       *                     the default attribute set (all user attributes) is to
3797       *                     be requested.
3798       *
3799       * @return  The entry that was returned from the search, or {@code null} if no
3800       *          entry was returned or the base entry does not exist.
3801       *
3802       * @throws  LDAPSearchException  If the search does not complete successfully,
3803       *                               if more than a single entry is returned, or
3804       *                               if a problem is encountered while parsing the
3805       *                               provided filter string, sending the request,
3806       *                               or reading the response.  If one or more
3807       *                               entries or references were returned before
3808       *                               the failure was encountered, then the
3809       *                               {@code LDAPSearchException} object may be
3810       *                               examined to obtain information about those
3811       *                               entries and/or references.
3812       */
3813      public SearchResultEntry searchForEntry(final String baseDN,
3814                                              final SearchScope scope,
3815                                              final Filter filter,
3816                                              final String... attributes)
3817             throws LDAPSearchException
3818      {
3819        return searchForEntry(new SearchRequest(baseDN, scope,
3820             DereferencePolicy.NEVER, 1, 0, false, filter, attributes));
3821      }
3822    
3823    
3824    
3825      /**
3826       * Processes a search operation with the provided information.  It is expected
3827       * that at most one entry will be returned from the search, and that no
3828       * additional content from the successful search result (e.g., diagnostic
3829       * message or response controls) are needed.
3830       * <BR><BR>
3831       * Note that if the search does not complete successfully, an
3832       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3833       * search result entries or references may have been returned before the
3834       * failure response is received.  In this case, the
3835       * {@code LDAPSearchException} methods like {@code getEntryCount},
3836       * {@code getSearchEntries}, {@code getReferenceCount}, and
3837       * {@code getSearchReferences} may be used to obtain information about those
3838       * entries and references.
3839       *
3840       * @param  baseDN       The base DN for the search request.  It must not be
3841       *                      {@code null}.
3842       * @param  scope        The scope that specifies the range of entries that
3843       *                      should be examined for the search.
3844       * @param  derefPolicy  The dereference policy the server should use for any
3845       *                      aliases encountered while processing the search.
3846       * @param  timeLimit    The maximum length of time in seconds that the server
3847       *                      should spend processing this search request.  A value
3848       *                      of zero indicates that there should be no limit.
3849       * @param  typesOnly    Indicates whether to return only attribute names in
3850       *                      matching entries, or both attribute names and values.
3851       * @param  filter       The string representation of the filter to use to
3852       *                      identify matching entries.  It must not be
3853       *                      {@code null}.
3854       * @param  attributes   The set of attributes that should be returned in
3855       *                      matching entries.  It may be {@code null} or empty if
3856       *                      the default attribute set (all user attributes) is to
3857       *                      be requested.
3858       *
3859       * @return  The entry that was returned from the search, or {@code null} if no
3860       *          entry was returned or the base entry does not exist.
3861       *
3862       * @throws  LDAPSearchException  If the search does not complete successfully,
3863       *                               if more than a single entry is returned, or
3864       *                               if a problem is encountered while parsing the
3865       *                               provided filter string, sending the request,
3866       *                               or reading the response.  If one or more
3867       *                               entries or references were returned before
3868       *                               the failure was encountered, then the
3869       *                               {@code LDAPSearchException} object may be
3870       *                               examined to obtain information about those
3871       *                               entries and/or references.
3872       */
3873      public SearchResultEntry searchForEntry(final String baseDN,
3874                                              final SearchScope scope,
3875                                              final DereferencePolicy derefPolicy,
3876                                              final int timeLimit,
3877                                              final boolean typesOnly,
3878                                              final String filter,
3879                                              final String... attributes)
3880             throws LDAPSearchException
3881      {
3882        final SearchRequest r;
3883        try
3884        {
3885          r = new SearchRequest(baseDN, scope, derefPolicy, 1, timeLimit, typesOnly,
3886               filter, attributes);
3887        }
3888        catch (final LDAPException le)
3889        {
3890          debugException(le);
3891          throw new LDAPSearchException(le);
3892        }
3893    
3894        return searchForEntry(r);
3895      }
3896    
3897    
3898    
3899      /**
3900       * Processes a search operation with the provided information.  It is expected
3901       * that at most one entry will be returned from the search, and that no
3902       * additional content from the successful search result (e.g., diagnostic
3903       * message or response controls) are needed.
3904       * <BR><BR>
3905       * Note that if the search does not complete successfully, an
3906       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3907       * search result entries or references may have been returned before the
3908       * failure response is received.  In this case, the
3909       * {@code LDAPSearchException} methods like {@code getEntryCount},
3910       * {@code getSearchEntries}, {@code getReferenceCount}, and
3911       * {@code getSearchReferences} may be used to obtain information about those
3912       * entries and references.
3913       *
3914       * @param  baseDN       The base DN for the search request.  It must not be
3915       *                      {@code null}.
3916       * @param  scope        The scope that specifies the range of entries that
3917       *                      should be examined for the search.
3918       * @param  derefPolicy  The dereference policy the server should use for any
3919       *                      aliases encountered while processing the search.
3920       * @param  timeLimit    The maximum length of time in seconds that the server
3921       *                      should spend processing this search request.  A value
3922       *                      of zero indicates that there should be no limit.
3923       * @param  typesOnly    Indicates whether to return only attribute names in
3924       *                      matching entries, or both attribute names and values.
3925       * @param  filter       The filter to use to identify matching entries.  It
3926       *                      must not be {@code null}.
3927       * @param  attributes   The set of attributes that should be returned in
3928       *                      matching entries.  It may be {@code null} or empty if
3929       *                      the default attribute set (all user attributes) is to
3930       *                      be requested.
3931       *
3932       * @return  The entry that was returned from the search, or {@code null} if no
3933       *          entry was returned or the base entry does not exist.
3934       *
3935       * @throws  LDAPSearchException  If the search does not complete successfully,
3936       *                               if more than a single entry is returned, or
3937       *                               if a problem is encountered while parsing the
3938       *                               provided filter string, sending the request,
3939       *                               or reading the response.  If one or more
3940       *                               entries or references were returned before
3941       *                               the failure was encountered, then the
3942       *                               {@code LDAPSearchException} object may be
3943       *                               examined to obtain information about those
3944       *                               entries and/or references.
3945       */
3946      public SearchResultEntry searchForEntry(final String baseDN,
3947                                              final SearchScope scope,
3948                                              final DereferencePolicy derefPolicy,
3949                                              final int timeLimit,
3950                                              final boolean typesOnly,
3951                                              final Filter filter,
3952                                              final String... attributes)
3953           throws LDAPSearchException
3954      {
3955        return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
3956             timeLimit, typesOnly, filter, attributes));
3957      }
3958    
3959    
3960    
3961      /**
3962       * Processes the provided search request.  It is expected that at most one
3963       * entry will be returned from the search, and that no additional content from
3964       * the successful search result (e.g., diagnostic message or response
3965       * controls) are needed.
3966       * <BR><BR>
3967       * Note that if the search does not complete successfully, an
3968       * {@code LDAPSearchException} will be thrown  In some cases, one or more
3969       * search result entries or references may have been returned before the
3970       * failure response is received.  In this case, the
3971       * {@code LDAPSearchException} methods like {@code getEntryCount},
3972       * {@code getSearchEntries}, {@code getReferenceCount}, and
3973       * {@code getSearchReferences} may be used to obtain information about those
3974       * entries and references.
3975       *
3976       * @param  searchRequest  The search request to be processed.  If it is
3977       *                        configured with a search result listener or a size
3978       *                        limit other than one, then the provided request will
3979       *                        be duplicated with the appropriate settings.
3980       *
3981       * @return  The entry that was returned from the search, or {@code null} if no
3982       *          entry was returned or the base entry does not exist.
3983       *
3984       * @throws  LDAPSearchException  If the search does not complete successfully,
3985       *                               if more than a single entry is returned, or
3986       *                               if a problem is encountered while parsing the
3987       *                               provided filter string, sending the request,
3988       *                               or reading the response.  If one or more
3989       *                               entries or references were returned before
3990       *                               the failure was encountered, then the
3991       *                               {@code LDAPSearchException} object may be
3992       *                               examined to obtain information about those
3993       *                               entries and/or references.
3994       */
3995      public SearchResultEntry searchForEntry(final SearchRequest searchRequest)
3996             throws LDAPSearchException
3997      {
3998        final SearchRequest r;
3999        if ((searchRequest.getSearchResultListener() != null) ||
4000            (searchRequest.getSizeLimit() != 1))
4001        {
4002          r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(),
4003               searchRequest.getDereferencePolicy(), 1,
4004               searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(),
4005               searchRequest.getFilter(), searchRequest.getAttributes());
4006    
4007          r.setFollowReferrals(searchRequest.followReferralsInternal());
4008          r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null));
4009    
4010          if (searchRequest.hasControl())
4011          {
4012            r.setControlsInternal(searchRequest.getControls());
4013          }
4014        }
4015        else
4016        {
4017          r = searchRequest;
4018        }
4019    
4020        final SearchResult result;
4021        try
4022        {
4023          result = search(r);
4024        }
4025        catch (final LDAPSearchException lse)
4026        {
4027          debugException(lse);
4028    
4029          if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT)
4030          {
4031            return null;
4032          }
4033    
4034          throw lse;
4035        }
4036    
4037        if (result.getEntryCount() == 0)
4038        {
4039          return null;
4040        }
4041        else
4042        {
4043          return result.getSearchEntries().get(0);
4044        }
4045      }
4046    
4047    
4048    
4049      /**
4050       * Processes the provided search request.  It is expected that at most one
4051       * entry will be returned from the search, and that no additional content from
4052       * the successful search result (e.g., diagnostic message or response
4053       * controls) are needed.
4054       * <BR><BR>
4055       * Note that if the search does not complete successfully, an
4056       * {@code LDAPSearchException} will be thrown  In some cases, one or more
4057       * search result entries or references may have been returned before the
4058       * failure response is received.  In this case, the
4059       * {@code LDAPSearchException} methods like {@code getEntryCount},
4060       * {@code getSearchEntries}, {@code getReferenceCount}, and
4061       * {@code getSearchReferences} may be used to obtain information about those
4062       * entries and references.
4063       *
4064       * @param  searchRequest  The search request to be processed.  If it is
4065       *                        configured with a search result listener or a size
4066       *                        limit other than one, then the provided request will
4067       *                        be duplicated with the appropriate settings.
4068       *
4069       * @return  The entry that was returned from the search, or {@code null} if no
4070       *          entry was returned or the base entry does not exist.
4071       *
4072       * @throws  LDAPSearchException  If the search does not complete successfully,
4073       *                               if more than a single entry is returned, or
4074       *                               if a problem is encountered while parsing the
4075       *                               provided filter string, sending the request,
4076       *                               or reading the response.  If one or more
4077       *                               entries or references were returned before
4078       *                               the failure was encountered, then the
4079       *                               {@code LDAPSearchException} object may be
4080       *                               examined to obtain information about those
4081       *                               entries and/or references.
4082       */
4083      public SearchResultEntry searchForEntry(
4084                                    final ReadOnlySearchRequest searchRequest)
4085             throws LDAPSearchException
4086      {
4087        return searchForEntry((SearchRequest) searchRequest);
4088      }
4089    
4090    
4091    
4092      /**
4093       * Processes the provided search request as an asynchronous operation.
4094       *
4095       * @param  searchRequest  The search request to be processed.  It must not be
4096       *                        {@code null}, and it must be configured with a
4097       *                        search result listener that is also an
4098       *                        {@code AsyncSearchResultListener}.
4099       *
4100       * @return  An async request ID that may be used to reference the operation.
4101       *
4102       * @throws  LDAPException  If the provided search request does not have a
4103       *                         search result listener that is an
4104       *                         {@code AsyncSearchResultListener}, or if a problem
4105       *                         occurs while sending the request.
4106       */
4107      public AsyncRequestID asyncSearch(final SearchRequest searchRequest)
4108             throws LDAPException
4109      {
4110        ensureNotNull(searchRequest);
4111    
4112        final SearchResultListener searchListener =
4113             searchRequest.getSearchResultListener();
4114        if (searchListener == null)
4115        {
4116          final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
4117               ERR_ASYNC_SEARCH_NO_LISTENER.get());
4118          debugCodingError(le);
4119          throw le;
4120        }
4121        else if (! (searchListener instanceof AsyncSearchResultListener))
4122        {
4123          final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
4124               ERR_ASYNC_SEARCH_INVALID_LISTENER.get());
4125          debugCodingError(le);
4126          throw le;
4127        }
4128    
4129        if (synchronousMode())
4130        {
4131          throw new LDAPException(ResultCode.NOT_SUPPORTED,
4132               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
4133        }
4134    
4135        return searchRequest.processAsync(this,
4136             (AsyncSearchResultListener) searchListener);
4137      }
4138    
4139    
4140    
4141      /**
4142       * Processes the provided search request as an asynchronous operation.
4143       *
4144       * @param  searchRequest  The search request to be processed.  It must not be
4145       *                        {@code null}, and it must be configured with a
4146       *                        search result listener that is also an
4147       *                        {@code AsyncSearchResultListener}.
4148       *
4149       * @return  An async request ID that may be used to reference the operation.
4150       *
4151       * @throws  LDAPException  If the provided search request does not have a
4152       *                         search result listener that is an
4153       *                         {@code AsyncSearchResultListener}, or if a problem
4154       *                         occurs while sending the request.
4155       */
4156      public AsyncRequestID asyncSearch(final ReadOnlySearchRequest searchRequest)
4157             throws LDAPException
4158      {
4159        if (synchronousMode())
4160        {
4161          throw new LDAPException(ResultCode.NOT_SUPPORTED,
4162               ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
4163        }
4164    
4165        return asyncSearch((SearchRequest) searchRequest);
4166      }
4167    
4168    
4169    
4170      /**
4171       * Processes the provided generic request and returns the result.  This may
4172       * be useful for cases in which it is not known what type of operation the
4173       * request represents.
4174       *
4175       * @param  request  The request to be processed.
4176       *
4177       * @return  The result obtained from processing the request.
4178       *
4179       * @throws  LDAPException  If a problem occurs while sending the request or
4180       *                         reading the response.  Note simply having a
4181       *                         non-success result code in the response will not
4182       *                         cause an exception to be thrown.
4183       */
4184      public LDAPResult processOperation(final LDAPRequest request)
4185             throws LDAPException
4186      {
4187        return request.process(this, 1);
4188      }
4189    
4190    
4191    
4192      /**
4193       * Retrieves the referral connector that should be used to establish
4194       * connections for use when following referrals.
4195       *
4196       * @return  The referral connector that should be used to establish
4197       *          connections for use when following referrals.
4198       */
4199      public ReferralConnector getReferralConnector()
4200      {
4201        if (referralConnector == null)
4202        {
4203          return this;
4204        }
4205        else
4206        {
4207          return referralConnector;
4208        }
4209      }
4210    
4211    
4212    
4213      /**
4214       * Specifies the referral connector that should be used to establish
4215       * connections for use when following referrals.
4216       *
4217       * @param  referralConnector  The referral connector that should be used to
4218       *                            establish connections for use when following
4219       *                            referrals.
4220       */
4221      public void setReferralConnector(final ReferralConnector referralConnector)
4222      {
4223        if (referralConnector == null)
4224        {
4225          this.referralConnector = this;
4226        }
4227        else
4228        {
4229          this.referralConnector = referralConnector;
4230        }
4231      }
4232    
4233    
4234    
4235      /**
4236       * Sends the provided LDAP message to the server over this connection.
4237       *
4238       * @param  message  The LDAP message to send to the target server.
4239       *
4240       * @throws  LDAPException  If a problem occurs while sending the request.
4241       */
4242      void sendMessage(final LDAPMessage message)
4243             throws LDAPException
4244      {
4245        if (needsReconnect.compareAndSet(true, false))
4246        {
4247          reconnect();
4248        }
4249    
4250        final LDAPConnectionInternals internals = connectionInternals;
4251        if (internals == null)
4252        {
4253          throw new LDAPException(ResultCode.SERVER_DOWN,
4254                                  ERR_CONN_NOT_ESTABLISHED.get());
4255        }
4256        else
4257        {
4258          @SuppressWarnings("deprecation")
4259          final boolean autoReconnect = connectionOptions.autoReconnect();
4260          internals.sendMessage(message, autoReconnect);
4261          lastCommunicationTime = System.currentTimeMillis();
4262        }
4263      }
4264    
4265    
4266    
4267      /**
4268       * Retrieves the message ID that should be used for the next request sent
4269       * over this connection.
4270       *
4271       * @return  The message ID that should be used for the next request sent over
4272       *          this connection, or -1 if this connection is not established.
4273       */
4274      int nextMessageID()
4275      {
4276        final LDAPConnectionInternals internals = connectionInternals;
4277        if (internals == null)
4278        {
4279          return -1;
4280        }
4281        else
4282        {
4283          return internals.nextMessageID();
4284        }
4285      }
4286    
4287    
4288    
4289      /**
4290       * Retrieves the disconnect info object for this connection, if available.
4291       *
4292       * @return  The disconnect info for this connection, or {@code null} if none
4293       *          is set.
4294       */
4295      DisconnectInfo getDisconnectInfo()
4296      {
4297        return disconnectInfo.get();
4298      }
4299    
4300    
4301    
4302      /**
4303       * Sets the disconnect type, message, and cause for this connection, if those
4304       * values have not been previously set.  It will not overwrite any values that
4305       * had been previously set.
4306       * <BR><BR>
4307       * This method may be called by code which is not part of the LDAP SDK to
4308       * provide additional information about the reason for the closure.  In that
4309       * case, this method must be called before the call to
4310       * {@link LDAPConnection#close}.
4311       *
4312       * @param  type     The disconnect type.  It must not be {@code null}.
4313       * @param  message  A message providing additional information about the
4314       *                  disconnect.  It may be {@code null} if no message is
4315       *                  available.
4316       * @param  cause    The exception that was caught to trigger the disconnect.
4317       *                  It may be {@code null} if the disconnect was not triggered
4318       *                  by an exception.
4319       */
4320      public void setDisconnectInfo(final DisconnectType type, final String message,
4321                                    final Throwable cause)
4322      {
4323        disconnectInfo.compareAndSet(null,
4324             new DisconnectInfo(this, type, message, cause));
4325      }
4326    
4327    
4328    
4329      /**
4330       * Sets the disconnect info for this connection, if it is not already set.
4331       *
4332       * @param  info  The disconnect info to be set, if it is not already set.
4333       *
4334       * @return  The disconnect info set for the connection, whether it was
4335       *          previously or newly set.
4336       */
4337      DisconnectInfo setDisconnectInfo(final DisconnectInfo info)
4338      {
4339        disconnectInfo.compareAndSet(null, info);
4340        return disconnectInfo.get();
4341      }
4342    
4343    
4344    
4345      /**
4346       * Retrieves the disconnect type for this connection, if available.
4347       *
4348       * @return  The disconnect type for this connection, or {@code null} if no
4349       *          disconnect type has been set.
4350       */
4351      public DisconnectType getDisconnectType()
4352      {
4353        final DisconnectInfo di = disconnectInfo.get();
4354        if (di == null)
4355        {
4356          return null;
4357        }
4358        else
4359        {
4360          return di.getType();
4361        }
4362      }
4363    
4364    
4365    
4366      /**
4367       * Retrieves the disconnect message for this connection, which may provide
4368       * additional information about the reason for the disconnect, if available.
4369       *
4370       * @return  The disconnect message for this connection, or {@code null} if
4371       *          no disconnect message has been set.
4372       */
4373      public String getDisconnectMessage()
4374      {
4375        final DisconnectInfo di = disconnectInfo.get();
4376        if (di == null)
4377        {
4378          return null;
4379        }
4380        else
4381        {
4382          return di.getMessage();
4383        }
4384      }
4385    
4386    
4387    
4388      /**
4389       * Retrieves the disconnect cause for this connection, which is an exception
4390       * or error that triggered the connection termination, if available.
4391       *
4392       * @return  The disconnect cause for this connection, or {@code null} if no
4393       *          disconnect cause has been set.
4394       */
4395      public Throwable getDisconnectCause()
4396      {
4397        final DisconnectInfo di = disconnectInfo.get();
4398        if (di == null)
4399        {
4400          return null;
4401        }
4402        else
4403        {
4404          return di.getCause();
4405        }
4406      }
4407    
4408    
4409    
4410      /**
4411       * Indicates that this connection has been closed and is no longer available
4412       * for use.
4413       */
4414      void setClosed()
4415      {
4416        needsReconnect.set(false);
4417    
4418        if (disconnectInfo.get() == null)
4419        {
4420          try
4421          {
4422            final StackTraceElement[] stackElements =
4423                 Thread.currentThread().getStackTrace();
4424            final StackTraceElement[] parentStackElements =
4425                 new StackTraceElement[stackElements.length - 1];
4426            System.arraycopy(stackElements, 1, parentStackElements, 0,
4427                 parentStackElements.length);
4428    
4429            setDisconnectInfo(DisconnectType.OTHER,
4430                 ERR_CONN_CLOSED_BY_UNEXPECTED_CALL_PATH.get(
4431                      getStackTrace(parentStackElements)),
4432                 null);
4433          }
4434          catch (final Exception e)
4435          {
4436            debugException(e);
4437          }
4438        }
4439    
4440        connectionStatistics.incrementNumDisconnects();
4441        final LDAPConnectionInternals internals = connectionInternals;
4442        if (internals != null)
4443        {
4444          internals.close();
4445          connectionInternals = null;
4446        }
4447    
4448        cachedSchema = null;
4449        lastCommunicationTime = -1L;
4450    
4451        synchronized (this)
4452        {
4453          final Timer t = timer;
4454          timer = null;
4455    
4456          if (t != null)
4457          {
4458            t.cancel();
4459          }
4460        }
4461      }
4462    
4463    
4464    
4465      /**
4466       * Registers the provided response acceptor with the connection reader.
4467       *
4468       * @param  messageID         The message ID for which the acceptor is to be
4469       *                           registered.
4470       * @param  responseAcceptor  The response acceptor to register.
4471       *
4472       * @throws  LDAPException  If another message acceptor is already registered
4473       *                         with the provided message ID.
4474       */
4475      void registerResponseAcceptor(final int messageID,
4476                                    final ResponseAcceptor responseAcceptor)
4477           throws LDAPException
4478      {
4479        if (needsReconnect.compareAndSet(true, false))
4480        {
4481          reconnect();
4482        }
4483    
4484        final LDAPConnectionInternals internals = connectionInternals;
4485        if (internals == null)
4486        {
4487          throw new LDAPException(ResultCode.SERVER_DOWN,
4488                                  ERR_CONN_NOT_ESTABLISHED.get());
4489        }
4490        else
4491        {
4492          internals.registerResponseAcceptor(messageID, responseAcceptor);
4493        }
4494      }
4495    
4496    
4497    
4498      /**
4499       * Deregisters the response acceptor associated with the provided message ID.
4500       *
4501       * @param  messageID  The message ID for which to deregister the associated
4502       *                    response acceptor.
4503       */
4504      void deregisterResponseAcceptor(final int messageID)
4505      {
4506        final LDAPConnectionInternals internals = connectionInternals;
4507        if (internals != null)
4508        {
4509          internals.deregisterResponseAcceptor(messageID);
4510        }
4511      }
4512    
4513    
4514    
4515      /**
4516       * Retrieves a timer for use with this connection, creating one if necessary.
4517       *
4518       * @return  A timer for use with this connection.
4519       */
4520      synchronized Timer getTimer()
4521      {
4522        if (timer == null)
4523        {
4524          timer = new Timer("Timer thread for " + toString(), true);
4525        }
4526    
4527        return timer;
4528      }
4529    
4530    
4531    
4532      /**
4533       * {@inheritDoc}
4534       */
4535      public LDAPConnection getReferralConnection(final LDAPURL referralURL,
4536                                                  final LDAPConnection connection)
4537             throws LDAPException
4538      {
4539        final String host = referralURL.getHost();
4540        final int    port = referralURL.getPort();
4541    
4542        BindRequest bindRequest = null;
4543        if (connection.lastBindRequest != null)
4544        {
4545          bindRequest = connection.lastBindRequest.getRebindRequest(host, port);
4546          if (bindRequest == null)
4547          {
4548            throw new LDAPException(ResultCode.REFERRAL,
4549                                    ERR_CONN_CANNOT_AUTHENTICATE_FOR_REFERRAL.get(
4550                                         host, port));
4551          }
4552        }
4553    
4554        final ExtendedRequest connStartTLSRequest = connection.startTLSRequest;
4555    
4556        final LDAPConnection conn = new LDAPConnection(connection.socketFactory,
4557             connection.connectionOptions, host, port);
4558    
4559        if (connStartTLSRequest != null)
4560        {
4561          try
4562          {
4563            final ExtendedResult startTLSResult =
4564                 conn.processExtendedOperation(connStartTLSRequest);
4565            if (startTLSResult.getResultCode() != ResultCode.SUCCESS)
4566            {
4567              throw new LDAPException(startTLSResult);
4568            }
4569          }
4570          catch (final LDAPException le)
4571          {
4572            debugException(le);
4573            conn.setDisconnectInfo(DisconnectType.SECURITY_PROBLEM, null, le);
4574            conn.close();
4575    
4576            throw le;
4577          }
4578        }
4579    
4580        if (bindRequest != null)
4581        {
4582          try
4583          {
4584            conn.bind(bindRequest);
4585          }
4586          catch (final LDAPException le)
4587          {
4588            debugException(le);
4589            conn.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
4590            conn.close();
4591    
4592            throw le;
4593          }
4594        }
4595    
4596        return conn;
4597      }
4598    
4599    
4600    
4601      /**
4602       * Retrieves the last successful bind request processed on this connection.
4603       *
4604       * @return  The last successful bind request processed on this connection.  It
4605       *          may be {@code null} if no bind has been performed, or if the last
4606       *          bind attempt was not successful.
4607       */
4608      public BindRequest getLastBindRequest()
4609      {
4610        return lastBindRequest;
4611      }
4612    
4613    
4614    
4615      /**
4616       * Retrieves the StartTLS request used to secure this connection.
4617       *
4618       * @return  The StartTLS request used to secure this connection, or
4619       *          {@code null} if StartTLS has not been used to secure this
4620       *          connection.
4621       */
4622      public ExtendedRequest getStartTLSRequest()
4623      {
4624        return startTLSRequest;
4625      }
4626    
4627    
4628    
4629      /**
4630       * Retrieves an instance of the {@code LDAPConnectionInternals} object for
4631       * this connection.
4632       *
4633       * @param  throwIfDisconnected  Indicates whether to throw an
4634       *                              {@code LDAPException} if the connection is not
4635       *                              established.
4636       *
4637       * @return  The {@code LDAPConnectionInternals} object for this connection, or
4638       *          {@code null} if the connection is not established and no exception
4639       *          should be thrown.
4640       *
4641       * @throws  LDAPException  If the connection is not established and
4642       *                         {@code throwIfDisconnected} is {@code true}.
4643       */
4644      LDAPConnectionInternals getConnectionInternals(
4645                                   final boolean throwIfDisconnected)
4646           throws LDAPException
4647      {
4648        final LDAPConnectionInternals internals = connectionInternals;
4649        if ((internals == null) && throwIfDisconnected)
4650        {
4651          throw new LDAPException(ResultCode.SERVER_DOWN,
4652               ERR_CONN_NOT_ESTABLISHED.get());
4653        }
4654        else
4655        {
4656          return internals;
4657        }
4658      }
4659    
4660    
4661    
4662      /**
4663       * Retrieves the cached schema for this connection, if applicable.
4664       *
4665       * @return  The cached schema for this connection, or {@code null} if it is
4666       *          not available (e.g., because the connection is not established,
4667       *          because {@link LDAPConnectionOptions#useSchema()} is false, or
4668       *          because an error occurred when trying to read the server schema).
4669       */
4670      Schema getCachedSchema()
4671      {
4672        return cachedSchema;
4673      }
4674    
4675    
4676    
4677      /**
4678       * Sets the cached schema for this connection.
4679       *
4680       * @param  cachedSchema  The cached schema for this connection.  It may be
4681       *                       {@code null} if no cached schema is available.
4682       */
4683      void setCachedSchema(final Schema cachedSchema)
4684      {
4685        this.cachedSchema = cachedSchema;
4686      }
4687    
4688    
4689    
4690      /**
4691       * Indicates whether this connection is operating in synchronous mode.
4692       *
4693       * @return  {@code true} if this connection is operating in synchronous mode,
4694       *          or {@code false} if not.
4695       */
4696      public boolean synchronousMode()
4697      {
4698        final LDAPConnectionInternals internals = connectionInternals;
4699        if (internals == null)
4700        {
4701          return false;
4702        }
4703        else
4704        {
4705          return internals.synchronousMode();
4706        }
4707      }
4708    
4709    
4710    
4711      /**
4712       * Reads a response from the server, blocking if necessary until the response
4713       * has been received.  This should only be used for connections operating in
4714       * synchronous mode.
4715       *
4716       * @param  messageID  The message ID for the response to be read.  Any
4717       *                    response read with a different message ID will be
4718       *                    discarded, unless it is an unsolicited notification in
4719       *                    which case it will be provided to any registered
4720       *                    unsolicited notification handler.
4721       *
4722       * @return  The response read from the server.
4723       *
4724       * @throws  LDAPException  If a problem occurs while reading the response.
4725       */
4726      LDAPResponse readResponse(final int messageID)
4727                   throws LDAPException
4728      {
4729        final LDAPConnectionInternals internals = connectionInternals;
4730        if (internals != null)
4731        {
4732          final LDAPResponse response =
4733               internals.getConnectionReader().readResponse(messageID);
4734          debugLDAPResult(response, this);
4735          return response;
4736        }
4737        else
4738        {
4739          final DisconnectInfo di = disconnectInfo.get();
4740          if (di == null)
4741          {
4742            return new ConnectionClosedResponse(ResultCode.CONNECT_ERROR,
4743                 ERR_CONN_READ_RESPONSE_NOT_ESTABLISHED.get());
4744          }
4745          else
4746          {
4747            return new ConnectionClosedResponse(di.getType().getResultCode(),
4748                 di.getMessage());
4749          }
4750        }
4751      }
4752    
4753    
4754    
4755      /**
4756       * Retrieves the time that this connection was established in the number of
4757       * milliseconds since January 1, 1970 UTC (the same format used by
4758       * {@code System.currentTimeMillis}.
4759       *
4760       * @return  The time that this connection was established, or -1 if the
4761       *          connection is not currently established.
4762       */
4763      public long getConnectTime()
4764      {
4765        final LDAPConnectionInternals internals = connectionInternals;
4766        if (internals != null)
4767        {
4768          return internals.getConnectTime();
4769        }
4770        else
4771        {
4772          return -1L;
4773        }
4774      }
4775    
4776    
4777    
4778      /**
4779       * Retrieves the time that this connection was last used to send or receive an
4780       * LDAP message.  The value will represent the number of milliseconds since
4781       * January 1, 1970 UTC (the same format used by
4782       * {@code System.currentTimeMillis}.
4783       *
4784       * @return  The time that this connection was last used to send or receive an
4785       *          LDAP message.  If the connection is not established, then -1 will
4786       *          be returned.  If the connection is established but no
4787       *          communication has been performed over the connection since it was
4788       *          established, then the value of {@link #getConnectTime()} will be
4789       *          returned.
4790       */
4791      public long getLastCommunicationTime()
4792      {
4793        if (lastCommunicationTime > 0L)
4794        {
4795          return lastCommunicationTime;
4796        }
4797        else
4798        {
4799          return getConnectTime();
4800        }
4801      }
4802    
4803    
4804    
4805      /**
4806       * Updates the last communication time for this connection to be the current
4807       * time.
4808       */
4809      void setLastCommunicationTime()
4810      {
4811        lastCommunicationTime = System.currentTimeMillis();
4812      }
4813    
4814    
4815    
4816      /**
4817       * Retrieves the connection statistics for this LDAP connection.
4818       *
4819       * @return  The connection statistics for this LDAP connection.
4820       */
4821      public LDAPConnectionStatistics getConnectionStatistics()
4822      {
4823        return connectionStatistics;
4824      }
4825    
4826    
4827    
4828      /**
4829       * Retrieves the number of outstanding operations on this LDAP connection
4830       * (i.e., the number of operations currently in progress).  The value will
4831       * only be valid for connections not configured to use synchronous mode.
4832       *
4833       * @return  The number of outstanding operations on this LDAP connection, or
4834       *          -1 if it cannot be determined (e.g., because the connection is not
4835       *          established or is operating in synchronous mode).
4836       */
4837      public int getActiveOperationCount()
4838      {
4839        final LDAPConnectionInternals internals = connectionInternals;
4840    
4841        if (internals == null)
4842        {
4843          return -1;
4844        }
4845        else
4846        {
4847          if (internals.synchronousMode())
4848          {
4849            return -1;
4850          }
4851          else
4852          {
4853            return internals.getConnectionReader().getActiveOperationCount();
4854          }
4855        }
4856      }
4857    
4858    
4859    
4860      /**
4861       * Retrieves the schema from the provided connection.  If the retrieved schema
4862       * matches schema that's already in use by other connections, the common
4863       * schema will be used instead of the newly-retrieved version.
4864       *
4865       * @param  c  The connection for which to retrieve the schema.
4866       *
4867       * @return  The schema retrieved from the given connection, or a cached
4868       *          schema if it matched a schema that was already in use.
4869       *
4870       * @throws  LDAPException  If a problem is encountered while retrieving or
4871       *                         parsing the schema.
4872       */
4873      private static Schema getCachedSchema(final LDAPConnection c)
4874             throws LDAPException
4875      {
4876        final Schema s = c.getSchema();
4877    
4878        synchronized (SCHEMA_SET)
4879        {
4880          return SCHEMA_SET.addAndGet(s);
4881        }
4882      }
4883    
4884    
4885    
4886      /**
4887       * Retrieves the connection attachment with the specified name.
4888       *
4889       * @param  name  The name of the attachment to retrieve.  It must not be
4890       *               {@code null}.
4891       *
4892       * @return  The connection attachment with the specified name, or {@code null}
4893       *          if there is no such attachment.
4894       */
4895      synchronized Object getAttachment(final String name)
4896      {
4897        if (attachments == null)
4898        {
4899          return null;
4900        }
4901        else
4902        {
4903          return attachments.get(name);
4904        }
4905      }
4906    
4907    
4908    
4909      /**
4910       * Sets a connection attachment with the specified name and value.
4911       *
4912       * @param  name   The name of the attachment to set.  It must not be
4913       *                {@code null}.
4914       * @param  value  The value to use for the attachment.  It may be {@code null}
4915       *                if an attachment with the specified name should be cleared
4916       *                rather than overwritten.
4917       */
4918      synchronized void setAttachment(final String name, final Object value)
4919      {
4920        if (attachments == null)
4921        {
4922          attachments = new HashMap<String,Object>(10);
4923        }
4924    
4925        if (value == null)
4926        {
4927          attachments.remove(name);
4928        }
4929        else
4930        {
4931          attachments.put(name, value);
4932        }
4933      }
4934    
4935    
4936    
4937      /**
4938       * Performs any necessary cleanup to ensure that this connection is properly
4939       * closed before it is garbage collected.
4940       *
4941       * @throws  Throwable  If the superclass finalizer throws an exception.
4942       */
4943      @Override()
4944      protected void finalize()
4945                throws Throwable
4946      {
4947        super.finalize();
4948    
4949        setDisconnectInfo(DisconnectType.CLOSED_BY_FINALIZER, null, null);
4950        setClosed();
4951      }
4952    
4953    
4954    
4955      /**
4956       * Retrieves a string representation of this LDAP connection.
4957       *
4958       * @return  A string representation of this LDAP connection.
4959       */
4960      @Override()
4961      public String toString()
4962      {
4963        final StringBuilder buffer = new StringBuilder();
4964        toString(buffer);
4965        return buffer.toString();
4966      }
4967    
4968    
4969    
4970      /**
4971       * Appends a string representation of this LDAP connection to the provided
4972       * buffer.
4973       *
4974       * @param  buffer  The buffer to which to append a string representation of
4975       *                 this LDAP connection.
4976       */
4977      public void toString(final StringBuilder buffer)
4978      {
4979        buffer.append("LDAPConnection(");
4980    
4981        final String name     = connectionName;
4982        final String poolName = connectionPoolName;
4983        if (name != null)
4984        {
4985          buffer.append("name='");
4986          buffer.append(name);
4987          buffer.append("', ");
4988        }
4989        else if (poolName != null)
4990        {
4991          buffer.append("poolName='");
4992          buffer.append(poolName);
4993          buffer.append("', ");
4994        }
4995    
4996        final LDAPConnectionInternals internals = connectionInternals;
4997        if ((internals != null) && internals.isConnected())
4998        {
4999          buffer.append("connected to ");
5000          buffer.append(internals.getHost());
5001          buffer.append(':');
5002          buffer.append(internals.getPort());
5003        }
5004        else
5005        {
5006          buffer.append("not connected");
5007        }
5008    
5009        buffer.append(')');
5010      }
5011    }