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