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