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