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