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