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