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