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