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