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.io.Closeable;
026 import java.net.InetAddress;
027 import java.net.Socket;
028 import java.util.Collection;
029 import java.util.HashMap;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.Timer;
033 import java.util.concurrent.atomic.AtomicBoolean;
034 import java.util.concurrent.atomic.AtomicLong;
035 import java.util.concurrent.atomic.AtomicReference;
036 import java.util.logging.Level;
037 import javax.net.SocketFactory;
038 import javax.net.ssl.SSLSession;
039 import javax.net.ssl.SSLSocket;
040 import javax.net.ssl.SSLSocketFactory;
041 import javax.security.sasl.SaslClient;
042
043 import com.unboundid.asn1.ASN1OctetString;
044 import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
045 import com.unboundid.ldap.protocol.LDAPMessage;
046 import com.unboundid.ldap.protocol.LDAPResponse;
047 import com.unboundid.ldap.protocol.UnbindRequestProtocolOp;
048 import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
049 import com.unboundid.ldap.sdk.schema.Schema;
050 import com.unboundid.ldif.LDIFException;
051 import com.unboundid.util.DebugType;
052 import com.unboundid.util.SynchronizedSocketFactory;
053 import com.unboundid.util.SynchronizedSSLSocketFactory;
054 import com.unboundid.util.WeakHashSet;
055
056 import static com.unboundid.ldap.sdk.LDAPMessages.*;
057 import static com.unboundid.util.Debug.*;
058 import static com.unboundid.util.StaticUtils.*;
059 import static com.unboundid.util.Validator.*;
060
061
062
063 /**
064 * This class provides a facility for interacting with an LDAPv3 directory
065 * server. It provides a means of establishing a connection to the server,
066 * sending requests, and reading responses. See
067 * <A HREF="http://www.ietf.org/rfc/rfc4511.txt">RFC 4511</A> for the LDAPv3
068 * protocol specification and more information about the types of operations
069 * defined in LDAP.
070 * <BR><BR>
071 * <H2>Creating, Establishing, and Authenticating Connections</H2>
072 * An LDAP connection can be established either at the time that the object is
073 * created or as a separate step. Similarly, authentication can be performed on
074 * the connection at the time it is created, at the time it is established, or
075 * as a separate process. For example:
076 * <BR><BR>
077 * <PRE>
078 * // Create a new, unestablished connection. Then connect and perform a
079 * // simple bind as separate operations.
080 * LDAPConnection c = new LDAPConnection();
081 * c.connect(address, port);
082 * BindResult bindResult = c.bind(bindDN, password);
083 *
084 * // Create a new connection that is established at creation time, and then
085 * // authenticate separately using simple authentication.
086 * LDAPConnection c = new LDAPConnection(address, port);
087 * BindResult bindResult = c.bind(bindDN, password);
088 *
089 * // Create a new connection that is established and bound using simple
090 * // authentication all in one step.
091 * LDAPConnection c = new LDAPConnection(address, port, bindDN, password);
092 * </PRE>
093 * <BR><BR>
094 * When authentication is performed at the time that the connection is
095 * established, it is only possible to perform a simple bind and it is not
096 * possible to include controls in the bind request, nor is it possible to
097 * receive response controls if the bind was successful. Therefore, it is
098 * recommended that authentication be performed as a separate step if the server
099 * may return response controls even in the event of a successful authentication
100 * (e.g., a control that may indicate that the user's password will soon
101 * expire). See the {@code BindRequest} class for more information about
102 * authentication in the UnboundID LDAP SDK for Java.
103 * <BR><BR>
104 * By default, connections will use standard unencrypted network sockets.
105 * However, it may be desirable to create connections that use SSL/TLS to
106 * encrypt communication. This can be done by specifying a
107 * {@code javax.net.SocketFactory} that should be used to create the socket to
108 * use to communicate with the directory server. The
109 * {@code javax.net.ssl.SSLSocketFactory#getDefault} method or the
110 * {@code javax.net.ssl.SSLContext#getSocketFactory} method may be used to
111 * obtain a socket factory for performing SSL communication. See the
112 * <A HREF=
113 * "http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
114 * JSSE Reference Guide</A> for more information on using these classes.
115 * Alternately, you may use the {@code com.unboundid.util.ssl.SSLUtil} class to
116 * simplify the process.
117 * <BR><BR>
118 * Whenever the connection is no longer needed, it may be terminated using the
119 * {@code LDAPConnection#close} method.
120 * <BR><BR>
121 * <H2>Processing LDAP Operations</H2>
122 * This class provides a number of methods for processing the different types of
123 * operations. The types of operations that can be processed include:
124 * <UL>
125 * <LI>Abandon -- This may be used to request that the server stop processing
126 * on an operation that has been invoked asynchronously.</LI>
127 * <LI>Add -- This may be used to add a new entry to the directory
128 * server. See the {@code AddRequest} class for more information about
129 * processing add operations.</LI>
130 * <LI>Bind -- This may be used to authenticate to the directory server. See
131 * the {@code BindRequest} class for more information about processing
132 * bind operations.</LI>
133 * <LI>Compare -- This may be used to determine whether a specified entry has
134 * a given attribute value. See the {@code CompareRequest} class for more
135 * information about processing compare operations.</LI>
136 * <LI>Delete -- This may be used to remove an entry from the directory
137 * server. See the {@code DeleteRequest} class for more information about
138 * processing delete operations.</LI>
139 * <LI>Extended -- This may be used to process an operation which is not
140 * part of the core LDAP protocol but is a custom extension supported by
141 * the directory server. See the {@code ExtendedRequest} class for more
142 * information about processing extended operations.</LI>
143 * <LI>Modify -- This may be used to alter an entry in the directory
144 * server. See the {@code ModifyRequest} class for more information about
145 * processing modify operations.</LI>
146 * <LI>Modify DN -- This may be used to rename an entry or subtree and/or move
147 * that entry or subtree below a new parent in the directory server. See
148 * the {@code ModifyDNRequest} class for more information about processing
149 * modify DN operations.</LI>
150 * <LI>Search -- This may be used to retrieve a set of entries in the server
151 * that match a given set of criteria. See the {@code SearchRequest}
152 * class for more information about processing search operations.</LI>
153 * </UL>
154 * <BR><BR>
155 * Most of the methods in this class used to process operations operate in a
156 * synchronous manner. In these cases, the SDK will send a request to the
157 * server and wait for a response to arrive before returning to the caller. In
158 * these cases, the value returned will include the contents of that response,
159 * including the result code, diagnostic message, matched DN, referral URLs, and
160 * any controls that may have been included. However, it also possible to
161 * process operations asynchronously, in which case the SDK will return control
162 * back to the caller after the request has been sent to the server but before
163 * the response has been received. In this case, the SDK will return an
164 * {@code AsyncRequestID} object which may be used to later abandon or cancel
165 * that operation if necessary, and will notify the client when the response
166 * arrives via a listener interface.
167 * <BR><BR>
168 * This class is mostly threadsafe. It is possible to process multiple
169 * concurrent operations over the same connection as long as the methods being
170 * invoked will not change the state of the connection in a way that might
171 * impact other operations in progress in unexpected ways. In particular, the
172 * following should not be attempted while any other operations may be in
173 * progress on this connection:
174 * <UL>
175 * <LI>
176 * Using one of the {@code connect} methods to re-establish the connection.
177 * </LI>
178 * <LI>
179 * Using one of the {@code close} methods to terminate the connection.
180 * </LI>
181 * <LI>
182 * Using one of the {@code bind} methods to attempt to authenticate the
183 * connection (unless you are certain that the bind will not impact the
184 * identity of the associated connection, for example by including the
185 * retain identity request control in the bind request if using the
186 * Commercial Edition of the LDAP SDK in conjunction with a Ping Identity,
187 * UnboundID, or Alcatel-Lucent 8661 Directory Server).
188 * </LI>
189 * <LI>
190 * Attempting to make a change to the way that the underlying communication
191 * is processed (e.g., by using the StartTLS extended operation to convert
192 * an insecure connection into a secure one).
193 * </LI>
194 * </UL>
195 */
196 public final class LDAPConnection
197 implements LDAPInterface, ReferralConnector, Closeable
198 {
199 /**
200 * The counter that will be used when assigning connection IDs to connections.
201 */
202 private static final AtomicLong NEXT_CONNECTION_ID = new AtomicLong(0L);
203
204
205
206 /**
207 * The default socket factory that will be used if no alternate factory is
208 * provided.
209 */
210 private static final SocketFactory DEFAULT_SOCKET_FACTORY =
211 SocketFactory.getDefault();
212
213
214
215 /**
216 * A set of weak references to schema objects that can be shared across
217 * connections if they are identical.
218 */
219 private static final WeakHashSet<Schema> SCHEMA_SET =
220 new WeakHashSet<Schema>();
221
222
223
224 // The connection pool with which this connection is associated, if
225 // applicable.
226 private AbstractConnectionPool connectionPool;
227
228 // Indicates whether to perform a reconnect before the next write.
229 private final AtomicBoolean needsReconnect;
230
231 // The disconnect information for this connection.
232 private final AtomicReference<DisconnectInfo> disconnectInfo;
233
234 // The last successful bind request processed on this connection.
235 private volatile BindRequest lastBindRequest;
236
237 // Indicates whether a request has been made to close this connection.
238 private volatile boolean closeRequested;
239
240 // Indicates whether an unbind request has been sent over this connection.
241 private volatile boolean unbindRequestSent;
242
243 // The extended request used to initiate StartTLS on this connection.
244 private volatile ExtendedRequest startTLSRequest;
245
246 // The port of the server to which a connection should be re-established.
247 private int reconnectPort = -1;
248
249 // The connection internals used to actually perform the network
250 // communication.
251 private volatile LDAPConnectionInternals connectionInternals;
252
253 // The set of connection options for this connection.
254 private LDAPConnectionOptions connectionOptions;
255
256 // The set of statistics for this connection.
257 private final LDAPConnectionStatistics connectionStatistics;
258
259 // The unique identifier assigned to this connection when it was created. It
260 // will not change over the life of the connection, even if the connection is
261 // closed and re-established (or even re-established to a different server).
262 private final long connectionID;
263
264 // The time of the last rebind attempt.
265 private long lastReconnectTime;
266
267 // The most recent time that an LDAP message was sent or received on this
268 // connection.
269 private volatile long lastCommunicationTime;
270
271 // A map in which arbitrary attachments may be stored or managed.
272 private Map<String,Object> attachments;
273
274 // The referral connector that will be used to establish connections to remote
275 // servers when following a referral.
276 private volatile ReferralConnector referralConnector;
277
278 // The cached schema read from the server.
279 private volatile Schema cachedSchema;
280
281 // The socket factory used for the last connection attempt.
282 private SocketFactory lastUsedSocketFactory;
283
284 // The socket factory used to create sockets for subsequent connection
285 // attempts.
286 private volatile SocketFactory socketFactory;
287
288 // A stack trace of the thread that last established this connection.
289 private StackTraceElement[] connectStackTrace;
290
291 // The user-friendly name assigned to this connection.
292 private String connectionName;
293
294 // The user-friendly name assigned to the connection pool with which this
295 // connection is associated.
296 private String connectionPoolName;
297
298 // A string representation of the host and port to which the last connection
299 // attempt (whether successful or not, and whether it is still established)
300 // was made.
301 private String hostPort;
302
303 // The address of the server to which a connection should be re-established.
304 private String reconnectAddress;
305
306 // A timer that may be used to enforce timeouts for asynchronous operations.
307 private Timer timer;
308
309
310
311 /**
312 * Creates a new LDAP connection using the default socket factory and default
313 * set of connection options. No actual network connection will be
314 * established.
315 */
316 public LDAPConnection()
317 {
318 this(null, null);
319 }
320
321
322
323 /**
324 * Creates a new LDAP connection using the default socket factory and provided
325 * set of connection options. No actual network connection will be
326 * established.
327 *
328 * @param connectionOptions The set of connection options to use for this
329 * connection. If it is {@code null}, then a
330 * default set of options will be used.
331 */
332 public LDAPConnection(final LDAPConnectionOptions connectionOptions)
333 {
334 this(null, connectionOptions);
335 }
336
337
338
339 /**
340 * Creates a new LDAP connection using the specified socket factory. No
341 * actual network connection will be established.
342 *
343 * @param socketFactory The socket factory to use when establishing
344 * connections. If it is {@code null}, then a default
345 * socket factory will be used.
346 */
347 public LDAPConnection(final SocketFactory socketFactory)
348 {
349 this(socketFactory, null);
350 }
351
352
353
354 /**
355 * Creates a new LDAP connection using the specified socket factory. No
356 * actual network connection will be established.
357 *
358 * @param socketFactory The socket factory to use when establishing
359 * connections. If it is {@code null}, then a
360 * default socket factory will be used.
361 * @param connectionOptions The set of connection options to use for this
362 * connection. If it is {@code null}, then a
363 * default set of options will be used.
364 */
365 public LDAPConnection(final SocketFactory socketFactory,
366 final LDAPConnectionOptions connectionOptions)
367 {
368 needsReconnect = new AtomicBoolean(false);
369 disconnectInfo = new AtomicReference<DisconnectInfo>();
370 lastCommunicationTime = -1L;
371
372 connectionID = NEXT_CONNECTION_ID.getAndIncrement();
373
374 if (connectionOptions == null)
375 {
376 this.connectionOptions = new LDAPConnectionOptions();
377 }
378 else
379 {
380 this.connectionOptions = connectionOptions.duplicate();
381 }
382
383 final SocketFactory f;
384 if (socketFactory == null)
385 {
386 f = DEFAULT_SOCKET_FACTORY;
387 }
388 else
389 {
390 f = socketFactory;
391 }
392
393 if (this.connectionOptions.allowConcurrentSocketFactoryUse())
394 {
395 this.socketFactory = f;
396 }
397 else
398 {
399 if (f instanceof SSLSocketFactory)
400 {
401 this.socketFactory =
402 new SynchronizedSSLSocketFactory((SSLSocketFactory) f);
403 }
404 else
405 {
406 this.socketFactory = new SynchronizedSocketFactory(f);
407 }
408 }
409
410 attachments = null;
411 connectionStatistics = new LDAPConnectionStatistics();
412 connectionName = null;
413 connectionPoolName = null;
414 cachedSchema = null;
415 timer = null;
416
417 referralConnector = this.connectionOptions.getReferralConnector();
418 if (referralConnector == null)
419 {
420 referralConnector = this;
421 }
422 }
423
424
425
426 /**
427 * Creates a new, unauthenticated LDAP connection that is established to the
428 * specified server.
429 *
430 * @param host The string representation of the address of the server to
431 * which the connection should be established. It may be a
432 * resolvable name or an IP address. It must not be
433 * {@code null}.
434 * @param port The port number of the server to which the connection should
435 * be established. It should be a value between 1 and 65535,
436 * inclusive.
437 *
438 * @throws LDAPException If a problem occurs while attempting to connect to
439 * the specified server.
440 */
441 public LDAPConnection(final String host, final int port)
442 throws LDAPException
443 {
444 this(null, null, host, port);
445 }
446
447
448
449 /**
450 * Creates a new, unauthenticated LDAP connection that is established to the
451 * specified server.
452 *
453 * @param connectionOptions The set of connection options to use for this
454 * connection. If it is {@code null}, then a
455 * default set of options will be used.
456 * @param host The string representation of the address of the
457 * server to which the connection should be
458 * established. It may be a resolvable name or an
459 * IP address. It must not be {@code null}.
460 * @param port The port number of the server to which the
461 * connection should be established. It should be
462 * a value between 1 and 65535, inclusive.
463 *
464 * @throws LDAPException If a problem occurs while attempting to connect to
465 * the specified server.
466 */
467 public LDAPConnection(final LDAPConnectionOptions connectionOptions,
468 final String host, final int port)
469 throws LDAPException
470 {
471 this(null, connectionOptions, host, port);
472 }
473
474
475
476 /**
477 * Creates a new, unauthenticated LDAP connection that is established to the
478 * specified server.
479 *
480 * @param socketFactory The socket factory to use when establishing
481 * connections. If it is {@code null}, then a default
482 * socket factory will be used.
483 * @param host The string representation of the address of the
484 * server to which the connection should be
485 * established. It may be a resolvable name or an IP
486 * address. It must not be {@code null}.
487 * @param port The port number of the server to which the
488 * connection should be established. It should be a
489 * value between 1 and 65535, inclusive.
490 *
491 * @throws LDAPException If a problem occurs while attempting to connect to
492 * the specified server.
493 */
494 public LDAPConnection(final SocketFactory socketFactory, final String host,
495 final int port)
496 throws LDAPException
497 {
498 this(socketFactory, null, host, port);
499 }
500
501
502
503 /**
504 * Creates a new, unauthenticated LDAP connection that is established to the
505 * specified server.
506 *
507 * @param socketFactory The socket factory to use when establishing
508 * connections. If it is {@code null}, then a
509 * default socket factory will be used.
510 * @param connectionOptions The set of connection options to use for this
511 * connection. If it is {@code null}, then a
512 * default set of options will be used.
513 * @param host The string representation of the address of the
514 * server to which the connection should be
515 * established. It may be a resolvable name or an
516 * IP address. It must not be {@code null}.
517 * @param port The port number of the server to which the
518 * connection should be established. It should be
519 * a value between 1 and 65535, inclusive.
520 *
521 * @throws LDAPException If a problem occurs while attempting to connect to
522 * the specified server.
523 */
524 public LDAPConnection(final SocketFactory socketFactory,
525 final LDAPConnectionOptions connectionOptions,
526 final String host, final int port)
527 throws LDAPException
528 {
529 this(socketFactory, connectionOptions);
530
531 connect(host, port);
532 }
533
534
535
536 /**
537 * Creates a new LDAP connection that is established to the specified server
538 * and is authenticated as the specified user (via LDAP simple
539 * authentication).
540 *
541 * @param host The string representation of the address of the
542 * server to which the connection should be established.
543 * It may be a resolvable name or an IP address. It
544 * must not be {@code null}.
545 * @param port The port number of the server to which the
546 * connection should be established. It should be a
547 * value between 1 and 65535, inclusive.
548 * @param bindDN The DN to use to authenticate to the directory
549 * server.
550 * @param bindPassword The password to use to authenticate to the directory
551 * server.
552 *
553 * @throws LDAPException If a problem occurs while attempting to connect to
554 * the specified server.
555 */
556 public LDAPConnection(final String host, final int port, final String bindDN,
557 final String bindPassword)
558 throws LDAPException
559 {
560 this(null, null, host, port, bindDN, bindPassword);
561 }
562
563
564
565 /**
566 * Creates a new LDAP connection that is established to the specified server
567 * and is authenticated as the specified user (via LDAP simple
568 * authentication).
569 *
570 * @param connectionOptions The set of connection options to use for this
571 * connection. If it is {@code null}, then a
572 * default set of options will be used.
573 * @param host The string representation of the address of the
574 * server to which the connection should be
575 * established. It may be a resolvable name or an
576 * IP address. It must not be {@code null}.
577 * @param port The port number of the server to which the
578 * connection should be established. It should be
579 * a value between 1 and 65535, inclusive.
580 * @param bindDN The DN to use to authenticate to the directory
581 * server.
582 * @param bindPassword The password to use to authenticate to the
583 * directory server.
584 *
585 * @throws LDAPException If a problem occurs while attempting to connect to
586 * the specified server.
587 */
588 public LDAPConnection(final LDAPConnectionOptions connectionOptions,
589 final String host, final int port, final String bindDN,
590 final String bindPassword)
591 throws LDAPException
592 {
593 this(null, connectionOptions, host, port, bindDN, bindPassword);
594 }
595
596
597
598 /**
599 * Creates a new LDAP connection that is established to the specified server
600 * and is authenticated as the specified user (via LDAP simple
601 * authentication).
602 *
603 * @param socketFactory The socket factory to use when establishing
604 * connections. If it is {@code null}, then a default
605 * socket factory will be used.
606 * @param host The string representation of the address of the
607 * server to which the connection should be
608 * established. It may be a resolvable name or an IP
609 * address. It must not be {@code null}.
610 * @param port The port number of the server to which the
611 * connection should be established. It should be a
612 * value between 1 and 65535, inclusive.
613 * @param bindDN The DN to use to authenticate to the directory
614 * server.
615 * @param bindPassword The password to use to authenticate to the directory
616 * server.
617 *
618 * @throws LDAPException If a problem occurs while attempting to connect to
619 * the specified server.
620 */
621 public LDAPConnection(final SocketFactory socketFactory, final String host,
622 final int port, final String bindDN,
623 final String bindPassword)
624 throws LDAPException
625 {
626 this(socketFactory, null, host, port, bindDN, bindPassword);
627 }
628
629
630
631 /**
632 * Creates a new LDAP connection that is established to the specified server
633 * and is authenticated as the specified user (via LDAP simple
634 * authentication).
635 *
636 * @param socketFactory The socket factory to use when establishing
637 * connections. If it is {@code null}, then a
638 * default socket factory will be used.
639 * @param connectionOptions The set of connection options to use for this
640 * connection. If it is {@code null}, then a
641 * default set of options will be used.
642 * @param host The string representation of the address of the
643 * server to which the connection should be
644 * established. It may be a resolvable name or an
645 * IP address. It must not be {@code null}.
646 * @param port The port number of the server to which the
647 * connection should be established. It should be
648 * a value between 1 and 65535, inclusive.
649 * @param bindDN The DN to use to authenticate to the directory
650 * server.
651 * @param bindPassword The password to use to authenticate to the
652 * directory server.
653 *
654 * @throws LDAPException If a problem occurs while attempting to connect to
655 * the specified server.
656 */
657 public LDAPConnection(final SocketFactory socketFactory,
658 final LDAPConnectionOptions connectionOptions,
659 final String host, final int port, final String bindDN,
660 final String bindPassword)
661 throws LDAPException
662 {
663 this(socketFactory, connectionOptions, host, port);
664
665 try
666 {
667 bind(new SimpleBindRequest(bindDN, bindPassword));
668 }
669 catch (LDAPException le)
670 {
671 debugException(le);
672 setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
673 close();
674 throw le;
675 }
676 }
677
678
679
680 /**
681 * Establishes an unauthenticated connection to the directory server using the
682 * provided information. If the connection is already established, then it
683 * will be closed and re-established.
684 * <BR><BR>
685 * If this method is invoked while any operations are in progress on this
686 * connection, then the directory server may or may not abort processing for
687 * those operations, depending on the type of operation and how far along the
688 * server has already gotten while processing that operation. It is
689 * recommended that all active operations be abandoned, canceled, or allowed
690 * to complete before attempting to re-establish an active connection.
691 *
692 * @param host The string representation of the address of the server to
693 * which the connection should be established. It may be a
694 * resolvable name or an IP address. It must not be
695 * {@code null}.
696 * @param port The port number of the server to which the connection should
697 * be established. It should be a value between 1 and 65535,
698 * inclusive.
699 *
700 * @throws LDAPException If an error occurs while attempting to establish
701 * the connection.
702 */
703 public void connect(final String host, final int port)
704 throws LDAPException
705 {
706 connect(host, port, connectionOptions.getConnectTimeoutMillis());
707 }
708
709
710
711 /**
712 * Establishes an unauthenticated connection to the directory server using the
713 * provided information. If the connection is already established, then it
714 * will be closed and re-established.
715 * <BR><BR>
716 * If this method is invoked while any operations are in progress on this
717 * connection, then the directory server may or may not abort processing for
718 * those operations, depending on the type of operation and how far along the
719 * server has already gotten while processing that operation. It is
720 * recommended that all active operations be abandoned, canceled, or allowed
721 * to complete before attempting to re-establish an active connection.
722 *
723 * @param host The string representation of the address of the server to
724 * which the connection should be established. It may be a
725 * resolvable name or an IP address. It must not be
726 * {@code null}.
727 * @param port The port number of the server to which the connection
728 * should be established. It should be a value between 1 and
729 * 65535, inclusive.
730 * @param timeout The maximum length of time in milliseconds to wait for the
731 * connection to be established before failing, or zero to
732 * indicate that no timeout should be enforced (although if
733 * the attempt stalls long enough, then the underlying
734 * operating system may cause it to timeout).
735 *
736 * @throws LDAPException If an error occurs while attempting to establish
737 * the connection.
738 */
739 public void connect(final String host, final int port, final int timeout)
740 throws LDAPException
741 {
742 final InetAddress inetAddress;
743 try
744 {
745 inetAddress = InetAddress.getByName(host);
746 }
747 catch (final Exception e)
748 {
749 debugException(e);
750 throw new LDAPException(ResultCode.CONNECT_ERROR,
751 ERR_CONN_RESOLVE_ERROR.get(host, getExceptionMessage(e)),
752 e);
753 }
754
755 connect(host, inetAddress, port, timeout);
756 }
757
758
759
760 /**
761 * Establishes an unauthenticated connection to the directory server using the
762 * provided information. If the connection is already established, then it
763 * will be closed and re-established.
764 * <BR><BR>
765 * If this method is invoked while any operations are in progress on this
766 * connection, then the directory server may or may not abort processing for
767 * those operations, depending on the type of operation and how far along the
768 * server has already gotten while processing that operation. It is
769 * recommended that all active operations be abandoned, canceled, or allowed
770 * to complete before attempting to re-establish an active connection.
771 *
772 * @param inetAddress The inet address of the server to which the connection
773 * should be established. It must not be {@code null}.
774 * @param port The port number of the server to which the connection
775 * should be established. It should be a value between 1
776 * and 65535, inclusive.
777 * @param timeout The maximum length of time in milliseconds to wait for
778 * the connection to be established before failing, or
779 * zero to indicate that no timeout should be enforced
780 * (although if the attempt stalls long enough, then the
781 * underlying operating system may cause it to timeout).
782 *
783 * @throws LDAPException If an error occurs while attempting to establish
784 * the connection.
785 */
786 public void connect(final InetAddress inetAddress, final int port,
787 final int timeout)
788 throws LDAPException
789 {
790 connect(inetAddress.getHostName(), inetAddress, port, timeout);
791 }
792
793
794
795 /**
796 * Establishes an unauthenticated connection to the directory server using the
797 * provided information. If the connection is already established, then it
798 * will be closed and re-established.
799 * <BR><BR>
800 * If this method is invoked while any operations are in progress on this
801 * connection, then the directory server may or may not abort processing for
802 * those operations, depending on the type of operation and how far along the
803 * server has already gotten while processing that operation. It is
804 * recommended that all active operations be abandoned, canceled, or allowed
805 * to complete before attempting to re-establish an active connection.
806 *
807 * @param host The string representation of the address of the server
808 * to which the connection should be established. It may
809 * be a resolvable name or an IP address. It must not be
810 * {@code null}.
811 * @param inetAddress The inet address of the server to which the connection
812 * should be established. It must not be {@code null}.
813 * @param port The port number of the server to which the connection
814 * should be established. It should be a value between 1
815 * and 65535, inclusive.
816 * @param timeout The maximum length of time in milliseconds to wait for
817 * the connection to be established before failing, or
818 * zero to indicate that no timeout should be enforced
819 * (although if the attempt stalls long enough, then the
820 * underlying operating system may cause it to timeout).
821 *
822 * @throws LDAPException If an error occurs while attempting to establish
823 * the connection.
824 */
825 public void connect(final String host, final InetAddress inetAddress,
826 final int port, final int timeout)
827 throws LDAPException
828 {
829 ensureNotNull(host, inetAddress, port);
830
831 needsReconnect.set(false);
832 hostPort = host + ':' + port;
833 lastCommunicationTime = -1L;
834 startTLSRequest = null;
835
836 if (isConnected())
837 {
838 setDisconnectInfo(DisconnectType.RECONNECT, null, null);
839 close();
840 }
841
842 lastUsedSocketFactory = socketFactory;
843 reconnectAddress = host;
844 reconnectPort = port;
845 cachedSchema = null;
846 unbindRequestSent = false;
847
848 disconnectInfo.set(null);
849
850 try
851 {
852 connectionStatistics.incrementNumConnects();
853 connectionInternals = new LDAPConnectionInternals(this, connectionOptions,
854 lastUsedSocketFactory, host, inetAddress, port, timeout);
855 connectionInternals.startConnectionReader();
856 lastCommunicationTime = System.currentTimeMillis();
857 }
858 catch (Exception e)
859 {
860 debugException(e);
861 setDisconnectInfo(DisconnectType.LOCAL_ERROR, null, e);
862 connectionInternals = null;
863 throw new LDAPException(ResultCode.CONNECT_ERROR,
864 ERR_CONN_CONNECT_ERROR.get(getHostPort(), String.valueOf(e)), 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 a Ping Identity, UnboundID, or Alcatel-Lucent
2061 * 8661 Directory Server). It is recommended that all active operations be
2062 * abandoned, canceled, or allowed to complete before attempting to perform a
2063 * bind on an active connection.
2064 *
2065 * @param bindDN The bind DN for the bind operation.
2066 * @param password The password for the simple bind operation.
2067 *
2068 * @return The result of processing the bind operation.
2069 *
2070 * @throws LDAPException If the server rejects the bind request, or if a
2071 * problem occurs while sending the request or reading
2072 * the response.
2073 */
2074 public BindResult bind(final String bindDN, final String password)
2075 throws LDAPException
2076 {
2077 return bind(new SimpleBindRequest(bindDN, password));
2078 }
2079
2080
2081
2082 /**
2083 * Processes the provided bind request.
2084 * <BR><BR>
2085 * The LDAP protocol specification forbids clients from attempting to perform
2086 * a bind on a connection in which one or more other operations are already in
2087 * progress. If a bind is attempted while any operations are in progress,
2088 * then the directory server may or may not abort processing for those
2089 * operations, depending on the type of operation and how far along the
2090 * server has already gotten while processing that operation (unless the bind
2091 * request is one that will not cause the server to attempt to change the
2092 * identity of this connection, for example by including the retain identity
2093 * request control in the bind request if using the Commercial Edition of the
2094 * LDAP SDK in conjunction with a Ping Identity, UnboundID, or Alcatel-Lucent
2095 * 8661 Directory Server). It is recommended that all active operations be
2096 * abandoned, canceled, or allowed to complete before attempting to perform a
2097 * bind on an active connection.
2098 *
2099 * @param bindRequest The bind request to be processed. It must not be
2100 * {@code null}.
2101 *
2102 * @return The result of processing the bind operation.
2103 *
2104 * @throws LDAPException If the server rejects the bind request, or if a
2105 * problem occurs while sending the request or reading
2106 * the response.
2107 */
2108 public BindResult bind(final BindRequest bindRequest)
2109 throws LDAPException
2110 {
2111 ensureNotNull(bindRequest);
2112
2113 // We don't want to update the last bind request or update the cached
2114 // schema for this connection if it included the retain identity control.
2115 // However, that's only available in the Commercial Edition, so just
2116 // reference it by OID here.
2117 boolean hasRetainIdentityControl = false;
2118 for (final Control c : bindRequest.getControls())
2119 {
2120 if (c.getOID().equals("1.3.6.1.4.1.30221.2.5.3"))
2121 {
2122 hasRetainIdentityControl = true;
2123 break;
2124 }
2125 }
2126
2127 if (! hasRetainIdentityControl)
2128 {
2129 lastBindRequest = null;
2130 }
2131
2132 final BindResult bindResult = bindRequest.process(this, 1);
2133 if (bindResult.getResultCode().equals(ResultCode.SUCCESS))
2134 {
2135 if (! hasRetainIdentityControl)
2136 {
2137 lastBindRequest = bindRequest;
2138 if (connectionOptions.useSchema())
2139 {
2140 try
2141 {
2142 cachedSchema = getCachedSchema(this);
2143 }
2144 catch (Exception e)
2145 {
2146 debugException(e);
2147 }
2148 }
2149 }
2150
2151 return bindResult;
2152 }
2153
2154 if (bindResult.getResultCode().equals(ResultCode.SASL_BIND_IN_PROGRESS))
2155 {
2156 throw new SASLBindInProgressException(bindResult);
2157 }
2158 else
2159 {
2160 throw new LDAPBindException(bindResult);
2161 }
2162 }
2163
2164
2165
2166 /**
2167 * Processes a compare operation with the provided information.
2168 *
2169 * @param dn The DN of the entry in which to make the
2170 * comparison. It must not be {@code null}.
2171 * @param attributeName The attribute name for which to make the
2172 * comparison. It must not be {@code null}.
2173 * @param assertionValue The assertion value to verify in the target entry.
2174 * It must not be {@code null}.
2175 *
2176 * @return The result of processing the compare operation.
2177 *
2178 * @throws LDAPException If the server rejects the compare request, or if a
2179 * problem is encountered while sending the request or
2180 * reading the response.
2181 */
2182 public CompareResult compare(final String dn, final String attributeName,
2183 final String assertionValue)
2184 throws LDAPException
2185 {
2186 ensureNotNull(dn, attributeName, assertionValue);
2187
2188 return compare(new CompareRequest(dn, attributeName, assertionValue));
2189 }
2190
2191
2192
2193 /**
2194 * Processes the provided compare request.
2195 *
2196 * @param compareRequest The compare request to be processed. It must not
2197 * be {@code null}.
2198 *
2199 * @return The result of processing the compare operation.
2200 *
2201 * @throws LDAPException If the server rejects the compare request, or if a
2202 * problem is encountered while sending the request or
2203 * reading the response.
2204 */
2205 public CompareResult compare(final CompareRequest compareRequest)
2206 throws LDAPException
2207 {
2208 ensureNotNull(compareRequest);
2209
2210 final LDAPResult result = compareRequest.process(this, 1);
2211 switch (result.getResultCode().intValue())
2212 {
2213 case ResultCode.COMPARE_FALSE_INT_VALUE:
2214 case ResultCode.COMPARE_TRUE_INT_VALUE:
2215 return new CompareResult(result);
2216
2217 default:
2218 throw new LDAPException(result);
2219 }
2220 }
2221
2222
2223
2224 /**
2225 * Processes the provided compare request.
2226 *
2227 * @param compareRequest The compare request to be processed. It must not
2228 * be {@code null}.
2229 *
2230 * @return The result of processing the compare operation.
2231 *
2232 * @throws LDAPException If the server rejects the compare request, or if a
2233 * problem is encountered while sending the request or
2234 * reading the response.
2235 */
2236 public CompareResult compare(final ReadOnlyCompareRequest compareRequest)
2237 throws LDAPException
2238 {
2239 return compare((CompareRequest) compareRequest);
2240 }
2241
2242
2243
2244 /**
2245 * Processes the provided compare request as an asynchronous operation.
2246 *
2247 * @param compareRequest The compare request to be processed. It must not
2248 * be {@code null}.
2249 * @param resultListener The async result listener to use to handle the
2250 * response for the compare operation. It may be
2251 * {@code null} if the result is going to be obtained
2252 * from the returned {@code AsyncRequestID} object via
2253 * the {@code Future} API.
2254 *
2255 * @return An async request ID that may be used to reference the operation.
2256 *
2257 * @throws LDAPException If a problem occurs while sending the request.
2258 */
2259 public AsyncRequestID asyncCompare(final CompareRequest compareRequest,
2260 final AsyncCompareResultListener resultListener)
2261 throws LDAPException
2262 {
2263 ensureNotNull(compareRequest);
2264
2265 if (synchronousMode())
2266 {
2267 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2268 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2269 }
2270
2271 final AsyncCompareResultListener listener;
2272 if (resultListener == null)
2273 {
2274 listener = DiscardAsyncListener.getInstance();
2275 }
2276 else
2277 {
2278 listener = resultListener;
2279 }
2280
2281 return compareRequest.processAsync(this, listener);
2282 }
2283
2284
2285
2286 /**
2287 * Processes the provided compare request as an asynchronous operation.
2288 *
2289 * @param compareRequest The compare request to be processed. It must not
2290 * be {@code null}.
2291 * @param resultListener The async result listener to use to handle the
2292 * response for the compare operation. It may be
2293 * {@code null} if the result is going to be obtained
2294 * from the returned {@code AsyncRequestID} object via
2295 * the {@code Future} API.
2296 *
2297 * @return An async request ID that may be used to reference the operation.
2298 *
2299 * @throws LDAPException If a problem occurs while sending the request.
2300 */
2301 public AsyncRequestID asyncCompare(
2302 final ReadOnlyCompareRequest compareRequest,
2303 final AsyncCompareResultListener resultListener)
2304 throws LDAPException
2305 {
2306 if (synchronousMode())
2307 {
2308 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2309 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2310 }
2311
2312 return asyncCompare((CompareRequest) compareRequest, resultListener);
2313 }
2314
2315
2316
2317 /**
2318 * Deletes the entry with the specified DN.
2319 *
2320 * @param dn The DN of the entry to delete. It must not be {@code null}.
2321 *
2322 * @return The result of processing the delete operation.
2323 *
2324 * @throws LDAPException If the server rejects the delete request, or if a
2325 * problem is encountered while sending the request or
2326 * reading the response.
2327 */
2328 public LDAPResult delete(final String dn)
2329 throws LDAPException
2330 {
2331 return delete(new DeleteRequest(dn));
2332 }
2333
2334
2335
2336 /**
2337 * Processes the provided delete request.
2338 *
2339 * @param deleteRequest The delete request to be processed. It must not be
2340 * {@code null}.
2341 *
2342 * @return The result of processing the delete operation.
2343 *
2344 * @throws LDAPException If the server rejects the delete request, or if a
2345 * problem is encountered while sending the request or
2346 * reading the response.
2347 */
2348 public LDAPResult delete(final DeleteRequest deleteRequest)
2349 throws LDAPException
2350 {
2351 ensureNotNull(deleteRequest);
2352
2353 final LDAPResult ldapResult = deleteRequest.process(this, 1);
2354
2355 switch (ldapResult.getResultCode().intValue())
2356 {
2357 case ResultCode.SUCCESS_INT_VALUE:
2358 case ResultCode.NO_OPERATION_INT_VALUE:
2359 return ldapResult;
2360
2361 default:
2362 throw new LDAPException(ldapResult);
2363 }
2364 }
2365
2366
2367
2368 /**
2369 * Processes the provided delete request.
2370 *
2371 * @param deleteRequest The delete request to be processed. It must not be
2372 * {@code null}.
2373 *
2374 * @return The result of processing the delete operation.
2375 *
2376 * @throws LDAPException If the server rejects the delete request, or if a
2377 * problem is encountered while sending the request or
2378 * reading the response.
2379 */
2380 public LDAPResult delete(final ReadOnlyDeleteRequest deleteRequest)
2381 throws LDAPException
2382 {
2383 return delete((DeleteRequest) deleteRequest);
2384 }
2385
2386
2387
2388 /**
2389 * Processes the provided delete request as an asynchronous operation.
2390 *
2391 * @param deleteRequest The delete request to be processed. It must not be
2392 * {@code null}.
2393 * @param resultListener The async result listener to use to handle the
2394 * response for the delete operation. It may be
2395 * {@code null} if the result is going to be obtained
2396 * from the returned {@code AsyncRequestID} object via
2397 * the {@code Future} API.
2398 *
2399 * @return An async request ID that may be used to reference the operation.
2400 *
2401 * @throws LDAPException If a problem occurs while sending the request.
2402 */
2403 public AsyncRequestID asyncDelete(final DeleteRequest deleteRequest,
2404 final AsyncResultListener resultListener)
2405 throws LDAPException
2406 {
2407 ensureNotNull(deleteRequest);
2408
2409 if (synchronousMode())
2410 {
2411 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2412 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2413 }
2414
2415 final AsyncResultListener listener;
2416 if (resultListener == null)
2417 {
2418 listener = DiscardAsyncListener.getInstance();
2419 }
2420 else
2421 {
2422 listener = resultListener;
2423 }
2424
2425 return deleteRequest.processAsync(this, listener);
2426 }
2427
2428
2429
2430 /**
2431 * Processes the provided delete request as an asynchronous operation.
2432 *
2433 * @param deleteRequest The delete request to be processed. It must not be
2434 * {@code null}.
2435 * @param resultListener The async result listener to use to handle the
2436 * response for the delete operation. It may be
2437 * {@code null} if the result is going to be obtained
2438 * from the returned {@code AsyncRequestID} object via
2439 * the {@code Future} API.
2440 *
2441 * @return An async request ID that may be used to reference the operation.
2442 *
2443 * @throws LDAPException If a problem occurs while sending the request.
2444 */
2445 public AsyncRequestID asyncDelete(final ReadOnlyDeleteRequest deleteRequest,
2446 final AsyncResultListener resultListener)
2447 throws LDAPException
2448 {
2449 if (synchronousMode())
2450 {
2451 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2452 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2453 }
2454
2455 return asyncDelete((DeleteRequest) deleteRequest, resultListener);
2456 }
2457
2458
2459
2460 /**
2461 * Processes an extended request with the provided request OID. Note that
2462 * because some types of extended operations return unusual result codes under
2463 * "normal" conditions, the server may not always throw an exception for a
2464 * failed extended operation like it does for other types of operations. It
2465 * will throw an exception under conditions where there appears to be a
2466 * problem with the connection or the server to which the connection is
2467 * established, but there may be many circumstances in which an extended
2468 * operation is not processed correctly but this method does not throw an
2469 * exception. In the event that no exception is thrown, it is the
2470 * responsibility of the caller to interpret the result to determine whether
2471 * the operation was processed as expected.
2472 * <BR><BR>
2473 * Note that extended operations which may change the state of this connection
2474 * (e.g., the StartTLS extended operation, which will add encryption to a
2475 * previously-unencrypted connection) should not be invoked while any other
2476 * operations are active on the connection. It is recommended that all active
2477 * operations be abandoned, canceled, or allowed to complete before attempting
2478 * to process an extended operation that may change the state of this
2479 * connection.
2480 *
2481 * @param requestOID The OID for the extended request to process. It must
2482 * not be {@code null}.
2483 *
2484 * @return The extended result object that provides information about the
2485 * result of the request processing. It may or may not indicate that
2486 * the operation was successful.
2487 *
2488 * @throws LDAPException If a problem occurs while sending the request or
2489 * reading the response.
2490 */
2491 public ExtendedResult processExtendedOperation(final String requestOID)
2492 throws LDAPException
2493 {
2494 ensureNotNull(requestOID);
2495
2496 return processExtendedOperation(new ExtendedRequest(requestOID));
2497 }
2498
2499
2500
2501 /**
2502 * Processes an extended request with the provided request OID and value.
2503 * Note that because some types of extended operations return unusual result
2504 * codes under "normal" conditions, the server may not always throw an
2505 * exception for a failed extended operation like it does for other types of
2506 * operations. It will throw an exception under conditions where there
2507 * appears to be a problem with the connection or the server to which the
2508 * connection is established, but there may be many circumstances in which an
2509 * extended operation is not processed correctly but this method does not
2510 * throw an exception. In the event that no exception is thrown, it is the
2511 * responsibility of the caller to interpret the result to determine whether
2512 * the operation was processed as expected.
2513 * <BR><BR>
2514 * Note that extended operations which may change the state of this connection
2515 * (e.g., the StartTLS extended operation, which will add encryption to a
2516 * previously-unencrypted connection) should not be invoked while any other
2517 * operations are active on the connection. It is recommended that all active
2518 * operations be abandoned, canceled, or allowed to complete before attempting
2519 * to process an extended operation that may change the state of this
2520 * connection.
2521 *
2522 * @param requestOID The OID for the extended request to process. It must
2523 * not be {@code null}.
2524 * @param requestValue The encoded value for the extended request to
2525 * process. It may be {@code null} if there does not
2526 * need to be a value for the requested operation.
2527 *
2528 * @return The extended result object that provides information about the
2529 * result of the request processing. It may or may not indicate that
2530 * the operation was successful.
2531 *
2532 * @throws LDAPException If a problem occurs while sending the request or
2533 * reading the response.
2534 */
2535 public ExtendedResult processExtendedOperation(final String requestOID,
2536 final ASN1OctetString requestValue)
2537 throws LDAPException
2538 {
2539 ensureNotNull(requestOID);
2540
2541 return processExtendedOperation(new ExtendedRequest(requestOID,
2542 requestValue));
2543 }
2544
2545
2546
2547 /**
2548 * Processes the provided extended request. Note that because some types of
2549 * extended operations return unusual result codes under "normal" conditions,
2550 * the server may not always throw an exception for a failed extended
2551 * operation like it does for other types of operations. It will throw an
2552 * exception under conditions where there appears to be a problem with the
2553 * connection or the server to which the connection is established, but there
2554 * may be many circumstances in which an extended operation is not processed
2555 * correctly but this method does not throw an exception. In the event that
2556 * no exception is thrown, it is the responsibility of the caller to interpret
2557 * the result to determine whether the operation was processed as expected.
2558 * <BR><BR>
2559 * Note that extended operations which may change the state of this connection
2560 * (e.g., the StartTLS extended operation, which will add encryption to a
2561 * previously-unencrypted connection) should not be invoked while any other
2562 * operations are active on the connection. It is recommended that all active
2563 * operations be abandoned, canceled, or allowed to complete before attempting
2564 * to process an extended operation that may change the state of this
2565 * connection.
2566 *
2567 * @param extendedRequest The extended request to be processed. It must not
2568 * be {@code null}.
2569 *
2570 * @return The extended result object that provides information about the
2571 * result of the request processing. It may or may not indicate that
2572 * the operation was successful.
2573 *
2574 * @throws LDAPException If a problem occurs while sending the request or
2575 * reading the response.
2576 */
2577 public ExtendedResult processExtendedOperation(
2578 final ExtendedRequest extendedRequest)
2579 throws LDAPException
2580 {
2581 ensureNotNull(extendedRequest);
2582
2583 final ExtendedResult extendedResult = extendedRequest.process(this, 1);
2584
2585 if ((extendedResult.getOID() == null) &&
2586 (extendedResult.getValue() == null))
2587 {
2588 switch (extendedResult.getResultCode().intValue())
2589 {
2590 case ResultCode.OPERATIONS_ERROR_INT_VALUE:
2591 case ResultCode.PROTOCOL_ERROR_INT_VALUE:
2592 case ResultCode.BUSY_INT_VALUE:
2593 case ResultCode.UNAVAILABLE_INT_VALUE:
2594 case ResultCode.OTHER_INT_VALUE:
2595 case ResultCode.SERVER_DOWN_INT_VALUE:
2596 case ResultCode.LOCAL_ERROR_INT_VALUE:
2597 case ResultCode.ENCODING_ERROR_INT_VALUE:
2598 case ResultCode.DECODING_ERROR_INT_VALUE:
2599 case ResultCode.TIMEOUT_INT_VALUE:
2600 case ResultCode.NO_MEMORY_INT_VALUE:
2601 case ResultCode.CONNECT_ERROR_INT_VALUE:
2602 throw new LDAPException(extendedResult);
2603 }
2604 }
2605
2606 if ((extendedResult.getResultCode() == ResultCode.SUCCESS) &&
2607 extendedRequest.getOID().equals(
2608 StartTLSExtendedRequest.STARTTLS_REQUEST_OID))
2609 {
2610 startTLSRequest = extendedRequest.duplicate();
2611 }
2612
2613 return extendedResult;
2614 }
2615
2616
2617
2618 /**
2619 * Applies the provided modification to the specified entry.
2620 *
2621 * @param dn The DN of the entry to modify. It must not be {@code null}.
2622 * @param mod The modification to apply to the target entry. It must not
2623 * be {@code null}.
2624 *
2625 * @return The result of processing the modify operation.
2626 *
2627 * @throws LDAPException If the server rejects the modify request, or if a
2628 * problem is encountered while sending the request or
2629 * reading the response.
2630 */
2631 public LDAPResult modify(final String dn, final Modification mod)
2632 throws LDAPException
2633 {
2634 ensureNotNull(dn, mod);
2635
2636 return modify(new ModifyRequest(dn, mod));
2637 }
2638
2639
2640
2641 /**
2642 * Applies the provided set of modifications to the specified entry.
2643 *
2644 * @param dn The DN of the entry to modify. It must not be {@code null}.
2645 * @param mods The set of modifications to apply to the target entry. It
2646 * must not be {@code null} or empty. *
2647 * @return The result of processing the modify operation.
2648 *
2649 * @throws LDAPException If the server rejects the modify request, or if a
2650 * problem is encountered while sending the request or
2651 * reading the response.
2652 */
2653 public LDAPResult modify(final String dn, final Modification... mods)
2654 throws LDAPException
2655 {
2656 ensureNotNull(dn, mods);
2657
2658 return modify(new ModifyRequest(dn, mods));
2659 }
2660
2661
2662
2663 /**
2664 * Applies the provided set of modifications to the specified entry.
2665 *
2666 * @param dn The DN of the entry to modify. It must not be {@code null}.
2667 * @param mods The set of modifications to apply to the target entry. It
2668 * must not be {@code null} or empty.
2669 *
2670 * @return The result of processing the modify operation.
2671 *
2672 * @throws LDAPException If the server rejects the modify request, or if a
2673 * problem is encountered while sending the request or
2674 * reading the response.
2675 */
2676 public LDAPResult modify(final String dn, final List<Modification> mods)
2677 throws LDAPException
2678 {
2679 ensureNotNull(dn, mods);
2680
2681 return modify(new ModifyRequest(dn, mods));
2682 }
2683
2684
2685
2686 /**
2687 * Processes a modify request from the provided LDIF representation of the
2688 * changes.
2689 *
2690 * @param ldifModificationLines The lines that comprise an LDIF
2691 * representation of a modify change record.
2692 * It must not be {@code null} or empty.
2693 *
2694 * @return The result of processing the modify operation.
2695 *
2696 * @throws LDIFException If the provided set of lines cannot be parsed as an
2697 * LDIF modify change record.
2698 *
2699 * @throws LDAPException If the server rejects the modify request, or if a
2700 * problem is encountered while sending the request or
2701 * reading the response.
2702 *
2703 */
2704 public LDAPResult modify(final String... ldifModificationLines)
2705 throws LDIFException, LDAPException
2706 {
2707 ensureNotNull(ldifModificationLines);
2708
2709 return modify(new ModifyRequest(ldifModificationLines));
2710 }
2711
2712
2713
2714 /**
2715 * Processes the provided modify request.
2716 *
2717 * @param modifyRequest The modify request to be processed. It must not be
2718 * {@code null}.
2719 *
2720 * @return The result of processing the modify operation.
2721 *
2722 * @throws LDAPException If the server rejects the modify request, or if a
2723 * problem is encountered while sending the request or
2724 * reading the response.
2725 */
2726 public LDAPResult modify(final ModifyRequest modifyRequest)
2727 throws LDAPException
2728 {
2729 ensureNotNull(modifyRequest);
2730
2731 final LDAPResult ldapResult = modifyRequest.process(this, 1);
2732
2733 switch (ldapResult.getResultCode().intValue())
2734 {
2735 case ResultCode.SUCCESS_INT_VALUE:
2736 case ResultCode.NO_OPERATION_INT_VALUE:
2737 return ldapResult;
2738
2739 default:
2740 throw new LDAPException(ldapResult);
2741 }
2742 }
2743
2744
2745
2746 /**
2747 * Processes the provided modify request.
2748 *
2749 * @param modifyRequest The modify request to be processed. It must not be
2750 * {@code null}.
2751 *
2752 * @return The result of processing the modify operation.
2753 *
2754 * @throws LDAPException If the server rejects the modify request, or if a
2755 * problem is encountered while sending the request or
2756 * reading the response.
2757 */
2758 public LDAPResult modify(final ReadOnlyModifyRequest modifyRequest)
2759 throws LDAPException
2760 {
2761 return modify((ModifyRequest) modifyRequest);
2762 }
2763
2764
2765
2766 /**
2767 * Processes the provided modify request as an asynchronous operation.
2768 *
2769 * @param modifyRequest The modify request to be processed. It must not be
2770 * {@code null}.
2771 * @param resultListener The async result listener to use to handle the
2772 * response for the modify operation. It may be
2773 * {@code null} if the result is going to be obtained
2774 * from the returned {@code AsyncRequestID} object via
2775 * the {@code Future} API.
2776 *
2777 * @return An async request ID that may be used to reference the operation.
2778 *
2779 * @throws LDAPException If a problem occurs while sending the request.
2780 */
2781 public AsyncRequestID asyncModify(final ModifyRequest modifyRequest,
2782 final AsyncResultListener resultListener)
2783 throws LDAPException
2784 {
2785 ensureNotNull(modifyRequest);
2786
2787 if (synchronousMode())
2788 {
2789 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2790 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2791 }
2792
2793 final AsyncResultListener listener;
2794 if (resultListener == null)
2795 {
2796 listener = DiscardAsyncListener.getInstance();
2797 }
2798 else
2799 {
2800 listener = resultListener;
2801 }
2802
2803 return modifyRequest.processAsync(this, listener);
2804 }
2805
2806
2807
2808 /**
2809 * Processes the provided modify request as an asynchronous operation.
2810 *
2811 * @param modifyRequest The modify request to be processed. It must not be
2812 * {@code null}.
2813 * @param resultListener The async result listener to use to handle the
2814 * response for the modify operation. It may be
2815 * {@code null} if the result is going to be obtained
2816 * from the returned {@code AsyncRequestID} object via
2817 * the {@code Future} API.
2818 *
2819 * @return An async request ID that may be used to reference the operation.
2820 *
2821 * @throws LDAPException If a problem occurs while sending the request.
2822 */
2823 public AsyncRequestID asyncModify(final ReadOnlyModifyRequest modifyRequest,
2824 final AsyncResultListener resultListener)
2825 throws LDAPException
2826 {
2827 if (synchronousMode())
2828 {
2829 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2830 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2831 }
2832
2833 return asyncModify((ModifyRequest) modifyRequest, resultListener);
2834 }
2835
2836
2837
2838 /**
2839 * Performs a modify DN operation with the provided information.
2840 *
2841 * @param dn The current DN for the entry to rename. It must not
2842 * be {@code null}.
2843 * @param newRDN The new RDN to use for the entry. It must not be
2844 * {@code null}.
2845 * @param deleteOldRDN Indicates whether to delete the current RDN value
2846 * from the entry.
2847 *
2848 * @return The result of processing the modify DN operation.
2849 *
2850 * @throws LDAPException If the server rejects the modify DN request, or if
2851 * a problem is encountered while sending the request
2852 * or reading the response.
2853 */
2854 public LDAPResult modifyDN(final String dn, final String newRDN,
2855 final boolean deleteOldRDN)
2856 throws LDAPException
2857 {
2858 ensureNotNull(dn, newRDN);
2859
2860 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
2861 }
2862
2863
2864
2865 /**
2866 * Performs a modify DN operation with the provided information.
2867 *
2868 * @param dn The current DN for the entry to rename. It must not
2869 * be {@code null}.
2870 * @param newRDN The new RDN to use for the entry. It must not be
2871 * {@code null}.
2872 * @param deleteOldRDN Indicates whether to delete the current RDN value
2873 * from the entry.
2874 * @param newSuperiorDN The new superior DN for the entry. It may be
2875 * {@code null} if the entry is not to be moved below a
2876 * new parent.
2877 *
2878 * @return The result of processing the modify DN operation.
2879 *
2880 * @throws LDAPException If the server rejects the modify DN request, or if
2881 * a problem is encountered while sending the request
2882 * or reading the response.
2883 */
2884 public LDAPResult modifyDN(final String dn, final String newRDN,
2885 final boolean deleteOldRDN,
2886 final String newSuperiorDN)
2887 throws LDAPException
2888 {
2889 ensureNotNull(dn, newRDN);
2890
2891 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
2892 newSuperiorDN));
2893 }
2894
2895
2896
2897 /**
2898 * Processes the provided modify DN request.
2899 *
2900 * @param modifyDNRequest The modify DN request to be processed. It must
2901 * not be {@code null}.
2902 *
2903 * @return The result of processing the modify DN operation.
2904 *
2905 * @throws LDAPException If the server rejects the modify DN request, or if
2906 * a problem is encountered while sending the request
2907 * or reading the response.
2908 */
2909 public LDAPResult modifyDN(final ModifyDNRequest modifyDNRequest)
2910 throws LDAPException
2911 {
2912 ensureNotNull(modifyDNRequest);
2913
2914 final LDAPResult ldapResult = modifyDNRequest.process(this, 1);
2915
2916 switch (ldapResult.getResultCode().intValue())
2917 {
2918 case ResultCode.SUCCESS_INT_VALUE:
2919 case ResultCode.NO_OPERATION_INT_VALUE:
2920 return ldapResult;
2921
2922 default:
2923 throw new LDAPException(ldapResult);
2924 }
2925 }
2926
2927
2928
2929 /**
2930 * Processes the provided modify DN request.
2931 *
2932 * @param modifyDNRequest The modify DN request to be processed. It must
2933 * not be {@code null}.
2934 *
2935 * @return The result of processing the modify DN operation.
2936 *
2937 * @throws LDAPException If the server rejects the modify DN request, or if
2938 * a problem is encountered while sending the request
2939 * or reading the response.
2940 */
2941 public LDAPResult modifyDN(final ReadOnlyModifyDNRequest modifyDNRequest)
2942 throws LDAPException
2943 {
2944 return modifyDN((ModifyDNRequest) modifyDNRequest);
2945 }
2946
2947
2948
2949 /**
2950 * Processes the provided modify DN request as an asynchronous operation.
2951 *
2952 * @param modifyDNRequest The modify DN request to be processed. It must
2953 * not be {@code null}.
2954 * @param resultListener The async result listener to use to handle the
2955 * response for the modify DN operation. It may be
2956 * {@code null} if the result is going to be obtained
2957 * from the returned {@code AsyncRequestID} object via
2958 * the {@code Future} API.
2959 *
2960 * @return An async request ID that may be used to reference the operation.
2961 *
2962 * @throws LDAPException If a problem occurs while sending the request.
2963 */
2964 public AsyncRequestID asyncModifyDN(final ModifyDNRequest modifyDNRequest,
2965 final AsyncResultListener resultListener)
2966 throws LDAPException
2967 {
2968 ensureNotNull(modifyDNRequest);
2969
2970 if (synchronousMode())
2971 {
2972 throw new LDAPException(ResultCode.NOT_SUPPORTED,
2973 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
2974 }
2975
2976 final AsyncResultListener listener;
2977 if (resultListener == null)
2978 {
2979 listener = DiscardAsyncListener.getInstance();
2980 }
2981 else
2982 {
2983 listener = resultListener;
2984 }
2985
2986 return modifyDNRequest.processAsync(this, listener);
2987 }
2988
2989
2990
2991 /**
2992 * Processes the provided modify DN request as an asynchronous operation.
2993 *
2994 * @param modifyDNRequest The modify DN request to be processed. It must
2995 * not be {@code null}.
2996 * @param resultListener The async result listener to use to handle the
2997 * response for the modify DN operation. It may be
2998 * {@code null} if the result is going to be obtained
2999 * from the returned {@code AsyncRequestID} object via
3000 * the {@code Future} API.
3001 *
3002 * @return An async request ID that may be used to reference the operation.
3003 *
3004 * @throws LDAPException If a problem occurs while sending the request.
3005 */
3006 public AsyncRequestID asyncModifyDN(
3007 final ReadOnlyModifyDNRequest modifyDNRequest,
3008 final AsyncResultListener resultListener)
3009 throws LDAPException
3010 {
3011 if (synchronousMode())
3012 {
3013 throw new LDAPException(ResultCode.NOT_SUPPORTED,
3014 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
3015 }
3016
3017 return asyncModifyDN((ModifyDNRequest) modifyDNRequest, resultListener);
3018 }
3019
3020
3021
3022 /**
3023 * Processes a search operation with the provided information. The search
3024 * result entries and references will be collected internally and included in
3025 * the {@code SearchResult} object that is returned.
3026 * <BR><BR>
3027 * Note that if the search does not complete successfully, an
3028 * {@code LDAPSearchException} will be thrown In some cases, one or more
3029 * search result entries or references may have been returned before the
3030 * failure response is received. In this case, the
3031 * {@code LDAPSearchException} methods like {@code getEntryCount},
3032 * {@code getSearchEntries}, {@code getReferenceCount}, and
3033 * {@code getSearchReferences} may be used to obtain information about those
3034 * entries and references.
3035 *
3036 * @param baseDN The base DN for the search request. It must not be
3037 * {@code null}.
3038 * @param scope The scope that specifies the range of entries that
3039 * should be examined for the search.
3040 * @param filter The string representation of the filter to use to
3041 * identify matching entries. It must not be
3042 * {@code null}.
3043 * @param attributes The set of attributes that should be returned in
3044 * matching entries. It may be {@code null} or empty if
3045 * the default attribute set (all user attributes) is to
3046 * be requested.
3047 *
3048 * @return A search result object that provides information about the
3049 * processing of the search, including the set of matching entries
3050 * and search references returned by the server.
3051 *
3052 * @throws LDAPSearchException If the search does not complete successfully,
3053 * or if a problem is encountered while parsing
3054 * the provided filter string, sending the
3055 * request, or reading the response. If one
3056 * or more entries or references were returned
3057 * before the failure was encountered, then the
3058 * {@code LDAPSearchException} object may be
3059 * examined to obtain information about those
3060 * entries and/or references.
3061 */
3062 public SearchResult search(final String baseDN, final SearchScope scope,
3063 final String filter, final String... attributes)
3064 throws LDAPSearchException
3065 {
3066 ensureNotNull(baseDN, filter);
3067
3068 try
3069 {
3070 return search(new SearchRequest(baseDN, scope, filter, attributes));
3071 }
3072 catch (LDAPSearchException lse)
3073 {
3074 debugException(lse);
3075 throw lse;
3076 }
3077 catch (LDAPException le)
3078 {
3079 debugException(le);
3080 throw new LDAPSearchException(le);
3081 }
3082 }
3083
3084
3085
3086 /**
3087 * Processes a search operation with the provided information. The search
3088 * result entries and references will be collected internally and included in
3089 * the {@code SearchResult} object that is returned.
3090 * <BR><BR>
3091 * Note that if the search does not complete successfully, an
3092 * {@code LDAPSearchException} will be thrown In some cases, one or more
3093 * search result entries or references may have been returned before the
3094 * failure response is received. In this case, the
3095 * {@code LDAPSearchException} methods like {@code getEntryCount},
3096 * {@code getSearchEntries}, {@code getReferenceCount}, and
3097 * {@code getSearchReferences} may be used to obtain information about those
3098 * entries and references.
3099 *
3100 * @param baseDN The base DN for the search request. It must not be
3101 * {@code null}.
3102 * @param scope The scope that specifies the range of entries that
3103 * should be examined for the search.
3104 * @param filter The filter to use to identify matching entries. It
3105 * must not be {@code null}.
3106 * @param attributes The set of attributes that should be returned in
3107 * matching entries. It may be {@code null} or empty if
3108 * the default attribute set (all user attributes) is to
3109 * be requested.
3110 *
3111 * @return A search result object that provides information about the
3112 * processing of the search, including the set of matching entries
3113 * and search references returned by the server.
3114 *
3115 * @throws LDAPSearchException If the search does not complete successfully,
3116 * or if a problem is encountered while sending
3117 * the request or reading the response. If one
3118 * or more entries or references were returned
3119 * before the failure was encountered, then the
3120 * {@code LDAPSearchException} object may be
3121 * examined to obtain information about those
3122 * entries and/or references.
3123 */
3124 public SearchResult search(final String baseDN, final SearchScope scope,
3125 final Filter filter, final String... attributes)
3126 throws LDAPSearchException
3127 {
3128 ensureNotNull(baseDN, filter);
3129
3130 return search(new SearchRequest(baseDN, scope, filter, attributes));
3131 }
3132
3133
3134
3135 /**
3136 * Processes a search operation with the provided information.
3137 * <BR><BR>
3138 * Note that if the search does not complete successfully, an
3139 * {@code LDAPSearchException} will be thrown In some cases, one or more
3140 * search result entries or references may have been returned before the
3141 * failure response is received. In this case, the
3142 * {@code LDAPSearchException} methods like {@code getEntryCount},
3143 * {@code getSearchEntries}, {@code getReferenceCount}, and
3144 * {@code getSearchReferences} may be used to obtain information about those
3145 * entries and references (although if a search result listener was provided,
3146 * then it will have been used to make any entries and references available,
3147 * and they will not be available through the {@code getSearchEntries} and
3148 * {@code getSearchReferences} methods).
3149 *
3150 * @param searchResultListener The search result listener that should be
3151 * used to return results to the client. It may
3152 * be {@code null} if the search results should
3153 * be collected internally and returned in the
3154 * {@code SearchResult} object.
3155 * @param baseDN The base DN for the search request. It must
3156 * not be {@code null}.
3157 * @param scope The scope that specifies the range of entries
3158 * that should be examined for the search.
3159 * @param filter The string representation of the filter to
3160 * use to identify matching entries. It must
3161 * not be {@code null}.
3162 * @param attributes The set of attributes that should be returned
3163 * in matching entries. It may be {@code null}
3164 * or empty if the default attribute set (all
3165 * user attributes) is to be requested.
3166 *
3167 * @return A search result object that provides information about the
3168 * processing of the search, potentially including the set of
3169 * matching entries and search references returned by the server.
3170 *
3171 * @throws LDAPSearchException If the search does not complete successfully,
3172 * or if a problem is encountered while parsing
3173 * the provided filter string, sending the
3174 * request, or reading the response. If one
3175 * or more entries or references were returned
3176 * before the failure was encountered, then the
3177 * {@code LDAPSearchException} object may be
3178 * examined to obtain information about those
3179 * entries and/or references.
3180 */
3181 public SearchResult search(final SearchResultListener searchResultListener,
3182 final String baseDN, final SearchScope scope,
3183 final String filter, final String... attributes)
3184 throws LDAPSearchException
3185 {
3186 ensureNotNull(baseDN, filter);
3187
3188 try
3189 {
3190 return search(new SearchRequest(searchResultListener, baseDN, scope,
3191 filter, attributes));
3192 }
3193 catch (LDAPSearchException lse)
3194 {
3195 debugException(lse);
3196 throw lse;
3197 }
3198 catch (LDAPException le)
3199 {
3200 debugException(le);
3201 throw new LDAPSearchException(le);
3202 }
3203 }
3204
3205
3206
3207 /**
3208 * Processes a search operation with the provided information.
3209 * <BR><BR>
3210 * Note that if the search does not complete successfully, an
3211 * {@code LDAPSearchException} will be thrown In some cases, one or more
3212 * search result entries or references may have been returned before the
3213 * failure response is received. In this case, the
3214 * {@code LDAPSearchException} methods like {@code getEntryCount},
3215 * {@code getSearchEntries}, {@code getReferenceCount}, and
3216 * {@code getSearchReferences} may be used to obtain information about those
3217 * entries and references (although if a search result listener was provided,
3218 * then it will have been used to make any entries and references available,
3219 * and they will not be available through the {@code getSearchEntries} and
3220 * {@code getSearchReferences} methods).
3221 *
3222 * @param searchResultListener The search result listener that should be
3223 * used to return results to the client. It may
3224 * be {@code null} if the search results should
3225 * be collected internally and returned in the
3226 * {@code SearchResult} object.
3227 * @param baseDN The base DN for the search request. It must
3228 * not be {@code null}.
3229 * @param scope The scope that specifies the range of entries
3230 * that should be examined for the search.
3231 * @param filter The filter to use to identify matching
3232 * entries. It must not be {@code null}.
3233 * @param attributes The set of attributes that should be returned
3234 * in matching entries. It may be {@code null}
3235 * or empty if the default attribute set (all
3236 * user attributes) is to be requested.
3237 *
3238 * @return A search result object that provides information about the
3239 * processing of the search, potentially including the set of
3240 * matching entries and search references returned by the server.
3241 *
3242 * @throws LDAPSearchException If the search does not complete successfully,
3243 * or if a problem is encountered while sending
3244 * the request or reading the response. If one
3245 * or more entries or references were returned
3246 * before the failure was encountered, then the
3247 * {@code LDAPSearchException} object may be
3248 * examined to obtain information about those
3249 * entries and/or references.
3250 */
3251 public SearchResult search(final SearchResultListener searchResultListener,
3252 final String baseDN, final SearchScope scope,
3253 final Filter filter, final String... attributes)
3254 throws LDAPSearchException
3255 {
3256 ensureNotNull(baseDN, filter);
3257
3258 try
3259 {
3260 return search(new SearchRequest(searchResultListener, baseDN, scope,
3261 filter, attributes));
3262 }
3263 catch (LDAPSearchException lse)
3264 {
3265 debugException(lse);
3266 throw lse;
3267 }
3268 catch (LDAPException le)
3269 {
3270 debugException(le);
3271 throw new LDAPSearchException(le);
3272 }
3273 }
3274
3275
3276
3277 /**
3278 * Processes a search operation with the provided information. The search
3279 * result entries and references will be collected internally and included in
3280 * the {@code SearchResult} object that is returned.
3281 * <BR><BR>
3282 * Note that if the search does not complete successfully, an
3283 * {@code LDAPSearchException} will be thrown In some cases, one or more
3284 * search result entries or references may have been returned before the
3285 * failure response is received. In this case, the
3286 * {@code LDAPSearchException} methods like {@code getEntryCount},
3287 * {@code getSearchEntries}, {@code getReferenceCount}, and
3288 * {@code getSearchReferences} may be used to obtain information about those
3289 * entries and references.
3290 *
3291 * @param baseDN The base DN for the search request. It must not be
3292 * {@code null}.
3293 * @param scope The scope that specifies the range of entries that
3294 * should be examined for the search.
3295 * @param derefPolicy The dereference policy the server should use for any
3296 * aliases encountered while processing the search.
3297 * @param sizeLimit The maximum number of entries that the server should
3298 * return for the search. A value of zero indicates that
3299 * there should be no limit.
3300 * @param timeLimit The maximum length of time in seconds that the server
3301 * should spend processing this search request. A value
3302 * of zero indicates that there should be no limit.
3303 * @param typesOnly Indicates whether to return only attribute names in
3304 * matching entries, or both attribute names and values.
3305 * @param filter The string representation of the filter to use to
3306 * identify matching entries. It must not be
3307 * {@code null}.
3308 * @param attributes The set of attributes that should be returned in
3309 * matching entries. It may be {@code null} or empty if
3310 * the default attribute set (all user attributes) is to
3311 * be requested.
3312 *
3313 * @return A search result object that provides information about the
3314 * processing of the search, including the set of matching entries
3315 * and search references returned by the server.
3316 *
3317 * @throws LDAPSearchException If the search does not complete successfully,
3318 * or if a problem is encountered while parsing
3319 * the provided filter string, sending the
3320 * request, or reading the response. If one
3321 * or more entries or references were returned
3322 * before the failure was encountered, then the
3323 * {@code LDAPSearchException} object may be
3324 * examined to obtain information about those
3325 * entries and/or references.
3326 */
3327 public SearchResult search(final String baseDN, final SearchScope scope,
3328 final DereferencePolicy derefPolicy,
3329 final int sizeLimit, final int timeLimit,
3330 final boolean typesOnly, final String filter,
3331 final String... attributes)
3332 throws LDAPSearchException
3333 {
3334 ensureNotNull(baseDN, filter);
3335
3336 try
3337 {
3338 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
3339 timeLimit, typesOnly, filter,
3340 attributes));
3341 }
3342 catch (LDAPSearchException lse)
3343 {
3344 debugException(lse);
3345 throw lse;
3346 }
3347 catch (LDAPException le)
3348 {
3349 debugException(le);
3350 throw new LDAPSearchException(le);
3351 }
3352 }
3353
3354
3355
3356 /**
3357 * Processes a search operation with the provided information. The search
3358 * result entries and references will be collected internally and included in
3359 * the {@code SearchResult} object that is returned.
3360 * <BR><BR>
3361 * Note that if the search does not complete successfully, an
3362 * {@code LDAPSearchException} will be thrown In some cases, one or more
3363 * search result entries or references may have been returned before the
3364 * failure response is received. In this case, the
3365 * {@code LDAPSearchException} methods like {@code getEntryCount},
3366 * {@code getSearchEntries}, {@code getReferenceCount}, and
3367 * {@code getSearchReferences} may be used to obtain information about those
3368 * entries and references.
3369 *
3370 * @param baseDN The base DN for the search request. It must not be
3371 * {@code null}.
3372 * @param scope The scope that specifies the range of entries that
3373 * should be examined for the search.
3374 * @param derefPolicy The dereference policy the server should use for any
3375 * aliases encountered while processing the search.
3376 * @param sizeLimit The maximum number of entries that the server should
3377 * return for the search. A value of zero indicates that
3378 * there should be no limit.
3379 * @param timeLimit The maximum length of time in seconds that the server
3380 * should spend processing this search request. A value
3381 * of zero indicates that there should be no limit.
3382 * @param typesOnly Indicates whether to return only attribute names in
3383 * matching entries, or both attribute names and values.
3384 * @param filter The filter to use to identify matching entries. It
3385 * must not be {@code null}.
3386 * @param attributes The set of attributes that should be returned in
3387 * matching entries. It may be {@code null} or empty if
3388 * the default attribute set (all user attributes) is to
3389 * be requested.
3390 *
3391 * @return A search result object that provides information about the
3392 * processing of the search, including the set of matching entries
3393 * and search references returned by the server.
3394 *
3395 * @throws LDAPSearchException If the search does not complete successfully,
3396 * or if a problem is encountered while sending
3397 * the request or reading the response. If one
3398 * or more entries or references were returned
3399 * before the failure was encountered, then the
3400 * {@code LDAPSearchException} object may be
3401 * examined to obtain information about those
3402 * entries and/or references.
3403 */
3404 public SearchResult search(final String baseDN, final SearchScope scope,
3405 final DereferencePolicy derefPolicy,
3406 final int sizeLimit, final int timeLimit,
3407 final boolean typesOnly, final Filter filter,
3408 final String... attributes)
3409 throws LDAPSearchException
3410 {
3411 ensureNotNull(baseDN, filter);
3412
3413 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
3414 timeLimit, typesOnly, filter, attributes));
3415 }
3416
3417
3418
3419 /**
3420 * Processes a search operation with the provided information.
3421 * <BR><BR>
3422 * Note that if the search does not complete successfully, an
3423 * {@code LDAPSearchException} will be thrown In some cases, one or more
3424 * search result entries or references may have been returned before the
3425 * failure response is received. In this case, the
3426 * {@code LDAPSearchException} methods like {@code getEntryCount},
3427 * {@code getSearchEntries}, {@code getReferenceCount}, and
3428 * {@code getSearchReferences} may be used to obtain information about those
3429 * entries and references (although if a search result listener was provided,
3430 * then it will have been used to make any entries and references available,
3431 * and they will not be available through the {@code getSearchEntries} and
3432 * {@code getSearchReferences} methods).
3433 *
3434 * @param searchResultListener The search result listener that should be
3435 * used to return results to the client. It may
3436 * be {@code null} if the search results should
3437 * be collected internally and returned in the
3438 * {@code SearchResult} object.
3439 * @param baseDN The base DN for the search request. It must
3440 * not be {@code null}.
3441 * @param scope The scope that specifies the range of entries
3442 * that should be examined for the search.
3443 * @param derefPolicy The dereference policy the server should use
3444 * for any aliases encountered while processing
3445 * the search.
3446 * @param sizeLimit The maximum number of entries that the server
3447 * should return for the search. A value of
3448 * zero indicates that there should be no limit.
3449 * @param timeLimit The maximum length of time in seconds that
3450 * the server should spend processing this
3451 * search request. A value of zero indicates
3452 * that there should be no limit.
3453 * @param typesOnly Indicates whether to return only attribute
3454 * names in matching entries, or both attribute
3455 * names and values.
3456 * @param filter The string representation of the filter to
3457 * use to identify matching entries. It must
3458 * not be {@code null}.
3459 * @param attributes The set of attributes that should be returned
3460 * in matching entries. It may be {@code null}
3461 * or empty if the default attribute set (all
3462 * user attributes) is to be requested.
3463 *
3464 * @return A search result object that provides information about the
3465 * processing of the search, potentially including the set of
3466 * matching entries and search references returned by the server.
3467 *
3468 * @throws LDAPSearchException If the search does not complete successfully,
3469 * or if a problem is encountered while parsing
3470 * the provided filter string, sending the
3471 * request, or reading the response. If one
3472 * or more entries or references were returned
3473 * before the failure was encountered, then the
3474 * {@code LDAPSearchException} object may be
3475 * examined to obtain information about those
3476 * entries and/or references.
3477 */
3478 public SearchResult search(final SearchResultListener searchResultListener,
3479 final String baseDN, final SearchScope scope,
3480 final DereferencePolicy derefPolicy,
3481 final int sizeLimit, final int timeLimit,
3482 final boolean typesOnly, final String filter,
3483 final String... attributes)
3484 throws LDAPSearchException
3485 {
3486 ensureNotNull(baseDN, filter);
3487
3488 try
3489 {
3490 return search(new SearchRequest(searchResultListener, baseDN, scope,
3491 derefPolicy, sizeLimit, timeLimit,
3492 typesOnly, filter, attributes));
3493 }
3494 catch (LDAPSearchException lse)
3495 {
3496 debugException(lse);
3497 throw lse;
3498 }
3499 catch (LDAPException le)
3500 {
3501 debugException(le);
3502 throw new LDAPSearchException(le);
3503 }
3504 }
3505
3506
3507
3508 /**
3509 * Processes a search operation with the provided information.
3510 * <BR><BR>
3511 * Note that if the search does not complete successfully, an
3512 * {@code LDAPSearchException} will be thrown In some cases, one or more
3513 * search result entries or references may have been returned before the
3514 * failure response is received. In this case, the
3515 * {@code LDAPSearchException} methods like {@code getEntryCount},
3516 * {@code getSearchEntries}, {@code getReferenceCount}, and
3517 * {@code getSearchReferences} may be used to obtain information about those
3518 * entries and references (although if a search result listener was provided,
3519 * then it will have been used to make any entries and references available,
3520 * and they will not be available through the {@code getSearchEntries} and
3521 * {@code getSearchReferences} methods).
3522 *
3523 * @param searchResultListener The search result listener that should be
3524 * used to return results to the client. It may
3525 * be {@code null} if the search results should
3526 * be collected internally and returned in the
3527 * {@code SearchResult} object.
3528 * @param baseDN The base DN for the search request. It must
3529 * not be {@code null}.
3530 * @param scope The scope that specifies the range of entries
3531 * that should be examined for the search.
3532 * @param derefPolicy The dereference policy the server should use
3533 * for any aliases encountered while processing
3534 * the search.
3535 * @param sizeLimit The maximum number of entries that the server
3536 * should return for the search. A value of
3537 * zero indicates that there should be no limit.
3538 * @param timeLimit The maximum length of time in seconds that
3539 * the server should spend processing this
3540 * search request. A value of zero indicates
3541 * that there should be no limit.
3542 * @param typesOnly Indicates whether to return only attribute
3543 * names in matching entries, or both attribute
3544 * names and values.
3545 * @param filter The filter to use to identify matching
3546 * entries. It must not be {@code null}.
3547 * @param attributes The set of attributes that should be returned
3548 * in matching entries. It may be {@code null}
3549 * or empty if the default attribute set (all
3550 * user attributes) is to be requested.
3551 *
3552 * @return A search result object that provides information about the
3553 * processing of the search, potentially including the set of
3554 * matching entries and search references returned by the server.
3555 *
3556 * @throws LDAPSearchException If the search does not complete successfully,
3557 * or if a problem is encountered while sending
3558 * the request or reading the response. If one
3559 * or more entries or references were returned
3560 * before the failure was encountered, then the
3561 * {@code LDAPSearchException} object may be
3562 * examined to obtain information about those
3563 * entries and/or references.
3564 */
3565 public SearchResult search(final SearchResultListener searchResultListener,
3566 final String baseDN, final SearchScope scope,
3567 final DereferencePolicy derefPolicy,
3568 final int sizeLimit, final int timeLimit,
3569 final boolean typesOnly, final Filter filter,
3570 final String... attributes)
3571 throws LDAPSearchException
3572 {
3573 ensureNotNull(baseDN, filter);
3574
3575 return search(new SearchRequest(searchResultListener, baseDN, scope,
3576 derefPolicy, sizeLimit, timeLimit,
3577 typesOnly, filter, attributes));
3578 }
3579
3580
3581
3582 /**
3583 * Processes the provided search request.
3584 * <BR><BR>
3585 * Note that if the search does not complete successfully, an
3586 * {@code LDAPSearchException} will be thrown In some cases, one or more
3587 * search result entries or references may have been returned before the
3588 * failure response is received. In this case, the
3589 * {@code LDAPSearchException} methods like {@code getEntryCount},
3590 * {@code getSearchEntries}, {@code getReferenceCount}, and
3591 * {@code getSearchReferences} may be used to obtain information about those
3592 * entries and references (although if a search result listener was provided,
3593 * then it will have been used to make any entries and references available,
3594 * and they will not be available through the {@code getSearchEntries} and
3595 * {@code getSearchReferences} methods).
3596 *
3597 * @param searchRequest The search request to be processed. It must not be
3598 * {@code null}.
3599 *
3600 * @return A search result object that provides information about the
3601 * processing of the search, potentially including the set of
3602 * matching entries and search references returned by the server.
3603 *
3604 * @throws LDAPSearchException If the search does not complete successfully,
3605 * or if a problem is encountered while sending
3606 * the request or reading the response. If one
3607 * or more entries or references were returned
3608 * before the failure was encountered, then the
3609 * {@code LDAPSearchException} object may be
3610 * examined to obtain information about those
3611 * entries and/or references.
3612 */
3613 public SearchResult search(final SearchRequest searchRequest)
3614 throws LDAPSearchException
3615 {
3616 ensureNotNull(searchRequest);
3617
3618 final SearchResult searchResult;
3619 try
3620 {
3621 searchResult = searchRequest.process(this, 1);
3622 }
3623 catch (LDAPSearchException lse)
3624 {
3625 debugException(lse);
3626 throw lse;
3627 }
3628 catch (LDAPException le)
3629 {
3630 debugException(le);
3631 throw new LDAPSearchException(le);
3632 }
3633
3634 if (! searchResult.getResultCode().equals(ResultCode.SUCCESS))
3635 {
3636 throw new LDAPSearchException(searchResult);
3637 }
3638
3639 return searchResult;
3640 }
3641
3642
3643
3644 /**
3645 * Processes the provided search request.
3646 * <BR><BR>
3647 * Note that if the search does not complete successfully, an
3648 * {@code LDAPSearchException} will be thrown In some cases, one or more
3649 * search result entries or references may have been returned before the
3650 * failure response is received. In this case, the
3651 * {@code LDAPSearchException} methods like {@code getEntryCount},
3652 * {@code getSearchEntries}, {@code getReferenceCount}, and
3653 * {@code getSearchReferences} may be used to obtain information about those
3654 * entries and references (although if a search result listener was provided,
3655 * then it will have been used to make any entries and references available,
3656 * and they will not be available through the {@code getSearchEntries} and
3657 * {@code getSearchReferences} methods).
3658 *
3659 * @param searchRequest The search request to be processed. It must not be
3660 * {@code null}.
3661 *
3662 * @return A search result object that provides information about the
3663 * processing of the search, potentially including the set of
3664 * matching entries and search references returned by the server.
3665 *
3666 * @throws LDAPSearchException If the search does not complete successfully,
3667 * or if a problem is encountered while sending
3668 * the request or reading the response. If one
3669 * or more entries or references were returned
3670 * before the failure was encountered, then the
3671 * {@code LDAPSearchException} object may be
3672 * examined to obtain information about those
3673 * entries and/or references.
3674 */
3675 public SearchResult search(final ReadOnlySearchRequest searchRequest)
3676 throws LDAPSearchException
3677 {
3678 return search((SearchRequest) searchRequest);
3679 }
3680
3681
3682
3683 /**
3684 * Processes a search operation with the provided information. It is expected
3685 * that at most one entry will be returned from the search, and that no
3686 * additional content from the successful search result (e.g., diagnostic
3687 * message or response controls) are needed.
3688 * <BR><BR>
3689 * Note that if the search does not complete successfully, an
3690 * {@code LDAPSearchException} will be thrown In some cases, one or more
3691 * search result entries or references may have been returned before the
3692 * failure response is received. In this case, the
3693 * {@code LDAPSearchException} methods like {@code getEntryCount},
3694 * {@code getSearchEntries}, {@code getReferenceCount}, and
3695 * {@code getSearchReferences} may be used to obtain information about those
3696 * entries and references.
3697 *
3698 * @param baseDN The base DN for the search request. It must not be
3699 * {@code null}.
3700 * @param scope The scope that specifies the range of entries that
3701 * should be examined for the search.
3702 * @param filter The string representation of the filter to use to
3703 * identify matching entries. It must not be
3704 * {@code null}.
3705 * @param attributes The set of attributes that should be returned in
3706 * matching entries. It may be {@code null} or empty if
3707 * the default attribute set (all user attributes) is to
3708 * be requested.
3709 *
3710 * @return The entry that was returned from the search, or {@code null} if no
3711 * entry was returned or the base entry does not exist.
3712 *
3713 * @throws LDAPSearchException If the search does not complete successfully,
3714 * if more than a single entry is returned, or
3715 * if a problem is encountered while parsing the
3716 * provided filter string, sending the request,
3717 * or reading the response. If one or more
3718 * entries or references were returned before
3719 * the failure was encountered, then the
3720 * {@code LDAPSearchException} object may be
3721 * examined to obtain information about those
3722 * entries and/or references.
3723 */
3724 public SearchResultEntry searchForEntry(final String baseDN,
3725 final SearchScope scope,
3726 final String filter,
3727 final String... attributes)
3728 throws LDAPSearchException
3729 {
3730 final SearchRequest r;
3731 try
3732 {
3733 r = new SearchRequest(baseDN, scope, DereferencePolicy.NEVER, 1, 0, false,
3734 filter, attributes);
3735 }
3736 catch (final LDAPException le)
3737 {
3738 debugException(le);
3739 throw new LDAPSearchException(le);
3740 }
3741
3742 return searchForEntry(r);
3743 }
3744
3745
3746
3747 /**
3748 * Processes a search operation with the provided information. It is expected
3749 * that at most one entry will be returned from the search, and that no
3750 * additional content from the successful search result (e.g., diagnostic
3751 * message or response controls) are needed.
3752 * <BR><BR>
3753 * Note that if the search does not complete successfully, an
3754 * {@code LDAPSearchException} will be thrown In some cases, one or more
3755 * search result entries or references may have been returned before the
3756 * failure response is received. In this case, the
3757 * {@code LDAPSearchException} methods like {@code getEntryCount},
3758 * {@code getSearchEntries}, {@code getReferenceCount}, and
3759 * {@code getSearchReferences} may be used to obtain information about those
3760 * entries and references.
3761 *
3762 * @param baseDN The base DN for the search request. It must not be
3763 * {@code null}.
3764 * @param scope The scope that specifies the range of entries that
3765 * should be examined for the search.
3766 * @param filter The string representation of the filter to use to
3767 * identify matching entries. It must not be
3768 * {@code null}.
3769 * @param attributes The set of attributes that should be returned in
3770 * matching entries. It may be {@code null} or empty if
3771 * the default attribute set (all user attributes) is to
3772 * be requested.
3773 *
3774 * @return The entry that was returned from the search, or {@code null} if no
3775 * entry was returned or the base entry does not exist.
3776 *
3777 * @throws LDAPSearchException If the search does not complete successfully,
3778 * if more than a single entry is returned, or
3779 * if a problem is encountered while parsing the
3780 * provided filter string, sending the request,
3781 * or reading the response. If one or more
3782 * entries or references were returned before
3783 * the failure was encountered, then the
3784 * {@code LDAPSearchException} object may be
3785 * examined to obtain information about those
3786 * entries and/or references.
3787 */
3788 public SearchResultEntry searchForEntry(final String baseDN,
3789 final SearchScope scope,
3790 final Filter filter,
3791 final String... attributes)
3792 throws LDAPSearchException
3793 {
3794 return searchForEntry(new SearchRequest(baseDN, scope,
3795 DereferencePolicy.NEVER, 1, 0, false, filter, attributes));
3796 }
3797
3798
3799
3800 /**
3801 * Processes a search operation with the provided information. It is expected
3802 * that at most one entry will be returned from the search, and that no
3803 * additional content from the successful search result (e.g., diagnostic
3804 * message or response controls) are needed.
3805 * <BR><BR>
3806 * Note that if the search does not complete successfully, an
3807 * {@code LDAPSearchException} will be thrown In some cases, one or more
3808 * search result entries or references may have been returned before the
3809 * failure response is received. In this case, the
3810 * {@code LDAPSearchException} methods like {@code getEntryCount},
3811 * {@code getSearchEntries}, {@code getReferenceCount}, and
3812 * {@code getSearchReferences} may be used to obtain information about those
3813 * entries and references.
3814 *
3815 * @param baseDN The base DN for the search request. It must not be
3816 * {@code null}.
3817 * @param scope The scope that specifies the range of entries that
3818 * should be examined for the search.
3819 * @param derefPolicy The dereference policy the server should use for any
3820 * aliases encountered while processing the search.
3821 * @param timeLimit The maximum length of time in seconds that the server
3822 * should spend processing this search request. A value
3823 * of zero indicates that there should be no limit.
3824 * @param typesOnly Indicates whether to return only attribute names in
3825 * matching entries, or both attribute names and values.
3826 * @param filter The string representation of the filter to use to
3827 * identify matching entries. It must not be
3828 * {@code null}.
3829 * @param attributes The set of attributes that should be returned in
3830 * matching entries. It may be {@code null} or empty if
3831 * the default attribute set (all user attributes) is to
3832 * be requested.
3833 *
3834 * @return The entry that was returned from the search, or {@code null} if no
3835 * entry was returned or the base entry does not exist.
3836 *
3837 * @throws LDAPSearchException If the search does not complete successfully,
3838 * if more than a single entry is returned, or
3839 * if a problem is encountered while parsing the
3840 * provided filter string, sending the request,
3841 * or reading the response. If one or more
3842 * entries or references were returned before
3843 * the failure was encountered, then the
3844 * {@code LDAPSearchException} object may be
3845 * examined to obtain information about those
3846 * entries and/or references.
3847 */
3848 public SearchResultEntry searchForEntry(final String baseDN,
3849 final SearchScope scope,
3850 final DereferencePolicy derefPolicy,
3851 final int timeLimit,
3852 final boolean typesOnly,
3853 final String filter,
3854 final String... attributes)
3855 throws LDAPSearchException
3856 {
3857 final SearchRequest r;
3858 try
3859 {
3860 r = new SearchRequest(baseDN, scope, derefPolicy, 1, timeLimit, typesOnly,
3861 filter, attributes);
3862 }
3863 catch (final LDAPException le)
3864 {
3865 debugException(le);
3866 throw new LDAPSearchException(le);
3867 }
3868
3869 return searchForEntry(r);
3870 }
3871
3872
3873
3874 /**
3875 * Processes a search operation with the provided information. It is expected
3876 * that at most one entry will be returned from the search, and that no
3877 * additional content from the successful search result (e.g., diagnostic
3878 * message or response controls) are needed.
3879 * <BR><BR>
3880 * Note that if the search does not complete successfully, an
3881 * {@code LDAPSearchException} will be thrown In some cases, one or more
3882 * search result entries or references may have been returned before the
3883 * failure response is received. In this case, the
3884 * {@code LDAPSearchException} methods like {@code getEntryCount},
3885 * {@code getSearchEntries}, {@code getReferenceCount}, and
3886 * {@code getSearchReferences} may be used to obtain information about those
3887 * entries and references.
3888 *
3889 * @param baseDN The base DN for the search request. It must not be
3890 * {@code null}.
3891 * @param scope The scope that specifies the range of entries that
3892 * should be examined for the search.
3893 * @param derefPolicy The dereference policy the server should use for any
3894 * aliases encountered while processing the search.
3895 * @param timeLimit The maximum length of time in seconds that the server
3896 * should spend processing this search request. A value
3897 * of zero indicates that there should be no limit.
3898 * @param typesOnly Indicates whether to return only attribute names in
3899 * matching entries, or both attribute names and values.
3900 * @param filter The filter to use to identify matching entries. It
3901 * must not be {@code null}.
3902 * @param attributes The set of attributes that should be returned in
3903 * matching entries. It may be {@code null} or empty if
3904 * the default attribute set (all user attributes) is to
3905 * be requested.
3906 *
3907 * @return The entry that was returned from the search, or {@code null} if no
3908 * entry was returned or the base entry does not exist.
3909 *
3910 * @throws LDAPSearchException If the search does not complete successfully,
3911 * if more than a single entry is returned, or
3912 * if a problem is encountered while parsing the
3913 * provided filter string, sending the request,
3914 * or reading the response. If one or more
3915 * entries or references were returned before
3916 * the failure was encountered, then the
3917 * {@code LDAPSearchException} object may be
3918 * examined to obtain information about those
3919 * entries and/or references.
3920 */
3921 public SearchResultEntry searchForEntry(final String baseDN,
3922 final SearchScope scope,
3923 final DereferencePolicy derefPolicy,
3924 final int timeLimit,
3925 final boolean typesOnly,
3926 final Filter filter,
3927 final String... attributes)
3928 throws LDAPSearchException
3929 {
3930 return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
3931 timeLimit, typesOnly, filter, attributes));
3932 }
3933
3934
3935
3936 /**
3937 * Processes the provided search request. It is expected that at most one
3938 * entry will be returned from the search, and that no additional content from
3939 * the successful search result (e.g., diagnostic message or response
3940 * controls) are needed.
3941 * <BR><BR>
3942 * Note that if the search does not complete successfully, an
3943 * {@code LDAPSearchException} will be thrown In some cases, one or more
3944 * search result entries or references may have been returned before the
3945 * failure response is received. In this case, the
3946 * {@code LDAPSearchException} methods like {@code getEntryCount},
3947 * {@code getSearchEntries}, {@code getReferenceCount}, and
3948 * {@code getSearchReferences} may be used to obtain information about those
3949 * entries and references.
3950 *
3951 * @param searchRequest The search request to be processed. If it is
3952 * configured with a search result listener or a size
3953 * limit other than one, then the provided request will
3954 * be duplicated with the appropriate settings.
3955 *
3956 * @return The entry that was returned from the search, or {@code null} if no
3957 * entry was returned or the base entry does not exist.
3958 *
3959 * @throws LDAPSearchException If the search does not complete successfully,
3960 * if more than a single entry is returned, or
3961 * if a problem is encountered while parsing the
3962 * provided filter string, sending the request,
3963 * or reading the response. If one or more
3964 * entries or references were returned before
3965 * the failure was encountered, then the
3966 * {@code LDAPSearchException} object may be
3967 * examined to obtain information about those
3968 * entries and/or references.
3969 */
3970 public SearchResultEntry searchForEntry(final SearchRequest searchRequest)
3971 throws LDAPSearchException
3972 {
3973 final SearchRequest r;
3974 if ((searchRequest.getSearchResultListener() != null) ||
3975 (searchRequest.getSizeLimit() != 1))
3976 {
3977 r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(),
3978 searchRequest.getDereferencePolicy(), 1,
3979 searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(),
3980 searchRequest.getFilter(), searchRequest.getAttributes());
3981
3982 r.setFollowReferrals(searchRequest.followReferralsInternal());
3983 r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null));
3984
3985 if (searchRequest.hasControl())
3986 {
3987 r.setControlsInternal(searchRequest.getControls());
3988 }
3989 }
3990 else
3991 {
3992 r = searchRequest;
3993 }
3994
3995 final SearchResult result;
3996 try
3997 {
3998 result = search(r);
3999 }
4000 catch (final LDAPSearchException lse)
4001 {
4002 debugException(lse);
4003
4004 if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT)
4005 {
4006 return null;
4007 }
4008
4009 throw lse;
4010 }
4011
4012 if (result.getEntryCount() == 0)
4013 {
4014 return null;
4015 }
4016 else
4017 {
4018 return result.getSearchEntries().get(0);
4019 }
4020 }
4021
4022
4023
4024 /**
4025 * Processes the provided search request. It is expected that at most one
4026 * entry will be returned from the search, and that no additional content from
4027 * the successful search result (e.g., diagnostic message or response
4028 * controls) are needed.
4029 * <BR><BR>
4030 * Note that if the search does not complete successfully, an
4031 * {@code LDAPSearchException} will be thrown In some cases, one or more
4032 * search result entries or references may have been returned before the
4033 * failure response is received. In this case, the
4034 * {@code LDAPSearchException} methods like {@code getEntryCount},
4035 * {@code getSearchEntries}, {@code getReferenceCount}, and
4036 * {@code getSearchReferences} may be used to obtain information about those
4037 * entries and references.
4038 *
4039 * @param searchRequest The search request to be processed. If it is
4040 * configured with a search result listener or a size
4041 * limit other than one, then the provided request will
4042 * be duplicated with the appropriate settings.
4043 *
4044 * @return The entry that was returned from the search, or {@code null} if no
4045 * entry was returned or the base entry does not exist.
4046 *
4047 * @throws LDAPSearchException If the search does not complete successfully,
4048 * if more than a single entry is returned, or
4049 * if a problem is encountered while parsing the
4050 * provided filter string, sending the request,
4051 * or reading the response. If one or more
4052 * entries or references were returned before
4053 * the failure was encountered, then the
4054 * {@code LDAPSearchException} object may be
4055 * examined to obtain information about those
4056 * entries and/or references.
4057 */
4058 public SearchResultEntry searchForEntry(
4059 final ReadOnlySearchRequest searchRequest)
4060 throws LDAPSearchException
4061 {
4062 return searchForEntry((SearchRequest) searchRequest);
4063 }
4064
4065
4066
4067 /**
4068 * Processes the provided search request as an asynchronous operation.
4069 *
4070 * @param searchRequest The search request to be processed. It must not be
4071 * {@code null}, and it must be configured with a
4072 * search result listener that is also an
4073 * {@code AsyncSearchResultListener}.
4074 *
4075 * @return An async request ID that may be used to reference the operation.
4076 *
4077 * @throws LDAPException If the provided search request does not have a
4078 * search result listener that is an
4079 * {@code AsyncSearchResultListener}, or if a problem
4080 * occurs while sending the request.
4081 */
4082 public AsyncRequestID asyncSearch(final SearchRequest searchRequest)
4083 throws LDAPException
4084 {
4085 ensureNotNull(searchRequest);
4086
4087 final SearchResultListener searchListener =
4088 searchRequest.getSearchResultListener();
4089 if (searchListener == null)
4090 {
4091 final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
4092 ERR_ASYNC_SEARCH_NO_LISTENER.get());
4093 debugCodingError(le);
4094 throw le;
4095 }
4096 else if (! (searchListener instanceof AsyncSearchResultListener))
4097 {
4098 final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR,
4099 ERR_ASYNC_SEARCH_INVALID_LISTENER.get());
4100 debugCodingError(le);
4101 throw le;
4102 }
4103
4104 if (synchronousMode())
4105 {
4106 throw new LDAPException(ResultCode.NOT_SUPPORTED,
4107 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
4108 }
4109
4110 return searchRequest.processAsync(this,
4111 (AsyncSearchResultListener) searchListener);
4112 }
4113
4114
4115
4116 /**
4117 * Processes the provided search request as an asynchronous operation.
4118 *
4119 * @param searchRequest The search request to be processed. It must not be
4120 * {@code null}, and it must be configured with a
4121 * search result listener that is also an
4122 * {@code AsyncSearchResultListener}.
4123 *
4124 * @return An async request ID that may be used to reference the operation.
4125 *
4126 * @throws LDAPException If the provided search request does not have a
4127 * search result listener that is an
4128 * {@code AsyncSearchResultListener}, or if a problem
4129 * occurs while sending the request.
4130 */
4131 public AsyncRequestID asyncSearch(final ReadOnlySearchRequest searchRequest)
4132 throws LDAPException
4133 {
4134 if (synchronousMode())
4135 {
4136 throw new LDAPException(ResultCode.NOT_SUPPORTED,
4137 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get());
4138 }
4139
4140 return asyncSearch((SearchRequest) searchRequest);
4141 }
4142
4143
4144
4145 /**
4146 * Processes the provided generic request and returns the result. This may
4147 * be useful for cases in which it is not known what type of operation the
4148 * request represents.
4149 *
4150 * @param request The request to be processed.
4151 *
4152 * @return The result obtained from processing the request.
4153 *
4154 * @throws LDAPException If a problem occurs while sending the request or
4155 * reading the response. Note simply having a
4156 * non-success result code in the response will not
4157 * cause an exception to be thrown.
4158 */
4159 public LDAPResult processOperation(final LDAPRequest request)
4160 throws LDAPException
4161 {
4162 return request.process(this, 1);
4163 }
4164
4165
4166
4167 /**
4168 * Retrieves the referral connector that should be used to establish
4169 * connections for use when following referrals.
4170 *
4171 * @return The referral connector that should be used to establish
4172 * connections for use when following referrals.
4173 */
4174 public ReferralConnector getReferralConnector()
4175 {
4176 if (referralConnector == null)
4177 {
4178 return this;
4179 }
4180 else
4181 {
4182 return referralConnector;
4183 }
4184 }
4185
4186
4187
4188 /**
4189 * Specifies the referral connector that should be used to establish
4190 * connections for use when following referrals.
4191 *
4192 * @param referralConnector The referral connector that should be used to
4193 * establish connections for use when following
4194 * referrals.
4195 */
4196 public void setReferralConnector(final ReferralConnector referralConnector)
4197 {
4198 if (referralConnector == null)
4199 {
4200 this.referralConnector = this;
4201 }
4202 else
4203 {
4204 this.referralConnector = referralConnector;
4205 }
4206 }
4207
4208
4209
4210 /**
4211 * Sends the provided LDAP message to the server over this connection.
4212 *
4213 * @param message The LDAP message to send to the target server.
4214 *
4215 * @throws LDAPException If a problem occurs while sending the request.
4216 */
4217 void sendMessage(final LDAPMessage message)
4218 throws LDAPException
4219 {
4220 if (needsReconnect.compareAndSet(true, false))
4221 {
4222 reconnect();
4223 }
4224
4225 final LDAPConnectionInternals internals = connectionInternals;
4226 if (internals == null)
4227 {
4228 throw new LDAPException(ResultCode.SERVER_DOWN,
4229 ERR_CONN_NOT_ESTABLISHED.get());
4230 }
4231 else
4232 {
4233 @SuppressWarnings("deprecation")
4234 final boolean autoReconnect = connectionOptions.autoReconnect();
4235 internals.sendMessage(message, autoReconnect);
4236 lastCommunicationTime = System.currentTimeMillis();
4237 }
4238 }
4239
4240
4241
4242 /**
4243 * Retrieves the message ID that should be used for the next request sent
4244 * over this connection.
4245 *
4246 * @return The message ID that should be used for the next request sent over
4247 * this connection, or -1 if this connection is not established.
4248 */
4249 int nextMessageID()
4250 {
4251 final LDAPConnectionInternals internals = connectionInternals;
4252 if (internals == null)
4253 {
4254 return -1;
4255 }
4256 else
4257 {
4258 return internals.nextMessageID();
4259 }
4260 }
4261
4262
4263
4264 /**
4265 * Retrieves the disconnect info object for this connection, if available.
4266 *
4267 * @return The disconnect info for this connection, or {@code null} if none
4268 * is set.
4269 */
4270 DisconnectInfo getDisconnectInfo()
4271 {
4272 return disconnectInfo.get();
4273 }
4274
4275
4276
4277 /**
4278 * Sets the disconnect type, message, and cause for this connection, if those
4279 * values have not been previously set. It will not overwrite any values that
4280 * had been previously set.
4281 * <BR><BR>
4282 * This method may be called by code which is not part of the LDAP SDK to
4283 * provide additional information about the reason for the closure. In that
4284 * case, this method must be called before the call to
4285 * {@code LDAPConnection#close}.
4286 *
4287 * @param type The disconnect type. It must not be {@code null}.
4288 * @param message A message providing additional information about the
4289 * disconnect. It may be {@code null} if no message is
4290 * available.
4291 * @param cause The exception that was caught to trigger the disconnect.
4292 * It may be {@code null} if the disconnect was not triggered
4293 * by an exception.
4294 */
4295 public void setDisconnectInfo(final DisconnectType type, final String message,
4296 final Throwable cause)
4297 {
4298 disconnectInfo.compareAndSet(null,
4299 new DisconnectInfo(this, type, message, cause));
4300 }
4301
4302
4303
4304 /**
4305 * Sets the disconnect info for this connection, if it is not already set.
4306 *
4307 * @param info The disconnect info to be set, if it is not already set.
4308 *
4309 * @return The disconnect info set for the connection, whether it was
4310 * previously or newly set.
4311 */
4312 DisconnectInfo setDisconnectInfo(final DisconnectInfo info)
4313 {
4314 disconnectInfo.compareAndSet(null, info);
4315 return disconnectInfo.get();
4316 }
4317
4318
4319
4320 /**
4321 * Retrieves the disconnect type for this connection, if available.
4322 *
4323 * @return The disconnect type for this connection, or {@code null} if no
4324 * disconnect type has been set.
4325 */
4326 public DisconnectType getDisconnectType()
4327 {
4328 final DisconnectInfo di = disconnectInfo.get();
4329 if (di == null)
4330 {
4331 return null;
4332 }
4333 else
4334 {
4335 return di.getType();
4336 }
4337 }
4338
4339
4340
4341 /**
4342 * Retrieves the disconnect message for this connection, which may provide
4343 * additional information about the reason for the disconnect, if available.
4344 *
4345 * @return The disconnect message for this connection, or {@code null} if
4346 * no disconnect message has been set.
4347 */
4348 public String getDisconnectMessage()
4349 {
4350 final DisconnectInfo di = disconnectInfo.get();
4351 if (di == null)
4352 {
4353 return null;
4354 }
4355 else
4356 {
4357 return di.getMessage();
4358 }
4359 }
4360
4361
4362
4363 /**
4364 * Retrieves the disconnect cause for this connection, which is an exception
4365 * or error that triggered the connection termination, if available.
4366 *
4367 * @return The disconnect cause for this connection, or {@code null} if no
4368 * disconnect cause has been set.
4369 */
4370 public Throwable getDisconnectCause()
4371 {
4372 final DisconnectInfo di = disconnectInfo.get();
4373 if (di == null)
4374 {
4375 return null;
4376 }
4377 else
4378 {
4379 return di.getCause();
4380 }
4381 }
4382
4383
4384
4385 /**
4386 * Indicates that this connection has been closed and is no longer available
4387 * for use.
4388 */
4389 void setClosed()
4390 {
4391 needsReconnect.set(false);
4392
4393 if (disconnectInfo.get() == null)
4394 {
4395 try
4396 {
4397 final StackTraceElement[] stackElements =
4398 Thread.currentThread().getStackTrace();
4399 final StackTraceElement[] parentStackElements =
4400 new StackTraceElement[stackElements.length - 1];
4401 System.arraycopy(stackElements, 1, parentStackElements, 0,
4402 parentStackElements.length);
4403
4404 setDisconnectInfo(DisconnectType.OTHER,
4405 ERR_CONN_CLOSED_BY_UNEXPECTED_CALL_PATH.get(
4406 getStackTrace(parentStackElements)),
4407 null);
4408 }
4409 catch (final Exception e)
4410 {
4411 debugException(e);
4412 }
4413 }
4414
4415 connectionStatistics.incrementNumDisconnects();
4416 final LDAPConnectionInternals internals = connectionInternals;
4417 if (internals != null)
4418 {
4419 internals.close();
4420 connectionInternals = null;
4421 }
4422
4423 cachedSchema = null;
4424 lastCommunicationTime = -1L;
4425
4426 synchronized (this)
4427 {
4428 final Timer t = timer;
4429 timer = null;
4430
4431 if (t != null)
4432 {
4433 t.cancel();
4434 }
4435 }
4436 }
4437
4438
4439
4440 /**
4441 * Registers the provided response acceptor with the connection reader.
4442 *
4443 * @param messageID The message ID for which the acceptor is to be
4444 * registered.
4445 * @param responseAcceptor The response acceptor to register.
4446 *
4447 * @throws LDAPException If another message acceptor is already registered
4448 * with the provided message ID.
4449 */
4450 void registerResponseAcceptor(final int messageID,
4451 final ResponseAcceptor responseAcceptor)
4452 throws LDAPException
4453 {
4454 if (needsReconnect.compareAndSet(true, false))
4455 {
4456 reconnect();
4457 }
4458
4459 final LDAPConnectionInternals internals = connectionInternals;
4460 if (internals == null)
4461 {
4462 throw new LDAPException(ResultCode.SERVER_DOWN,
4463 ERR_CONN_NOT_ESTABLISHED.get());
4464 }
4465 else
4466 {
4467 internals.registerResponseAcceptor(messageID, responseAcceptor);
4468 }
4469 }
4470
4471
4472
4473 /**
4474 * Deregisters the response acceptor associated with the provided message ID.
4475 *
4476 * @param messageID The message ID for which to deregister the associated
4477 * response acceptor.
4478 */
4479 void deregisterResponseAcceptor(final int messageID)
4480 {
4481 final LDAPConnectionInternals internals = connectionInternals;
4482 if (internals != null)
4483 {
4484 internals.deregisterResponseAcceptor(messageID);
4485 }
4486 }
4487
4488
4489
4490 /**
4491 * Retrieves a timer for use with this connection, creating one if necessary.
4492 *
4493 * @return A timer for use with this connection.
4494 */
4495 synchronized Timer getTimer()
4496 {
4497 if (timer == null)
4498 {
4499 timer = new Timer("Timer thread for " + toString(), true);
4500 }
4501
4502 return timer;
4503 }
4504
4505
4506
4507 /**
4508 * {@inheritDoc}
4509 */
4510 public LDAPConnection getReferralConnection(final LDAPURL referralURL,
4511 final LDAPConnection connection)
4512 throws LDAPException
4513 {
4514 final String host = referralURL.getHost();
4515 final int port = referralURL.getPort();
4516
4517 BindRequest bindRequest = null;
4518 if (connection.lastBindRequest != null)
4519 {
4520 bindRequest = connection.lastBindRequest.getRebindRequest(host, port);
4521 if (bindRequest == null)
4522 {
4523 throw new LDAPException(ResultCode.REFERRAL,
4524 ERR_CONN_CANNOT_AUTHENTICATE_FOR_REFERRAL.get(
4525 host, port));
4526 }
4527 }
4528
4529 final ExtendedRequest connStartTLSRequest = connection.startTLSRequest;
4530
4531 final LDAPConnection conn = new LDAPConnection(connection.socketFactory,
4532 connection.connectionOptions, host, port);
4533
4534 if (connStartTLSRequest != null)
4535 {
4536 try
4537 {
4538 final ExtendedResult startTLSResult =
4539 conn.processExtendedOperation(connStartTLSRequest);
4540 if (startTLSResult.getResultCode() != ResultCode.SUCCESS)
4541 {
4542 throw new LDAPException(startTLSResult);
4543 }
4544 }
4545 catch (final LDAPException le)
4546 {
4547 debugException(le);
4548 conn.setDisconnectInfo(DisconnectType.SECURITY_PROBLEM, null, le);
4549 conn.close();
4550
4551 throw le;
4552 }
4553 }
4554
4555 if (bindRequest != null)
4556 {
4557 try
4558 {
4559 conn.bind(bindRequest);
4560 }
4561 catch (final LDAPException le)
4562 {
4563 debugException(le);
4564 conn.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le);
4565 conn.close();
4566
4567 throw le;
4568 }
4569 }
4570
4571 return conn;
4572 }
4573
4574
4575
4576 /**
4577 * Retrieves the last successful bind request processed on this connection.
4578 *
4579 * @return The last successful bind request processed on this connection. It
4580 * may be {@code null} if no bind has been performed, or if the last
4581 * bind attempt was not successful.
4582 */
4583 public BindRequest getLastBindRequest()
4584 {
4585 return lastBindRequest;
4586 }
4587
4588
4589
4590 /**
4591 * Retrieves the StartTLS request used to secure this connection.
4592 *
4593 * @return The StartTLS request used to secure this connection, or
4594 * {@code null} if StartTLS has not been used to secure this
4595 * connection.
4596 */
4597 public ExtendedRequest getStartTLSRequest()
4598 {
4599 return startTLSRequest;
4600 }
4601
4602
4603
4604 /**
4605 * Retrieves an instance of the {@code LDAPConnectionInternals} object for
4606 * this connection.
4607 *
4608 * @param throwIfDisconnected Indicates whether to throw an
4609 * {@code LDAPException} if the connection is not
4610 * established.
4611 *
4612 * @return The {@code LDAPConnectionInternals} object for this connection, or
4613 * {@code null} if the connection is not established and no exception
4614 * should be thrown.
4615 *
4616 * @throws LDAPException If the connection is not established and
4617 * {@code throwIfDisconnected} is {@code true}.
4618 */
4619 LDAPConnectionInternals getConnectionInternals(
4620 final boolean throwIfDisconnected)
4621 throws LDAPException
4622 {
4623 final LDAPConnectionInternals internals = connectionInternals;
4624 if ((internals == null) && throwIfDisconnected)
4625 {
4626 throw new LDAPException(ResultCode.SERVER_DOWN,
4627 ERR_CONN_NOT_ESTABLISHED.get());
4628 }
4629 else
4630 {
4631 return internals;
4632 }
4633 }
4634
4635
4636
4637 /**
4638 * Retrieves the cached schema for this connection, if applicable.
4639 *
4640 * @return The cached schema for this connection, or {@code null} if it is
4641 * not available (e.g., because the connection is not established,
4642 * because {@code LDAPConnectionOptions#useSchema()} is false, or
4643 * because an error occurred when trying to read the server schema).
4644 */
4645 Schema getCachedSchema()
4646 {
4647 return cachedSchema;
4648 }
4649
4650
4651
4652 /**
4653 * Sets the cached schema for this connection.
4654 *
4655 * @param cachedSchema The cached schema for this connection. It may be
4656 * {@code null} if no cached schema is available.
4657 */
4658 void setCachedSchema(final Schema cachedSchema)
4659 {
4660 this.cachedSchema = cachedSchema;
4661 }
4662
4663
4664
4665 /**
4666 * Indicates whether this connection is operating in synchronous mode.
4667 *
4668 * @return {@code true} if this connection is operating in synchronous mode,
4669 * or {@code false} if not.
4670 */
4671 public boolean synchronousMode()
4672 {
4673 final LDAPConnectionInternals internals = connectionInternals;
4674 if (internals == null)
4675 {
4676 return false;
4677 }
4678 else
4679 {
4680 return internals.synchronousMode();
4681 }
4682 }
4683
4684
4685
4686 /**
4687 * Reads a response from the server, blocking if necessary until the response
4688 * has been received. This should only be used for connections operating in
4689 * synchronous mode.
4690 *
4691 * @param messageID The message ID for the response to be read. Any
4692 * response read with a different message ID will be
4693 * discarded, unless it is an unsolicited notification in
4694 * which case it will be provided to any registered
4695 * unsolicited notification handler.
4696 *
4697 * @return The response read from the server.
4698 *
4699 * @throws LDAPException If a problem occurs while reading the response.
4700 */
4701 LDAPResponse readResponse(final int messageID)
4702 throws LDAPException
4703 {
4704 final LDAPConnectionInternals internals = connectionInternals;
4705 if (internals != null)
4706 {
4707 final LDAPResponse response =
4708 internals.getConnectionReader().readResponse(messageID);
4709 debugLDAPResult(response, this);
4710 return response;
4711 }
4712 else
4713 {
4714 final DisconnectInfo di = disconnectInfo.get();
4715 if (di == null)
4716 {
4717 return new ConnectionClosedResponse(ResultCode.CONNECT_ERROR,
4718 ERR_CONN_READ_RESPONSE_NOT_ESTABLISHED.get());
4719 }
4720 else
4721 {
4722 return new ConnectionClosedResponse(di.getType().getResultCode(),
4723 di.getMessage());
4724 }
4725 }
4726 }
4727
4728
4729
4730 /**
4731 * Retrieves the time that this connection was established in the number of
4732 * milliseconds since January 1, 1970 UTC (the same format used by
4733 * {@code System.currentTimeMillis}.
4734 *
4735 * @return The time that this connection was established, or -1 if the
4736 * connection is not currently established.
4737 */
4738 public long getConnectTime()
4739 {
4740 final LDAPConnectionInternals internals = connectionInternals;
4741 if (internals != null)
4742 {
4743 return internals.getConnectTime();
4744 }
4745 else
4746 {
4747 return -1L;
4748 }
4749 }
4750
4751
4752
4753 /**
4754 * Retrieves the time that this connection was last used to send or receive an
4755 * LDAP message. The value will represent the number of milliseconds since
4756 * January 1, 1970 UTC (the same format used by
4757 * {@code System.currentTimeMillis}.
4758 *
4759 * @return The time that this connection was last used to send or receive an
4760 * LDAP message. If the connection is not established, then -1 will
4761 * be returned. If the connection is established but no
4762 * communication has been performed over the connection since it was
4763 * established, then the value of {@code getConnectTime()} will be
4764 * returned.
4765 */
4766 public long getLastCommunicationTime()
4767 {
4768 if (lastCommunicationTime > 0L)
4769 {
4770 return lastCommunicationTime;
4771 }
4772 else
4773 {
4774 return getConnectTime();
4775 }
4776 }
4777
4778
4779
4780 /**
4781 * Updates the last communication time for this connection to be the current
4782 * time.
4783 */
4784 void setLastCommunicationTime()
4785 {
4786 lastCommunicationTime = System.currentTimeMillis();
4787 }
4788
4789
4790
4791 /**
4792 * Retrieves the connection statistics for this LDAP connection.
4793 *
4794 * @return The connection statistics for this LDAP connection.
4795 */
4796 public LDAPConnectionStatistics getConnectionStatistics()
4797 {
4798 return connectionStatistics;
4799 }
4800
4801
4802
4803 /**
4804 * Retrieves the number of outstanding operations on this LDAP connection
4805 * (i.e., the number of operations currently in progress). The value will
4806 * only be valid for connections not configured to use synchronous mode.
4807 *
4808 * @return The number of outstanding operations on this LDAP connection, or
4809 * -1 if it cannot be determined (e.g., because the connection is not
4810 * established or is operating in synchronous mode).
4811 */
4812 public int getActiveOperationCount()
4813 {
4814 final LDAPConnectionInternals internals = connectionInternals;
4815
4816 if (internals == null)
4817 {
4818 return -1;
4819 }
4820 else
4821 {
4822 if (internals.synchronousMode())
4823 {
4824 return -1;
4825 }
4826 else
4827 {
4828 return internals.getConnectionReader().getActiveOperationCount();
4829 }
4830 }
4831 }
4832
4833
4834
4835 /**
4836 * Retrieves the schema from the provided connection. If the retrieved schema
4837 * matches schema that's already in use by other connections, the common
4838 * schema will be used instead of the newly-retrieved version.
4839 *
4840 * @param c The connection for which to retrieve the schema.
4841 *
4842 * @return The schema retrieved from the given connection, or a cached
4843 * schema if it matched a schema that was already in use.
4844 *
4845 * @throws LDAPException If a problem is encountered while retrieving or
4846 * parsing the schema.
4847 */
4848 private static Schema getCachedSchema(final LDAPConnection c)
4849 throws LDAPException
4850 {
4851 final Schema s = c.getSchema();
4852
4853 synchronized (SCHEMA_SET)
4854 {
4855 return SCHEMA_SET.addAndGet(s);
4856 }
4857 }
4858
4859
4860
4861 /**
4862 * Retrieves the connection attachment with the specified name.
4863 *
4864 * @param name The name of the attachment to retrieve. It must not be
4865 * {@code null}.
4866 *
4867 * @return The connection attachment with the specified name, or {@code null}
4868 * if there is no such attachment.
4869 */
4870 synchronized Object getAttachment(final String name)
4871 {
4872 if (attachments == null)
4873 {
4874 return null;
4875 }
4876 else
4877 {
4878 return attachments.get(name);
4879 }
4880 }
4881
4882
4883
4884 /**
4885 * Sets a connection attachment with the specified name and value.
4886 *
4887 * @param name The name of the attachment to set. It must not be
4888 * {@code null}.
4889 * @param value The value to use for the attachment. It may be {@code null}
4890 * if an attachment with the specified name should be cleared
4891 * rather than overwritten.
4892 */
4893 synchronized void setAttachment(final String name, final Object value)
4894 {
4895 if (attachments == null)
4896 {
4897 attachments = new HashMap<String,Object>(10);
4898 }
4899
4900 if (value == null)
4901 {
4902 attachments.remove(name);
4903 }
4904 else
4905 {
4906 attachments.put(name, value);
4907 }
4908 }
4909
4910
4911
4912 /**
4913 * Performs any necessary cleanup to ensure that this connection is properly
4914 * closed before it is garbage collected.
4915 *
4916 * @throws Throwable If the superclass finalizer throws an exception.
4917 */
4918 @Override()
4919 protected void finalize()
4920 throws Throwable
4921 {
4922 super.finalize();
4923
4924 setDisconnectInfo(DisconnectType.CLOSED_BY_FINALIZER, null, null);
4925 setClosed();
4926 }
4927
4928
4929
4930 /**
4931 * Retrieves a string representation of this LDAP connection.
4932 *
4933 * @return A string representation of this LDAP connection.
4934 */
4935 @Override()
4936 public String toString()
4937 {
4938 final StringBuilder buffer = new StringBuilder();
4939 toString(buffer);
4940 return buffer.toString();
4941 }
4942
4943
4944
4945 /**
4946 * Appends a string representation of this LDAP connection to the provided
4947 * buffer.
4948 *
4949 * @param buffer The buffer to which to append a string representation of
4950 * this LDAP connection.
4951 */
4952 public void toString(final StringBuilder buffer)
4953 {
4954 buffer.append("LDAPConnection(");
4955
4956 final String name = connectionName;
4957 final String poolName = connectionPoolName;
4958 if (name != null)
4959 {
4960 buffer.append("name='");
4961 buffer.append(name);
4962 buffer.append("', ");
4963 }
4964 else if (poolName != null)
4965 {
4966 buffer.append("poolName='");
4967 buffer.append(poolName);
4968 buffer.append("', ");
4969 }
4970
4971 final LDAPConnectionInternals internals = connectionInternals;
4972 if ((internals != null) && internals.isConnected())
4973 {
4974 buffer.append("connected to ");
4975 buffer.append(internals.getHost());
4976 buffer.append(':');
4977 buffer.append(internals.getPort());
4978 }
4979 else
4980 {
4981 buffer.append("not connected");
4982 }
4983
4984 buffer.append(')');
4985 }
4986 }