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