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