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