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