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