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