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