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