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