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