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