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 // We don't want to update the last bind request or update the cached 2253 // schema for this connection if it included the retain identity control. 2254 boolean hasRetainIdentityControl = false; 2255 for (final Control c : bindRequest.getControls()) 2256 { 2257 if (c.getOID().equals( 2258 RetainIdentityRequestControl.RETAIN_IDENTITY_REQUEST_OID)) 2259 { 2260 hasRetainIdentityControl = true; 2261 break; 2262 } 2263 } 2264 2265 if (! hasRetainIdentityControl) 2266 { 2267 lastBindRequest = null; 2268 } 2269 2270 final BindResult bindResult = bindRequest.process(this, 1); 2271 if (bindResult.getResultCode().equals(ResultCode.SUCCESS)) 2272 { 2273 if (! hasRetainIdentityControl) 2274 { 2275 lastBindRequest = bindRequest; 2276 if (connectionOptions.useSchema()) 2277 { 2278 try 2279 { 2280 cachedSchema = getCachedSchema(this); 2281 } 2282 catch (final Exception e) 2283 { 2284 debugException(e); 2285 } 2286 } 2287 } 2288 2289 return bindResult; 2290 } 2291 2292 if (bindResult.getResultCode().equals(ResultCode.SASL_BIND_IN_PROGRESS)) 2293 { 2294 throw new SASLBindInProgressException(bindResult); 2295 } 2296 else 2297 { 2298 throw new LDAPBindException(bindResult); 2299 } 2300 } 2301 2302 2303 2304 /** 2305 * Processes a compare operation with the provided information. 2306 * 2307 * @param dn The DN of the entry in which to make the 2308 * comparison. It must not be {@code null}. 2309 * @param attributeName The attribute name for which to make the 2310 * comparison. It must not be {@code null}. 2311 * @param assertionValue The assertion value to verify in the target entry. 2312 * It must not be {@code null}. 2313 * 2314 * @return The result of processing the compare operation. 2315 * 2316 * @throws LDAPException If the server rejects the compare request, or if a 2317 * problem is encountered while sending the request or 2318 * reading the response. 2319 */ 2320 public CompareResult compare(final String dn, final String attributeName, 2321 final String assertionValue) 2322 throws LDAPException 2323 { 2324 ensureNotNull(dn, attributeName, assertionValue); 2325 2326 return compare(new CompareRequest(dn, attributeName, assertionValue)); 2327 } 2328 2329 2330 2331 /** 2332 * Processes the provided compare request. 2333 * 2334 * @param compareRequest The compare request to be processed. It must not 2335 * be {@code null}. 2336 * 2337 * @return The result of processing the compare operation. 2338 * 2339 * @throws LDAPException If the server rejects the compare request, or if a 2340 * problem is encountered while sending the request or 2341 * reading the response. 2342 */ 2343 public CompareResult compare(final CompareRequest compareRequest) 2344 throws LDAPException 2345 { 2346 ensureNotNull(compareRequest); 2347 2348 final LDAPResult result = compareRequest.process(this, 1); 2349 switch (result.getResultCode().intValue()) 2350 { 2351 case ResultCode.COMPARE_FALSE_INT_VALUE: 2352 case ResultCode.COMPARE_TRUE_INT_VALUE: 2353 return new CompareResult(result); 2354 2355 default: 2356 throw new LDAPException(result); 2357 } 2358 } 2359 2360 2361 2362 /** 2363 * Processes the provided compare request. 2364 * 2365 * @param compareRequest The compare request to be processed. It must not 2366 * be {@code null}. 2367 * 2368 * @return The result of processing the compare operation. 2369 * 2370 * @throws LDAPException If the server rejects the compare request, or if a 2371 * problem is encountered while sending the request or 2372 * reading the response. 2373 */ 2374 public CompareResult compare(final ReadOnlyCompareRequest compareRequest) 2375 throws LDAPException 2376 { 2377 return compare((CompareRequest) compareRequest); 2378 } 2379 2380 2381 2382 /** 2383 * Processes the provided compare request as an asynchronous operation. 2384 * 2385 * @param compareRequest The compare request to be processed. It must not 2386 * be {@code null}. 2387 * @param resultListener The async result listener to use to handle the 2388 * response for the compare operation. It may be 2389 * {@code null} if the result is going to be obtained 2390 * from the returned {@code AsyncRequestID} object via 2391 * the {@code Future} API. 2392 * 2393 * @return An async request ID that may be used to reference the operation. 2394 * 2395 * @throws LDAPException If a problem occurs while sending the request. 2396 */ 2397 public AsyncRequestID asyncCompare(final CompareRequest compareRequest, 2398 final AsyncCompareResultListener resultListener) 2399 throws LDAPException 2400 { 2401 ensureNotNull(compareRequest); 2402 2403 if (synchronousMode()) 2404 { 2405 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2406 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2407 } 2408 2409 final AsyncCompareResultListener listener; 2410 if (resultListener == null) 2411 { 2412 listener = DiscardAsyncListener.getInstance(); 2413 } 2414 else 2415 { 2416 listener = resultListener; 2417 } 2418 2419 return compareRequest.processAsync(this, listener); 2420 } 2421 2422 2423 2424 /** 2425 * Processes the provided compare request as an asynchronous operation. 2426 * 2427 * @param compareRequest The compare request to be processed. It must not 2428 * be {@code null}. 2429 * @param resultListener The async result listener to use to handle the 2430 * response for the compare operation. It may be 2431 * {@code null} if the result is going to be obtained 2432 * from the returned {@code AsyncRequestID} object via 2433 * the {@code Future} API. 2434 * 2435 * @return An async request ID that may be used to reference the operation. 2436 * 2437 * @throws LDAPException If a problem occurs while sending the request. 2438 */ 2439 public AsyncRequestID asyncCompare( 2440 final ReadOnlyCompareRequest compareRequest, 2441 final AsyncCompareResultListener resultListener) 2442 throws LDAPException 2443 { 2444 if (synchronousMode()) 2445 { 2446 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2447 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2448 } 2449 2450 return asyncCompare((CompareRequest) compareRequest, resultListener); 2451 } 2452 2453 2454 2455 /** 2456 * Deletes the entry with the specified DN. 2457 * 2458 * @param dn The DN of the entry to delete. It must not be {@code null}. 2459 * 2460 * @return The result of processing the delete operation. 2461 * 2462 * @throws LDAPException If the server rejects the delete request, or if a 2463 * problem is encountered while sending the request or 2464 * reading the response. 2465 */ 2466 public LDAPResult delete(final String dn) 2467 throws LDAPException 2468 { 2469 return delete(new DeleteRequest(dn)); 2470 } 2471 2472 2473 2474 /** 2475 * Processes the provided delete request. 2476 * 2477 * @param deleteRequest The delete request to be processed. It must not be 2478 * {@code null}. 2479 * 2480 * @return The result of processing the delete operation. 2481 * 2482 * @throws LDAPException If the server rejects the delete request, or if a 2483 * problem is encountered while sending the request or 2484 * reading the response. 2485 */ 2486 public LDAPResult delete(final DeleteRequest deleteRequest) 2487 throws LDAPException 2488 { 2489 ensureNotNull(deleteRequest); 2490 2491 final LDAPResult ldapResult = deleteRequest.process(this, 1); 2492 2493 switch (ldapResult.getResultCode().intValue()) 2494 { 2495 case ResultCode.SUCCESS_INT_VALUE: 2496 case ResultCode.NO_OPERATION_INT_VALUE: 2497 return ldapResult; 2498 2499 default: 2500 throw new LDAPException(ldapResult); 2501 } 2502 } 2503 2504 2505 2506 /** 2507 * Processes the provided delete request. 2508 * 2509 * @param deleteRequest The delete request to be processed. It must not be 2510 * {@code null}. 2511 * 2512 * @return The result of processing the delete operation. 2513 * 2514 * @throws LDAPException If the server rejects the delete request, or if a 2515 * problem is encountered while sending the request or 2516 * reading the response. 2517 */ 2518 public LDAPResult delete(final ReadOnlyDeleteRequest deleteRequest) 2519 throws LDAPException 2520 { 2521 return delete((DeleteRequest) deleteRequest); 2522 } 2523 2524 2525 2526 /** 2527 * Processes the provided delete request as an asynchronous operation. 2528 * 2529 * @param deleteRequest The delete request to be processed. It must not be 2530 * {@code null}. 2531 * @param resultListener The async result listener to use to handle the 2532 * response for the delete operation. It may be 2533 * {@code null} if the result is going to be obtained 2534 * from the returned {@code AsyncRequestID} object via 2535 * the {@code Future} API. 2536 * 2537 * @return An async request ID that may be used to reference the operation. 2538 * 2539 * @throws LDAPException If a problem occurs while sending the request. 2540 */ 2541 public AsyncRequestID asyncDelete(final DeleteRequest deleteRequest, 2542 final AsyncResultListener resultListener) 2543 throws LDAPException 2544 { 2545 ensureNotNull(deleteRequest); 2546 2547 if (synchronousMode()) 2548 { 2549 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2550 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2551 } 2552 2553 final AsyncResultListener listener; 2554 if (resultListener == null) 2555 { 2556 listener = DiscardAsyncListener.getInstance(); 2557 } 2558 else 2559 { 2560 listener = resultListener; 2561 } 2562 2563 return deleteRequest.processAsync(this, listener); 2564 } 2565 2566 2567 2568 /** 2569 * Processes the provided delete request as an asynchronous operation. 2570 * 2571 * @param deleteRequest The delete request to be processed. It must not be 2572 * {@code null}. 2573 * @param resultListener The async result listener to use to handle the 2574 * response for the delete operation. It may be 2575 * {@code null} if the result is going to be obtained 2576 * from the returned {@code AsyncRequestID} object via 2577 * the {@code Future} API. 2578 * 2579 * @return An async request ID that may be used to reference the operation. 2580 * 2581 * @throws LDAPException If a problem occurs while sending the request. 2582 */ 2583 public AsyncRequestID asyncDelete(final ReadOnlyDeleteRequest deleteRequest, 2584 final AsyncResultListener resultListener) 2585 throws LDAPException 2586 { 2587 if (synchronousMode()) 2588 { 2589 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2590 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2591 } 2592 2593 return asyncDelete((DeleteRequest) deleteRequest, resultListener); 2594 } 2595 2596 2597 2598 /** 2599 * Processes an extended request with the provided request OID. Note that 2600 * because some types of extended operations return unusual result codes under 2601 * "normal" conditions, the server may not always throw an exception for a 2602 * failed extended operation like it does for other types of operations. It 2603 * will throw an exception under conditions where there appears to be a 2604 * problem with the connection or the server to which the connection is 2605 * established, but there may be many circumstances in which an extended 2606 * operation is not processed correctly but this method does not throw an 2607 * exception. In the event that no exception is thrown, it is the 2608 * responsibility of the caller to interpret the result to determine whether 2609 * the operation was processed as expected. 2610 * <BR><BR> 2611 * Note that extended operations which may change the state of this connection 2612 * (e.g., the StartTLS extended operation, which will add encryption to a 2613 * previously-unencrypted connection) should not be invoked while any other 2614 * operations are active on the connection. It is recommended that all active 2615 * operations be abandoned, canceled, or allowed to complete before attempting 2616 * to process an extended operation that may change the state of this 2617 * connection. 2618 * 2619 * @param requestOID The OID for the extended request to process. It must 2620 * not be {@code null}. 2621 * 2622 * @return The extended result object that provides information about the 2623 * result of the request processing. It may or may not indicate that 2624 * the operation was successful. 2625 * 2626 * @throws LDAPException If a problem occurs while sending the request or 2627 * reading the response. 2628 */ 2629 @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE) 2630 public ExtendedResult processExtendedOperation(final String requestOID) 2631 throws LDAPException 2632 { 2633 ensureNotNull(requestOID); 2634 2635 return processExtendedOperation(new ExtendedRequest(requestOID)); 2636 } 2637 2638 2639 2640 /** 2641 * Processes an extended request with the provided request OID and value. 2642 * Note that because some types of extended operations return unusual result 2643 * codes under "normal" conditions, the server may not always throw an 2644 * exception for a failed extended operation like it does for other types of 2645 * operations. It will throw an exception under conditions where there 2646 * appears to be a problem with the connection or the server to which the 2647 * connection is established, but there may be many circumstances in which an 2648 * extended operation is not processed correctly but this method does not 2649 * throw an exception. In the event that no exception is thrown, it is the 2650 * responsibility of the caller to interpret the result to determine whether 2651 * the operation was processed as expected. 2652 * <BR><BR> 2653 * Note that extended operations which may change the state of this connection 2654 * (e.g., the StartTLS extended operation, which will add encryption to a 2655 * previously-unencrypted connection) should not be invoked while any other 2656 * operations are active on the connection. It is recommended that all active 2657 * operations be abandoned, canceled, or allowed to complete before attempting 2658 * to process an extended operation that may change the state of this 2659 * connection. 2660 * 2661 * @param requestOID The OID for the extended request to process. It must 2662 * not be {@code null}. 2663 * @param requestValue The encoded value for the extended request to 2664 * process. It may be {@code null} if there does not 2665 * need to be a value for the requested operation. 2666 * 2667 * @return The extended result object that provides information about the 2668 * result of the request processing. It may or may not indicate that 2669 * the operation was successful. 2670 * 2671 * @throws LDAPException If a problem occurs while sending the request or 2672 * reading the response. 2673 */ 2674 @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE) 2675 public ExtendedResult processExtendedOperation(final String requestOID, 2676 final ASN1OctetString requestValue) 2677 throws LDAPException 2678 { 2679 ensureNotNull(requestOID); 2680 2681 return processExtendedOperation(new ExtendedRequest(requestOID, 2682 requestValue)); 2683 } 2684 2685 2686 2687 /** 2688 * Processes the provided extended request. Note that because some types of 2689 * extended operations return unusual result codes under "normal" conditions, 2690 * the server may not always throw an exception for a failed extended 2691 * operation like it does for other types of operations. It will throw an 2692 * exception under conditions where there appears to be a problem with the 2693 * connection or the server to which the connection is established, but there 2694 * may be many circumstances in which an extended operation is not processed 2695 * correctly but this method does not throw an exception. In the event that 2696 * no exception is thrown, it is the responsibility of the caller to interpret 2697 * the result to determine whether the operation was processed as expected. 2698 * <BR><BR> 2699 * Note that extended operations which may change the state of this connection 2700 * (e.g., the StartTLS extended operation, which will add encryption to a 2701 * previously-unencrypted connection) should not be invoked while any other 2702 * operations are active on the connection. It is recommended that all active 2703 * operations be abandoned, canceled, or allowed to complete before attempting 2704 * to process an extended operation that may change the state of this 2705 * connection. 2706 * 2707 * @param extendedRequest The extended request to be processed. It must not 2708 * be {@code null}. 2709 * 2710 * @return The extended result object that provides information about the 2711 * result of the request processing. It may or may not indicate that 2712 * the operation was successful. 2713 * 2714 * @throws LDAPException If a problem occurs while sending the request or 2715 * reading the response. 2716 */ 2717 @ThreadSafety(level=ThreadSafetyLevel.METHOD_NOT_THREADSAFE) 2718 public ExtendedResult processExtendedOperation( 2719 final ExtendedRequest extendedRequest) 2720 throws LDAPException 2721 { 2722 ensureNotNull(extendedRequest); 2723 2724 final ExtendedResult extendedResult = extendedRequest.process(this, 1); 2725 2726 if ((extendedResult.getOID() == null) && 2727 (extendedResult.getValue() == null)) 2728 { 2729 switch (extendedResult.getResultCode().intValue()) 2730 { 2731 case ResultCode.OPERATIONS_ERROR_INT_VALUE: 2732 case ResultCode.PROTOCOL_ERROR_INT_VALUE: 2733 case ResultCode.BUSY_INT_VALUE: 2734 case ResultCode.UNAVAILABLE_INT_VALUE: 2735 case ResultCode.OTHER_INT_VALUE: 2736 case ResultCode.SERVER_DOWN_INT_VALUE: 2737 case ResultCode.LOCAL_ERROR_INT_VALUE: 2738 case ResultCode.ENCODING_ERROR_INT_VALUE: 2739 case ResultCode.DECODING_ERROR_INT_VALUE: 2740 case ResultCode.TIMEOUT_INT_VALUE: 2741 case ResultCode.NO_MEMORY_INT_VALUE: 2742 case ResultCode.CONNECT_ERROR_INT_VALUE: 2743 throw new LDAPException(extendedResult); 2744 } 2745 } 2746 2747 if ((extendedResult.getResultCode() == ResultCode.SUCCESS) && 2748 extendedRequest.getOID().equals( 2749 StartTLSExtendedRequest.STARTTLS_REQUEST_OID)) 2750 { 2751 startTLSRequest = extendedRequest.duplicate(); 2752 } 2753 2754 return extendedResult; 2755 } 2756 2757 2758 2759 /** 2760 * Applies the provided modification to the specified entry. 2761 * 2762 * @param dn The DN of the entry to modify. It must not be {@code null}. 2763 * @param mod The modification to apply to the target entry. It must not 2764 * be {@code null}. 2765 * 2766 * @return The result of processing the modify operation. 2767 * 2768 * @throws LDAPException If the server rejects the modify request, or if a 2769 * problem is encountered while sending the request or 2770 * reading the response. 2771 */ 2772 public LDAPResult modify(final String dn, final Modification mod) 2773 throws LDAPException 2774 { 2775 ensureNotNull(dn, mod); 2776 2777 return modify(new ModifyRequest(dn, mod)); 2778 } 2779 2780 2781 2782 /** 2783 * Applies the provided set of modifications to the specified entry. 2784 * 2785 * @param dn The DN of the entry to modify. It must not be {@code null}. 2786 * @param mods The set of modifications to apply to the target entry. It 2787 * must not be {@code null} or empty. * 2788 * @return The result of processing the modify operation. 2789 * 2790 * @throws LDAPException If the server rejects the modify request, or if a 2791 * problem is encountered while sending the request or 2792 * reading the response. 2793 */ 2794 public LDAPResult modify(final String dn, final Modification... mods) 2795 throws LDAPException 2796 { 2797 ensureNotNull(dn, mods); 2798 2799 return modify(new ModifyRequest(dn, mods)); 2800 } 2801 2802 2803 2804 /** 2805 * Applies the provided set of modifications to the specified entry. 2806 * 2807 * @param dn The DN of the entry to modify. It must not be {@code null}. 2808 * @param mods The set of modifications to apply to the target entry. It 2809 * must not be {@code null} or empty. 2810 * 2811 * @return The result of processing the modify operation. 2812 * 2813 * @throws LDAPException If the server rejects the modify request, or if a 2814 * problem is encountered while sending the request or 2815 * reading the response. 2816 */ 2817 public LDAPResult modify(final String dn, final List<Modification> mods) 2818 throws LDAPException 2819 { 2820 ensureNotNull(dn, mods); 2821 2822 return modify(new ModifyRequest(dn, mods)); 2823 } 2824 2825 2826 2827 /** 2828 * Processes a modify request from the provided LDIF representation of the 2829 * changes. 2830 * 2831 * @param ldifModificationLines The lines that comprise an LDIF 2832 * representation of a modify change record. 2833 * It must not be {@code null} or empty. 2834 * 2835 * @return The result of processing the modify operation. 2836 * 2837 * @throws LDIFException If the provided set of lines cannot be parsed as an 2838 * LDIF modify change record. 2839 * 2840 * @throws LDAPException If the server rejects the modify request, or if a 2841 * problem is encountered while sending the request or 2842 * reading the response. 2843 * 2844 */ 2845 public LDAPResult modify(final String... ldifModificationLines) 2846 throws LDIFException, LDAPException 2847 { 2848 ensureNotNull(ldifModificationLines); 2849 2850 return modify(new ModifyRequest(ldifModificationLines)); 2851 } 2852 2853 2854 2855 /** 2856 * Processes the provided modify request. 2857 * 2858 * @param modifyRequest The modify request to be processed. It must not be 2859 * {@code null}. 2860 * 2861 * @return The result of processing the modify operation. 2862 * 2863 * @throws LDAPException If the server rejects the modify request, or if a 2864 * problem is encountered while sending the request or 2865 * reading the response. 2866 */ 2867 public LDAPResult modify(final ModifyRequest modifyRequest) 2868 throws LDAPException 2869 { 2870 ensureNotNull(modifyRequest); 2871 2872 final LDAPResult ldapResult = modifyRequest.process(this, 1); 2873 2874 switch (ldapResult.getResultCode().intValue()) 2875 { 2876 case ResultCode.SUCCESS_INT_VALUE: 2877 case ResultCode.NO_OPERATION_INT_VALUE: 2878 return ldapResult; 2879 2880 default: 2881 throw new LDAPException(ldapResult); 2882 } 2883 } 2884 2885 2886 2887 /** 2888 * Processes the provided modify request. 2889 * 2890 * @param modifyRequest The modify request to be processed. It must not be 2891 * {@code null}. 2892 * 2893 * @return The result of processing the modify operation. 2894 * 2895 * @throws LDAPException If the server rejects the modify request, or if a 2896 * problem is encountered while sending the request or 2897 * reading the response. 2898 */ 2899 public LDAPResult modify(final ReadOnlyModifyRequest modifyRequest) 2900 throws LDAPException 2901 { 2902 return modify((ModifyRequest) modifyRequest); 2903 } 2904 2905 2906 2907 /** 2908 * Processes the provided modify request as an asynchronous operation. 2909 * 2910 * @param modifyRequest The modify request to be processed. It must not be 2911 * {@code null}. 2912 * @param resultListener The async result listener to use to handle the 2913 * response for the modify operation. It may be 2914 * {@code null} if the result is going to be obtained 2915 * from the returned {@code AsyncRequestID} object via 2916 * the {@code Future} API. 2917 * 2918 * @return An async request ID that may be used to reference the operation. 2919 * 2920 * @throws LDAPException If a problem occurs while sending the request. 2921 */ 2922 public AsyncRequestID asyncModify(final ModifyRequest modifyRequest, 2923 final AsyncResultListener resultListener) 2924 throws LDAPException 2925 { 2926 ensureNotNull(modifyRequest); 2927 2928 if (synchronousMode()) 2929 { 2930 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2931 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2932 } 2933 2934 final AsyncResultListener listener; 2935 if (resultListener == null) 2936 { 2937 listener = DiscardAsyncListener.getInstance(); 2938 } 2939 else 2940 { 2941 listener = resultListener; 2942 } 2943 2944 return modifyRequest.processAsync(this, listener); 2945 } 2946 2947 2948 2949 /** 2950 * Processes the provided modify request as an asynchronous operation. 2951 * 2952 * @param modifyRequest The modify request to be processed. It must not be 2953 * {@code null}. 2954 * @param resultListener The async result listener to use to handle the 2955 * response for the modify operation. It may be 2956 * {@code null} if the result is going to be obtained 2957 * from the returned {@code AsyncRequestID} object via 2958 * the {@code Future} API. 2959 * 2960 * @return An async request ID that may be used to reference the operation. 2961 * 2962 * @throws LDAPException If a problem occurs while sending the request. 2963 */ 2964 public AsyncRequestID asyncModify(final ReadOnlyModifyRequest modifyRequest, 2965 final AsyncResultListener resultListener) 2966 throws LDAPException 2967 { 2968 if (synchronousMode()) 2969 { 2970 throw new LDAPException(ResultCode.NOT_SUPPORTED, 2971 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 2972 } 2973 2974 return asyncModify((ModifyRequest) modifyRequest, resultListener); 2975 } 2976 2977 2978 2979 /** 2980 * Performs a modify DN operation with the provided information. 2981 * 2982 * @param dn The current DN for the entry to rename. It must not 2983 * be {@code null}. 2984 * @param newRDN The new RDN to use for the entry. It must not be 2985 * {@code null}. 2986 * @param deleteOldRDN Indicates whether to delete the current RDN value 2987 * from the entry. 2988 * 2989 * @return The result of processing the modify DN operation. 2990 * 2991 * @throws LDAPException If the server rejects the modify DN request, or if 2992 * a problem is encountered while sending the request 2993 * or reading the response. 2994 */ 2995 public LDAPResult modifyDN(final String dn, final String newRDN, 2996 final boolean deleteOldRDN) 2997 throws LDAPException 2998 { 2999 ensureNotNull(dn, newRDN); 3000 3001 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN)); 3002 } 3003 3004 3005 3006 /** 3007 * Performs a modify DN operation with the provided information. 3008 * 3009 * @param dn The current DN for the entry to rename. It must not 3010 * be {@code null}. 3011 * @param newRDN The new RDN to use for the entry. It must not be 3012 * {@code null}. 3013 * @param deleteOldRDN Indicates whether to delete the current RDN value 3014 * from the entry. 3015 * @param newSuperiorDN The new superior DN for the entry. It may be 3016 * {@code null} if the entry is not to be moved below a 3017 * new parent. 3018 * 3019 * @return The result of processing the modify DN operation. 3020 * 3021 * @throws LDAPException If the server rejects the modify DN request, or if 3022 * a problem is encountered while sending the request 3023 * or reading the response. 3024 */ 3025 public LDAPResult modifyDN(final String dn, final String newRDN, 3026 final boolean deleteOldRDN, 3027 final String newSuperiorDN) 3028 throws LDAPException 3029 { 3030 ensureNotNull(dn, newRDN); 3031 3032 return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN, 3033 newSuperiorDN)); 3034 } 3035 3036 3037 3038 /** 3039 * Processes the provided modify DN request. 3040 * 3041 * @param modifyDNRequest The modify DN request to be processed. It must 3042 * not be {@code null}. 3043 * 3044 * @return The result of processing the modify DN operation. 3045 * 3046 * @throws LDAPException If the server rejects the modify DN request, or if 3047 * a problem is encountered while sending the request 3048 * or reading the response. 3049 */ 3050 public LDAPResult modifyDN(final ModifyDNRequest modifyDNRequest) 3051 throws LDAPException 3052 { 3053 ensureNotNull(modifyDNRequest); 3054 3055 final LDAPResult ldapResult = modifyDNRequest.process(this, 1); 3056 3057 switch (ldapResult.getResultCode().intValue()) 3058 { 3059 case ResultCode.SUCCESS_INT_VALUE: 3060 case ResultCode.NO_OPERATION_INT_VALUE: 3061 return ldapResult; 3062 3063 default: 3064 throw new LDAPException(ldapResult); 3065 } 3066 } 3067 3068 3069 3070 /** 3071 * Processes the provided modify DN request. 3072 * 3073 * @param modifyDNRequest The modify DN request to be processed. It must 3074 * not be {@code null}. 3075 * 3076 * @return The result of processing the modify DN operation. 3077 * 3078 * @throws LDAPException If the server rejects the modify DN request, or if 3079 * a problem is encountered while sending the request 3080 * or reading the response. 3081 */ 3082 public LDAPResult modifyDN(final ReadOnlyModifyDNRequest modifyDNRequest) 3083 throws LDAPException 3084 { 3085 return modifyDN((ModifyDNRequest) modifyDNRequest); 3086 } 3087 3088 3089 3090 /** 3091 * Processes the provided modify DN request as an asynchronous operation. 3092 * 3093 * @param modifyDNRequest The modify DN request to be processed. It must 3094 * not be {@code null}. 3095 * @param resultListener The async result listener to use to handle the 3096 * response for the modify DN operation. It may be 3097 * {@code null} if the result is going to be obtained 3098 * from the returned {@code AsyncRequestID} object via 3099 * the {@code Future} API. 3100 * 3101 * @return An async request ID that may be used to reference the operation. 3102 * 3103 * @throws LDAPException If a problem occurs while sending the request. 3104 */ 3105 public AsyncRequestID asyncModifyDN(final ModifyDNRequest modifyDNRequest, 3106 final AsyncResultListener resultListener) 3107 throws LDAPException 3108 { 3109 ensureNotNull(modifyDNRequest); 3110 3111 if (synchronousMode()) 3112 { 3113 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3114 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 3115 } 3116 3117 final AsyncResultListener listener; 3118 if (resultListener == null) 3119 { 3120 listener = DiscardAsyncListener.getInstance(); 3121 } 3122 else 3123 { 3124 listener = resultListener; 3125 } 3126 3127 return modifyDNRequest.processAsync(this, listener); 3128 } 3129 3130 3131 3132 /** 3133 * Processes the provided modify DN request as an asynchronous operation. 3134 * 3135 * @param modifyDNRequest The modify DN request to be processed. It must 3136 * not be {@code null}. 3137 * @param resultListener The async result listener to use to handle the 3138 * response for the modify DN operation. It may be 3139 * {@code null} if the result is going to be obtained 3140 * from the returned {@code AsyncRequestID} object via 3141 * the {@code Future} API. 3142 * 3143 * @return An async request ID that may be used to reference the operation. 3144 * 3145 * @throws LDAPException If a problem occurs while sending the request. 3146 */ 3147 public AsyncRequestID asyncModifyDN( 3148 final ReadOnlyModifyDNRequest modifyDNRequest, 3149 final AsyncResultListener resultListener) 3150 throws LDAPException 3151 { 3152 if (synchronousMode()) 3153 { 3154 throw new LDAPException(ResultCode.NOT_SUPPORTED, 3155 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 3156 } 3157 3158 return asyncModifyDN((ModifyDNRequest) modifyDNRequest, resultListener); 3159 } 3160 3161 3162 3163 /** 3164 * Processes a search operation with the provided information. The search 3165 * result entries and references will be collected internally and included in 3166 * the {@code SearchResult} object that is returned. 3167 * <BR><BR> 3168 * Note that if the search does not complete successfully, an 3169 * {@code LDAPSearchException} will be thrown In some cases, one or more 3170 * search result entries or references may have been returned before the 3171 * failure response is received. In this case, the 3172 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3173 * {@code getSearchEntries}, {@code getReferenceCount}, and 3174 * {@code getSearchReferences} may be used to obtain information about those 3175 * entries and references. 3176 * 3177 * @param baseDN The base DN for the search request. It must not be 3178 * {@code null}. 3179 * @param scope The scope that specifies the range of entries that 3180 * should be examined for the search. 3181 * @param filter The string representation of the filter to use to 3182 * identify matching entries. It must not be 3183 * {@code null}. 3184 * @param attributes The set of attributes that should be returned in 3185 * matching entries. It may be {@code null} or empty if 3186 * the default attribute set (all user attributes) is to 3187 * be requested. 3188 * 3189 * @return A search result object that provides information about the 3190 * processing of the search, including the set of matching entries 3191 * and search references returned by the server. 3192 * 3193 * @throws LDAPSearchException If the search does not complete successfully, 3194 * or if a problem is encountered while parsing 3195 * the provided filter string, sending the 3196 * request, or reading the response. If one 3197 * or more entries or references were returned 3198 * before the failure was encountered, then the 3199 * {@code LDAPSearchException} object may be 3200 * examined to obtain information about those 3201 * entries and/or references. 3202 */ 3203 public SearchResult search(final String baseDN, final SearchScope scope, 3204 final String filter, final String... attributes) 3205 throws LDAPSearchException 3206 { 3207 ensureNotNull(baseDN, filter); 3208 3209 try 3210 { 3211 return search(new SearchRequest(baseDN, scope, filter, attributes)); 3212 } 3213 catch (final LDAPSearchException lse) 3214 { 3215 debugException(lse); 3216 throw lse; 3217 } 3218 catch (final LDAPException le) 3219 { 3220 debugException(le); 3221 throw new LDAPSearchException(le); 3222 } 3223 } 3224 3225 3226 3227 /** 3228 * Processes a search operation with the provided information. The search 3229 * result entries and references will be collected internally and included in 3230 * the {@code SearchResult} object that is returned. 3231 * <BR><BR> 3232 * Note that if the search does not complete successfully, an 3233 * {@code LDAPSearchException} will be thrown In some cases, one or more 3234 * search result entries or references may have been returned before the 3235 * failure response is received. In this case, the 3236 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3237 * {@code getSearchEntries}, {@code getReferenceCount}, and 3238 * {@code getSearchReferences} may be used to obtain information about those 3239 * entries and references. 3240 * 3241 * @param baseDN The base DN for the search request. It must not be 3242 * {@code null}. 3243 * @param scope The scope that specifies the range of entries that 3244 * should be examined for the search. 3245 * @param filter The filter to use to identify matching entries. It 3246 * must not be {@code null}. 3247 * @param attributes The set of attributes that should be returned in 3248 * matching entries. It may be {@code null} or empty if 3249 * the default attribute set (all user attributes) is to 3250 * be requested. 3251 * 3252 * @return A search result object that provides information about the 3253 * processing of the search, including the set of matching entries 3254 * and search references returned by the server. 3255 * 3256 * @throws LDAPSearchException If the search does not complete successfully, 3257 * or if a problem is encountered while sending 3258 * the request or reading the response. If one 3259 * or more entries or references were returned 3260 * before the failure was encountered, then the 3261 * {@code LDAPSearchException} object may be 3262 * examined to obtain information about those 3263 * entries and/or references. 3264 */ 3265 public SearchResult search(final String baseDN, final SearchScope scope, 3266 final Filter filter, final String... attributes) 3267 throws LDAPSearchException 3268 { 3269 ensureNotNull(baseDN, filter); 3270 3271 return search(new SearchRequest(baseDN, scope, filter, attributes)); 3272 } 3273 3274 3275 3276 /** 3277 * Processes a search operation with the provided information. 3278 * <BR><BR> 3279 * Note that if the search does not complete successfully, an 3280 * {@code LDAPSearchException} will be thrown In some cases, one or more 3281 * search result entries or references may have been returned before the 3282 * failure response is received. In this case, the 3283 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3284 * {@code getSearchEntries}, {@code getReferenceCount}, and 3285 * {@code getSearchReferences} may be used to obtain information about those 3286 * entries and references (although if a search result listener was provided, 3287 * then it will have been used to make any entries and references available, 3288 * and they will not be available through the {@code getSearchEntries} and 3289 * {@code getSearchReferences} methods). 3290 * 3291 * @param searchResultListener The search result listener that should be 3292 * used to return results to the client. It may 3293 * be {@code null} if the search results should 3294 * be collected internally and returned in the 3295 * {@code SearchResult} object. 3296 * @param baseDN The base DN for the search request. It must 3297 * not be {@code null}. 3298 * @param scope The scope that specifies the range of entries 3299 * that should be examined for the search. 3300 * @param filter The string representation of the filter to 3301 * use to identify matching entries. It must 3302 * not be {@code null}. 3303 * @param attributes The set of attributes that should be returned 3304 * in matching entries. It may be {@code null} 3305 * or empty if the default attribute set (all 3306 * user attributes) is to be requested. 3307 * 3308 * @return A search result object that provides information about the 3309 * processing of the search, potentially including the set of 3310 * matching entries and search references returned by the server. 3311 * 3312 * @throws LDAPSearchException If the search does not complete successfully, 3313 * or if a problem is encountered while parsing 3314 * the provided filter string, sending the 3315 * request, or reading the response. If one 3316 * or more entries or references were returned 3317 * before the failure was encountered, then the 3318 * {@code LDAPSearchException} object may be 3319 * examined to obtain information about those 3320 * entries and/or references. 3321 */ 3322 public SearchResult search(final SearchResultListener searchResultListener, 3323 final String baseDN, final SearchScope scope, 3324 final String filter, final String... attributes) 3325 throws LDAPSearchException 3326 { 3327 ensureNotNull(baseDN, filter); 3328 3329 try 3330 { 3331 return search(new SearchRequest(searchResultListener, baseDN, scope, 3332 filter, attributes)); 3333 } 3334 catch (final LDAPSearchException lse) 3335 { 3336 debugException(lse); 3337 throw lse; 3338 } 3339 catch (final LDAPException le) 3340 { 3341 debugException(le); 3342 throw new LDAPSearchException(le); 3343 } 3344 } 3345 3346 3347 3348 /** 3349 * Processes a search operation with the provided information. 3350 * <BR><BR> 3351 * Note that if the search does not complete successfully, an 3352 * {@code LDAPSearchException} will be thrown In some cases, one or more 3353 * search result entries or references may have been returned before the 3354 * failure response is received. In this case, the 3355 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3356 * {@code getSearchEntries}, {@code getReferenceCount}, and 3357 * {@code getSearchReferences} may be used to obtain information about those 3358 * entries and references (although if a search result listener was provided, 3359 * then it will have been used to make any entries and references available, 3360 * and they will not be available through the {@code getSearchEntries} and 3361 * {@code getSearchReferences} methods). 3362 * 3363 * @param searchResultListener The search result listener that should be 3364 * used to return results to the client. It may 3365 * be {@code null} if the search results should 3366 * be collected internally and returned in the 3367 * {@code SearchResult} object. 3368 * @param baseDN The base DN for the search request. It must 3369 * not be {@code null}. 3370 * @param scope The scope that specifies the range of entries 3371 * that should be examined for the search. 3372 * @param filter The filter to use to identify matching 3373 * entries. It must not be {@code null}. 3374 * @param attributes The set of attributes that should be returned 3375 * in matching entries. It may be {@code null} 3376 * or empty if the default attribute set (all 3377 * user attributes) is to be requested. 3378 * 3379 * @return A search result object that provides information about the 3380 * processing of the search, potentially including the set of 3381 * matching entries and search references returned by the server. 3382 * 3383 * @throws LDAPSearchException If the search does not complete successfully, 3384 * or if a problem is encountered while sending 3385 * the request or reading the response. If one 3386 * or more entries or references were returned 3387 * before the failure was encountered, then the 3388 * {@code LDAPSearchException} object may be 3389 * examined to obtain information about those 3390 * entries and/or references. 3391 */ 3392 public SearchResult search(final SearchResultListener searchResultListener, 3393 final String baseDN, final SearchScope scope, 3394 final Filter filter, final String... attributes) 3395 throws LDAPSearchException 3396 { 3397 ensureNotNull(baseDN, filter); 3398 3399 try 3400 { 3401 return search(new SearchRequest(searchResultListener, baseDN, scope, 3402 filter, attributes)); 3403 } 3404 catch (final LDAPSearchException lse) 3405 { 3406 debugException(lse); 3407 throw lse; 3408 } 3409 } 3410 3411 3412 3413 /** 3414 * Processes a search operation with the provided information. The search 3415 * result entries and references will be collected internally and included in 3416 * the {@code SearchResult} object that is returned. 3417 * <BR><BR> 3418 * Note that if the search does not complete successfully, an 3419 * {@code LDAPSearchException} will be thrown In some cases, one or more 3420 * search result entries or references may have been returned before the 3421 * failure response is received. In this case, the 3422 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3423 * {@code getSearchEntries}, {@code getReferenceCount}, and 3424 * {@code getSearchReferences} may be used to obtain information about those 3425 * entries and references. 3426 * 3427 * @param baseDN The base DN for the search request. It must not be 3428 * {@code null}. 3429 * @param scope The scope that specifies the range of entries that 3430 * should be examined for the search. 3431 * @param derefPolicy The dereference policy the server should use for any 3432 * aliases encountered while processing the search. 3433 * @param sizeLimit The maximum number of entries that the server should 3434 * return for the search. A value of zero indicates that 3435 * there should be no limit. 3436 * @param timeLimit The maximum length of time in seconds that the server 3437 * should spend processing this search request. A value 3438 * of zero indicates that there should be no limit. 3439 * @param typesOnly Indicates whether to return only attribute names in 3440 * matching entries, or both attribute names and values. 3441 * @param filter The string representation of the filter to use to 3442 * identify matching entries. It must not be 3443 * {@code null}. 3444 * @param attributes The set of attributes that should be returned in 3445 * matching entries. It may be {@code null} or empty if 3446 * the default attribute set (all user attributes) is to 3447 * be requested. 3448 * 3449 * @return A search result object that provides information about the 3450 * processing of the search, including the set of matching entries 3451 * and search references returned by the server. 3452 * 3453 * @throws LDAPSearchException If the search does not complete successfully, 3454 * or if a problem is encountered while parsing 3455 * the provided filter string, sending the 3456 * request, or reading the response. If one 3457 * or more entries or references were returned 3458 * before the failure was encountered, then the 3459 * {@code LDAPSearchException} object may be 3460 * examined to obtain information about those 3461 * entries and/or references. 3462 */ 3463 public SearchResult search(final String baseDN, final SearchScope scope, 3464 final DereferencePolicy derefPolicy, 3465 final int sizeLimit, final int timeLimit, 3466 final boolean typesOnly, final String filter, 3467 final String... attributes) 3468 throws LDAPSearchException 3469 { 3470 ensureNotNull(baseDN, filter); 3471 3472 try 3473 { 3474 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit, 3475 timeLimit, typesOnly, filter, 3476 attributes)); 3477 } 3478 catch (final LDAPSearchException lse) 3479 { 3480 debugException(lse); 3481 throw lse; 3482 } 3483 catch (final LDAPException le) 3484 { 3485 debugException(le); 3486 throw new LDAPSearchException(le); 3487 } 3488 } 3489 3490 3491 3492 /** 3493 * Processes a search operation with the provided information. The search 3494 * result entries and references will be collected internally and included in 3495 * the {@code SearchResult} object that is returned. 3496 * <BR><BR> 3497 * Note that if the search does not complete successfully, an 3498 * {@code LDAPSearchException} will be thrown In some cases, one or more 3499 * search result entries or references may have been returned before the 3500 * failure response is received. In this case, the 3501 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3502 * {@code getSearchEntries}, {@code getReferenceCount}, and 3503 * {@code getSearchReferences} may be used to obtain information about those 3504 * entries and references. 3505 * 3506 * @param baseDN The base DN for the search request. It must not be 3507 * {@code null}. 3508 * @param scope The scope that specifies the range of entries that 3509 * should be examined for the search. 3510 * @param derefPolicy The dereference policy the server should use for any 3511 * aliases encountered while processing the search. 3512 * @param sizeLimit The maximum number of entries that the server should 3513 * return for the search. A value of zero indicates that 3514 * there should be no limit. 3515 * @param timeLimit The maximum length of time in seconds that the server 3516 * should spend processing this search request. A value 3517 * of zero indicates that there should be no limit. 3518 * @param typesOnly Indicates whether to return only attribute names in 3519 * matching entries, or both attribute names and values. 3520 * @param filter The filter to use to identify matching entries. It 3521 * must not be {@code null}. 3522 * @param attributes The set of attributes that should be returned in 3523 * matching entries. It may be {@code null} or empty if 3524 * the default attribute set (all user attributes) is to 3525 * be requested. 3526 * 3527 * @return A search result object that provides information about the 3528 * processing of the search, including the set of matching entries 3529 * and search references returned by the server. 3530 * 3531 * @throws LDAPSearchException If the search does not complete successfully, 3532 * or if a problem is encountered while sending 3533 * the request or reading the response. If one 3534 * or more entries or references were returned 3535 * before the failure was encountered, then the 3536 * {@code LDAPSearchException} object may be 3537 * examined to obtain information about those 3538 * entries and/or references. 3539 */ 3540 public SearchResult search(final String baseDN, final SearchScope scope, 3541 final DereferencePolicy derefPolicy, 3542 final int sizeLimit, final int timeLimit, 3543 final boolean typesOnly, final Filter filter, 3544 final String... attributes) 3545 throws LDAPSearchException 3546 { 3547 ensureNotNull(baseDN, filter); 3548 3549 return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit, 3550 timeLimit, typesOnly, filter, attributes)); 3551 } 3552 3553 3554 3555 /** 3556 * Processes a search operation with the provided information. 3557 * <BR><BR> 3558 * Note that if the search does not complete successfully, an 3559 * {@code LDAPSearchException} will be thrown In some cases, one or more 3560 * search result entries or references may have been returned before the 3561 * failure response is received. In this case, the 3562 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3563 * {@code getSearchEntries}, {@code getReferenceCount}, and 3564 * {@code getSearchReferences} may be used to obtain information about those 3565 * entries and references (although if a search result listener was provided, 3566 * then it will have been used to make any entries and references available, 3567 * and they will not be available through the {@code getSearchEntries} and 3568 * {@code getSearchReferences} methods). 3569 * 3570 * @param searchResultListener The search result listener that should be 3571 * used to return results to the client. It may 3572 * be {@code null} if the search results should 3573 * be collected internally and returned in the 3574 * {@code SearchResult} object. 3575 * @param baseDN The base DN for the search request. It must 3576 * not be {@code null}. 3577 * @param scope The scope that specifies the range of entries 3578 * that should be examined for the search. 3579 * @param derefPolicy The dereference policy the server should use 3580 * for any aliases encountered while processing 3581 * the search. 3582 * @param sizeLimit The maximum number of entries that the server 3583 * should return for the search. A value of 3584 * zero indicates that there should be no limit. 3585 * @param timeLimit The maximum length of time in seconds that 3586 * the server should spend processing this 3587 * search request. A value of zero indicates 3588 * that there should be no limit. 3589 * @param typesOnly Indicates whether to return only attribute 3590 * names in matching entries, or both attribute 3591 * names and values. 3592 * @param filter The string representation of the filter to 3593 * use to identify matching entries. It must 3594 * not be {@code null}. 3595 * @param attributes The set of attributes that should be returned 3596 * in matching entries. It may be {@code null} 3597 * or empty if the default attribute set (all 3598 * user attributes) is to be requested. 3599 * 3600 * @return A search result object that provides information about the 3601 * processing of the search, potentially including the set of 3602 * matching entries and search references returned by the server. 3603 * 3604 * @throws LDAPSearchException If the search does not complete successfully, 3605 * or if a problem is encountered while parsing 3606 * the provided filter string, sending the 3607 * request, or reading the response. If one 3608 * or more entries or references were returned 3609 * before the failure was encountered, then the 3610 * {@code LDAPSearchException} object may be 3611 * examined to obtain information about those 3612 * entries and/or references. 3613 */ 3614 public SearchResult search(final SearchResultListener searchResultListener, 3615 final String baseDN, final SearchScope scope, 3616 final DereferencePolicy derefPolicy, 3617 final int sizeLimit, final int timeLimit, 3618 final boolean typesOnly, final String filter, 3619 final String... attributes) 3620 throws LDAPSearchException 3621 { 3622 ensureNotNull(baseDN, filter); 3623 3624 try 3625 { 3626 return search(new SearchRequest(searchResultListener, baseDN, scope, 3627 derefPolicy, sizeLimit, timeLimit, 3628 typesOnly, filter, attributes)); 3629 } 3630 catch (final LDAPSearchException lse) 3631 { 3632 debugException(lse); 3633 throw lse; 3634 } 3635 catch (final LDAPException le) 3636 { 3637 debugException(le); 3638 throw new LDAPSearchException(le); 3639 } 3640 } 3641 3642 3643 3644 /** 3645 * Processes a search operation with the provided information. 3646 * <BR><BR> 3647 * Note that if the search does not complete successfully, an 3648 * {@code LDAPSearchException} will be thrown In some cases, one or more 3649 * search result entries or references may have been returned before the 3650 * failure response is received. In this case, the 3651 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3652 * {@code getSearchEntries}, {@code getReferenceCount}, and 3653 * {@code getSearchReferences} may be used to obtain information about those 3654 * entries and references (although if a search result listener was provided, 3655 * then it will have been used to make any entries and references available, 3656 * and they will not be available through the {@code getSearchEntries} and 3657 * {@code getSearchReferences} methods). 3658 * 3659 * @param searchResultListener The search result listener that should be 3660 * used to return results to the client. It may 3661 * be {@code null} if the search results should 3662 * be collected internally and returned in the 3663 * {@code SearchResult} object. 3664 * @param baseDN The base DN for the search request. It must 3665 * not be {@code null}. 3666 * @param scope The scope that specifies the range of entries 3667 * that should be examined for the search. 3668 * @param derefPolicy The dereference policy the server should use 3669 * for any aliases encountered while processing 3670 * the search. 3671 * @param sizeLimit The maximum number of entries that the server 3672 * should return for the search. A value of 3673 * zero indicates that there should be no limit. 3674 * @param timeLimit The maximum length of time in seconds that 3675 * the server should spend processing this 3676 * search request. A value of zero indicates 3677 * that there should be no limit. 3678 * @param typesOnly Indicates whether to return only attribute 3679 * names in matching entries, or both attribute 3680 * names and values. 3681 * @param filter The filter to use to identify matching 3682 * entries. It must not be {@code null}. 3683 * @param attributes The set of attributes that should be returned 3684 * in matching entries. It may be {@code null} 3685 * or empty if the default attribute set (all 3686 * user attributes) is to be requested. 3687 * 3688 * @return A search result object that provides information about the 3689 * processing of the search, potentially including the set of 3690 * matching entries and search references returned by the server. 3691 * 3692 * @throws LDAPSearchException If the search does not complete successfully, 3693 * or if a problem is encountered while sending 3694 * the request or reading the response. If one 3695 * or more entries or references were returned 3696 * before the failure was encountered, then the 3697 * {@code LDAPSearchException} object may be 3698 * examined to obtain information about those 3699 * entries and/or references. 3700 */ 3701 public SearchResult search(final SearchResultListener searchResultListener, 3702 final String baseDN, final SearchScope scope, 3703 final DereferencePolicy derefPolicy, 3704 final int sizeLimit, final int timeLimit, 3705 final boolean typesOnly, final Filter filter, 3706 final String... attributes) 3707 throws LDAPSearchException 3708 { 3709 ensureNotNull(baseDN, filter); 3710 3711 return search(new SearchRequest(searchResultListener, baseDN, scope, 3712 derefPolicy, sizeLimit, timeLimit, 3713 typesOnly, filter, attributes)); 3714 } 3715 3716 3717 3718 /** 3719 * Processes the provided search request. 3720 * <BR><BR> 3721 * Note that if the search does not complete successfully, an 3722 * {@code LDAPSearchException} will be thrown In some cases, one or more 3723 * search result entries or references may have been returned before the 3724 * failure response is received. In this case, the 3725 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3726 * {@code getSearchEntries}, {@code getReferenceCount}, and 3727 * {@code getSearchReferences} may be used to obtain information about those 3728 * entries and references (although if a search result listener was provided, 3729 * then it will have been used to make any entries and references available, 3730 * and they will not be available through the {@code getSearchEntries} and 3731 * {@code getSearchReferences} methods). 3732 * 3733 * @param searchRequest The search request to be processed. It must not be 3734 * {@code null}. 3735 * 3736 * @return A search result object that provides information about the 3737 * processing of the search, potentially including the set of 3738 * matching entries and search references returned by the server. 3739 * 3740 * @throws LDAPSearchException If the search does not complete successfully, 3741 * or if a problem is encountered while sending 3742 * the request or reading the response. If one 3743 * or more entries or references were returned 3744 * before the failure was encountered, then the 3745 * {@code LDAPSearchException} object may be 3746 * examined to obtain information about those 3747 * entries and/or references. 3748 */ 3749 public SearchResult search(final SearchRequest searchRequest) 3750 throws LDAPSearchException 3751 { 3752 ensureNotNull(searchRequest); 3753 3754 final SearchResult searchResult; 3755 try 3756 { 3757 searchResult = searchRequest.process(this, 1); 3758 } 3759 catch (final LDAPSearchException lse) 3760 { 3761 debugException(lse); 3762 throw lse; 3763 } 3764 catch (final LDAPException le) 3765 { 3766 debugException(le); 3767 throw new LDAPSearchException(le); 3768 } 3769 3770 if (! searchResult.getResultCode().equals(ResultCode.SUCCESS)) 3771 { 3772 throw new LDAPSearchException(searchResult); 3773 } 3774 3775 return searchResult; 3776 } 3777 3778 3779 3780 /** 3781 * Processes the provided search request. 3782 * <BR><BR> 3783 * Note that if the search does not complete successfully, an 3784 * {@code LDAPSearchException} will be thrown In some cases, one or more 3785 * search result entries or references may have been returned before the 3786 * failure response is received. In this case, the 3787 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3788 * {@code getSearchEntries}, {@code getReferenceCount}, and 3789 * {@code getSearchReferences} may be used to obtain information about those 3790 * entries and references (although if a search result listener was provided, 3791 * then it will have been used to make any entries and references available, 3792 * and they will not be available through the {@code getSearchEntries} and 3793 * {@code getSearchReferences} methods). 3794 * 3795 * @param searchRequest The search request to be processed. It must not be 3796 * {@code null}. 3797 * 3798 * @return A search result object that provides information about the 3799 * processing of the search, potentially including the set of 3800 * matching entries and search references returned by the server. 3801 * 3802 * @throws LDAPSearchException If the search does not complete successfully, 3803 * or if a problem is encountered while sending 3804 * the request or reading the response. If one 3805 * or more entries or references were returned 3806 * before the failure was encountered, then the 3807 * {@code LDAPSearchException} object may be 3808 * examined to obtain information about those 3809 * entries and/or references. 3810 */ 3811 public SearchResult search(final ReadOnlySearchRequest searchRequest) 3812 throws LDAPSearchException 3813 { 3814 return search((SearchRequest) searchRequest); 3815 } 3816 3817 3818 3819 /** 3820 * Processes a search operation with the provided information. It is expected 3821 * that at most one entry will be returned from the search, and that no 3822 * additional content from the successful search result (e.g., diagnostic 3823 * message or response controls) are needed. 3824 * <BR><BR> 3825 * Note that if the search does not complete successfully, an 3826 * {@code LDAPSearchException} will be thrown In some cases, one or more 3827 * search result entries or references may have been returned before the 3828 * failure response is received. In this case, the 3829 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3830 * {@code getSearchEntries}, {@code getReferenceCount}, and 3831 * {@code getSearchReferences} may be used to obtain information about those 3832 * entries and references. 3833 * 3834 * @param baseDN The base DN for the search request. It must not be 3835 * {@code null}. 3836 * @param scope The scope that specifies the range of entries that 3837 * should be examined for the search. 3838 * @param filter The string representation of the filter to use to 3839 * identify matching entries. It must not be 3840 * {@code null}. 3841 * @param attributes The set of attributes that should be returned in 3842 * matching entries. It may be {@code null} or empty if 3843 * the default attribute set (all user attributes) is to 3844 * be requested. 3845 * 3846 * @return The entry that was returned from the search, or {@code null} if no 3847 * entry was returned or the base entry does not exist. 3848 * 3849 * @throws LDAPSearchException If the search does not complete successfully, 3850 * if more than a single entry is returned, or 3851 * if a problem is encountered while parsing the 3852 * provided filter string, sending the request, 3853 * or reading the response. If one or more 3854 * entries or references were returned before 3855 * the failure was encountered, then the 3856 * {@code LDAPSearchException} object may be 3857 * examined to obtain information about those 3858 * entries and/or references. 3859 */ 3860 public SearchResultEntry searchForEntry(final String baseDN, 3861 final SearchScope scope, 3862 final String filter, 3863 final String... attributes) 3864 throws LDAPSearchException 3865 { 3866 final SearchRequest r; 3867 try 3868 { 3869 r = new SearchRequest(baseDN, scope, DereferencePolicy.NEVER, 1, 0, false, 3870 filter, attributes); 3871 } 3872 catch (final LDAPException le) 3873 { 3874 debugException(le); 3875 throw new LDAPSearchException(le); 3876 } 3877 3878 return searchForEntry(r); 3879 } 3880 3881 3882 3883 /** 3884 * Processes a search operation with the provided information. It is expected 3885 * that at most one entry will be returned from the search, and that no 3886 * additional content from the successful search result (e.g., diagnostic 3887 * message or response controls) are needed. 3888 * <BR><BR> 3889 * Note that if the search does not complete successfully, an 3890 * {@code LDAPSearchException} will be thrown In some cases, one or more 3891 * search result entries or references may have been returned before the 3892 * failure response is received. In this case, the 3893 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3894 * {@code getSearchEntries}, {@code getReferenceCount}, and 3895 * {@code getSearchReferences} may be used to obtain information about those 3896 * entries and references. 3897 * 3898 * @param baseDN The base DN for the search request. It must not be 3899 * {@code null}. 3900 * @param scope The scope that specifies the range of entries that 3901 * should be examined for the search. 3902 * @param filter The string representation of the filter to use to 3903 * identify matching entries. It must not be 3904 * {@code null}. 3905 * @param attributes The set of attributes that should be returned in 3906 * matching entries. It may be {@code null} or empty if 3907 * the default attribute set (all user attributes) is to 3908 * be requested. 3909 * 3910 * @return The entry that was returned from the search, or {@code null} if no 3911 * entry was returned or the base entry does not exist. 3912 * 3913 * @throws LDAPSearchException If the search does not complete successfully, 3914 * if more than a single entry is returned, or 3915 * if a problem is encountered while parsing the 3916 * provided filter string, sending the request, 3917 * or reading the response. If one or more 3918 * entries or references were returned before 3919 * the failure was encountered, then the 3920 * {@code LDAPSearchException} object may be 3921 * examined to obtain information about those 3922 * entries and/or references. 3923 */ 3924 public SearchResultEntry searchForEntry(final String baseDN, 3925 final SearchScope scope, 3926 final Filter filter, 3927 final String... attributes) 3928 throws LDAPSearchException 3929 { 3930 return searchForEntry(new SearchRequest(baseDN, scope, 3931 DereferencePolicy.NEVER, 1, 0, false, filter, attributes)); 3932 } 3933 3934 3935 3936 /** 3937 * Processes a search operation with the provided information. It is expected 3938 * that at most one entry will be returned from the search, and that no 3939 * additional content from the successful search result (e.g., diagnostic 3940 * message or response controls) are needed. 3941 * <BR><BR> 3942 * Note that if the search does not complete successfully, an 3943 * {@code LDAPSearchException} will be thrown In some cases, one or more 3944 * search result entries or references may have been returned before the 3945 * failure response is received. In this case, the 3946 * {@code LDAPSearchException} methods like {@code getEntryCount}, 3947 * {@code getSearchEntries}, {@code getReferenceCount}, and 3948 * {@code getSearchReferences} may be used to obtain information about those 3949 * entries and references. 3950 * 3951 * @param baseDN The base DN for the search request. It must not be 3952 * {@code null}. 3953 * @param scope The scope that specifies the range of entries that 3954 * should be examined for the search. 3955 * @param derefPolicy The dereference policy the server should use for any 3956 * aliases encountered while processing the search. 3957 * @param timeLimit The maximum length of time in seconds that the server 3958 * should spend processing this search request. A value 3959 * of zero indicates that there should be no limit. 3960 * @param typesOnly Indicates whether to return only attribute names in 3961 * matching entries, or both attribute names and values. 3962 * @param filter The string representation of the filter to use to 3963 * identify matching entries. It must not be 3964 * {@code null}. 3965 * @param attributes The set of attributes that should be returned in 3966 * matching entries. It may be {@code null} or empty if 3967 * the default attribute set (all user attributes) is to 3968 * be requested. 3969 * 3970 * @return The entry that was returned from the search, or {@code null} if no 3971 * entry was returned or the base entry does not exist. 3972 * 3973 * @throws LDAPSearchException If the search does not complete successfully, 3974 * if more than a single entry is returned, or 3975 * if a problem is encountered while parsing the 3976 * provided filter string, sending the request, 3977 * or reading the response. If one or more 3978 * entries or references were returned before 3979 * the failure was encountered, then the 3980 * {@code LDAPSearchException} object may be 3981 * examined to obtain information about those 3982 * entries and/or references. 3983 */ 3984 public SearchResultEntry searchForEntry(final String baseDN, 3985 final SearchScope scope, 3986 final DereferencePolicy derefPolicy, 3987 final int timeLimit, 3988 final boolean typesOnly, 3989 final String filter, 3990 final String... attributes) 3991 throws LDAPSearchException 3992 { 3993 final SearchRequest r; 3994 try 3995 { 3996 r = new SearchRequest(baseDN, scope, derefPolicy, 1, timeLimit, typesOnly, 3997 filter, attributes); 3998 } 3999 catch (final LDAPException le) 4000 { 4001 debugException(le); 4002 throw new LDAPSearchException(le); 4003 } 4004 4005 return searchForEntry(r); 4006 } 4007 4008 4009 4010 /** 4011 * Processes a search operation with the provided information. It is expected 4012 * that at most one entry will be returned from the search, and that no 4013 * additional content from the successful search result (e.g., diagnostic 4014 * message or response controls) are needed. 4015 * <BR><BR> 4016 * Note that if the search does not complete successfully, an 4017 * {@code LDAPSearchException} will be thrown In some cases, one or more 4018 * search result entries or references may have been returned before the 4019 * failure response is received. In this case, the 4020 * {@code LDAPSearchException} methods like {@code getEntryCount}, 4021 * {@code getSearchEntries}, {@code getReferenceCount}, and 4022 * {@code getSearchReferences} may be used to obtain information about those 4023 * entries and references. 4024 * 4025 * @param baseDN The base DN for the search request. It must not be 4026 * {@code null}. 4027 * @param scope The scope that specifies the range of entries that 4028 * should be examined for the search. 4029 * @param derefPolicy The dereference policy the server should use for any 4030 * aliases encountered while processing the search. 4031 * @param timeLimit The maximum length of time in seconds that the server 4032 * should spend processing this search request. A value 4033 * of zero indicates that there should be no limit. 4034 * @param typesOnly Indicates whether to return only attribute names in 4035 * matching entries, or both attribute names and values. 4036 * @param filter The filter to use to identify matching entries. It 4037 * must not be {@code null}. 4038 * @param attributes The set of attributes that should be returned in 4039 * matching entries. It may be {@code null} or empty if 4040 * the default attribute set (all user attributes) is to 4041 * be requested. 4042 * 4043 * @return The entry that was returned from the search, or {@code null} if no 4044 * entry was returned or the base entry does not exist. 4045 * 4046 * @throws LDAPSearchException If the search does not complete successfully, 4047 * if more than a single entry is returned, or 4048 * if a problem is encountered while parsing the 4049 * provided filter string, sending the request, 4050 * or reading the response. If one or more 4051 * entries or references were returned before 4052 * the failure was encountered, then the 4053 * {@code LDAPSearchException} object may be 4054 * examined to obtain information about those 4055 * entries and/or references. 4056 */ 4057 public SearchResultEntry searchForEntry(final String baseDN, 4058 final SearchScope scope, 4059 final DereferencePolicy derefPolicy, 4060 final int timeLimit, 4061 final boolean typesOnly, 4062 final Filter filter, 4063 final String... attributes) 4064 throws LDAPSearchException 4065 { 4066 return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1, 4067 timeLimit, typesOnly, filter, attributes)); 4068 } 4069 4070 4071 4072 /** 4073 * Processes the provided search request. It is expected that at most one 4074 * entry will be returned from the search, and that no additional content from 4075 * the successful search result (e.g., diagnostic message or response 4076 * controls) are needed. 4077 * <BR><BR> 4078 * Note that if the search does not complete successfully, an 4079 * {@code LDAPSearchException} will be thrown In some cases, one or more 4080 * search result entries or references may have been returned before the 4081 * failure response is received. In this case, the 4082 * {@code LDAPSearchException} methods like {@code getEntryCount}, 4083 * {@code getSearchEntries}, {@code getReferenceCount}, and 4084 * {@code getSearchReferences} may be used to obtain information about those 4085 * entries and references. 4086 * 4087 * @param searchRequest The search request to be processed. If it is 4088 * configured with a search result listener or a size 4089 * limit other than one, then the provided request will 4090 * be duplicated with the appropriate settings. 4091 * 4092 * @return The entry that was returned from the search, or {@code null} if no 4093 * entry was returned or the base entry does not exist. 4094 * 4095 * @throws LDAPSearchException If the search does not complete successfully, 4096 * if more than a single entry is returned, or 4097 * if a problem is encountered while parsing the 4098 * provided filter string, sending the request, 4099 * or reading the response. If one or more 4100 * entries or references were returned before 4101 * the failure was encountered, then the 4102 * {@code LDAPSearchException} object may be 4103 * examined to obtain information about those 4104 * entries and/or references. 4105 */ 4106 public SearchResultEntry searchForEntry(final SearchRequest searchRequest) 4107 throws LDAPSearchException 4108 { 4109 final SearchRequest r; 4110 if ((searchRequest.getSearchResultListener() != null) || 4111 (searchRequest.getSizeLimit() != 1)) 4112 { 4113 r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(), 4114 searchRequest.getDereferencePolicy(), 1, 4115 searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(), 4116 searchRequest.getFilter(), searchRequest.getAttributes()); 4117 4118 r.setFollowReferrals(searchRequest.followReferralsInternal()); 4119 r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null)); 4120 4121 if (searchRequest.hasControl()) 4122 { 4123 r.setControlsInternal(searchRequest.getControls()); 4124 } 4125 } 4126 else 4127 { 4128 r = searchRequest; 4129 } 4130 4131 final SearchResult result; 4132 try 4133 { 4134 result = search(r); 4135 } 4136 catch (final LDAPSearchException lse) 4137 { 4138 debugException(lse); 4139 4140 if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT) 4141 { 4142 return null; 4143 } 4144 4145 throw lse; 4146 } 4147 4148 if (result.getEntryCount() == 0) 4149 { 4150 return null; 4151 } 4152 else 4153 { 4154 return result.getSearchEntries().get(0); 4155 } 4156 } 4157 4158 4159 4160 /** 4161 * Processes the provided search request. It is expected that at most one 4162 * entry will be returned from the search, and that no additional content from 4163 * the successful search result (e.g., diagnostic message or response 4164 * controls) are needed. 4165 * <BR><BR> 4166 * Note that if the search does not complete successfully, an 4167 * {@code LDAPSearchException} will be thrown In some cases, one or more 4168 * search result entries or references may have been returned before the 4169 * failure response is received. In this case, the 4170 * {@code LDAPSearchException} methods like {@code getEntryCount}, 4171 * {@code getSearchEntries}, {@code getReferenceCount}, and 4172 * {@code getSearchReferences} may be used to obtain information about those 4173 * entries and references. 4174 * 4175 * @param searchRequest The search request to be processed. If it is 4176 * configured with a search result listener or a size 4177 * limit other than one, then the provided request will 4178 * be duplicated with the appropriate settings. 4179 * 4180 * @return The entry that was returned from the search, or {@code null} if no 4181 * entry was returned or the base entry does not exist. 4182 * 4183 * @throws LDAPSearchException If the search does not complete successfully, 4184 * if more than a single entry is returned, or 4185 * if a problem is encountered while parsing the 4186 * provided filter string, sending the request, 4187 * or reading the response. If one or more 4188 * entries or references were returned before 4189 * the failure was encountered, then the 4190 * {@code LDAPSearchException} object may be 4191 * examined to obtain information about those 4192 * entries and/or references. 4193 */ 4194 public SearchResultEntry searchForEntry( 4195 final ReadOnlySearchRequest searchRequest) 4196 throws LDAPSearchException 4197 { 4198 return searchForEntry((SearchRequest) searchRequest); 4199 } 4200 4201 4202 4203 /** 4204 * Processes the provided search request as an asynchronous operation. 4205 * 4206 * @param searchRequest The search request to be processed. It must not be 4207 * {@code null}, and it must be configured with a 4208 * search result listener that is also an 4209 * {@code AsyncSearchResultListener}. 4210 * 4211 * @return An async request ID that may be used to reference the operation. 4212 * 4213 * @throws LDAPException If the provided search request does not have a 4214 * search result listener that is an 4215 * {@code AsyncSearchResultListener}, or if a problem 4216 * occurs while sending the request. 4217 */ 4218 public AsyncRequestID asyncSearch(final SearchRequest searchRequest) 4219 throws LDAPException 4220 { 4221 ensureNotNull(searchRequest); 4222 4223 final SearchResultListener searchListener = 4224 searchRequest.getSearchResultListener(); 4225 if (searchListener == null) 4226 { 4227 final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR, 4228 ERR_ASYNC_SEARCH_NO_LISTENER.get()); 4229 debugCodingError(le); 4230 throw le; 4231 } 4232 else if (! (searchListener instanceof AsyncSearchResultListener)) 4233 { 4234 final LDAPException le = new LDAPException(ResultCode.PARAM_ERROR, 4235 ERR_ASYNC_SEARCH_INVALID_LISTENER.get()); 4236 debugCodingError(le); 4237 throw le; 4238 } 4239 4240 if (synchronousMode()) 4241 { 4242 throw new LDAPException(ResultCode.NOT_SUPPORTED, 4243 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 4244 } 4245 4246 return searchRequest.processAsync(this, 4247 (AsyncSearchResultListener) searchListener); 4248 } 4249 4250 4251 4252 /** 4253 * Processes the provided search request as an asynchronous operation. 4254 * 4255 * @param searchRequest The search request to be processed. It must not be 4256 * {@code null}, and it must be configured with a 4257 * search result listener that is also an 4258 * {@code AsyncSearchResultListener}. 4259 * 4260 * @return An async request ID that may be used to reference the operation. 4261 * 4262 * @throws LDAPException If the provided search request does not have a 4263 * search result listener that is an 4264 * {@code AsyncSearchResultListener}, or if a problem 4265 * occurs while sending the request. 4266 */ 4267 public AsyncRequestID asyncSearch(final ReadOnlySearchRequest searchRequest) 4268 throws LDAPException 4269 { 4270 if (synchronousMode()) 4271 { 4272 throw new LDAPException(ResultCode.NOT_SUPPORTED, 4273 ERR_ASYNC_NOT_SUPPORTED_IN_SYNCHRONOUS_MODE.get()); 4274 } 4275 4276 return asyncSearch((SearchRequest) searchRequest); 4277 } 4278 4279 4280 4281 /** 4282 * Processes the provided generic request and returns the result. This may 4283 * be useful for cases in which it is not known what type of operation the 4284 * request represents. 4285 * 4286 * @param request The request to be processed. 4287 * 4288 * @return The result obtained from processing the request. 4289 * 4290 * @throws LDAPException If a problem occurs while sending the request or 4291 * reading the response. Note simply having a 4292 * non-success result code in the response will not 4293 * cause an exception to be thrown. 4294 */ 4295 public LDAPResult processOperation(final LDAPRequest request) 4296 throws LDAPException 4297 { 4298 return request.process(this, 1); 4299 } 4300 4301 4302 4303 /** 4304 * Retrieves the referral connector that should be used to establish 4305 * connections for use when following referrals. 4306 * 4307 * @return The referral connector that should be used to establish 4308 * connections for use when following referrals. 4309 */ 4310 public ReferralConnector getReferralConnector() 4311 { 4312 if (referralConnector == null) 4313 { 4314 return this; 4315 } 4316 else 4317 { 4318 return referralConnector; 4319 } 4320 } 4321 4322 4323 4324 /** 4325 * Specifies the referral connector that should be used to establish 4326 * connections for use when following referrals. 4327 * 4328 * @param referralConnector The referral connector that should be used to 4329 * establish connections for use when following 4330 * referrals. 4331 */ 4332 public void setReferralConnector(final ReferralConnector referralConnector) 4333 { 4334 if (referralConnector == null) 4335 { 4336 this.referralConnector = this; 4337 } 4338 else 4339 { 4340 this.referralConnector = referralConnector; 4341 } 4342 } 4343 4344 4345 4346 /** 4347 * Sends the provided LDAP message to the server over this connection. 4348 * 4349 * @param message The LDAP message to send to the target server. 4350 * @param sendTimeoutMillis The maximum length of time, in milliseconds, to 4351 * block while trying to send the request. If this 4352 * is less than or equal to zero, then no send 4353 * timeout will be enforced. 4354 * 4355 * @throws LDAPException If a problem occurs while sending the request. 4356 */ 4357 void sendMessage(final LDAPMessage message, final long sendTimeoutMillis) 4358 throws LDAPException 4359 { 4360 if (needsReconnect.compareAndSet(true, false)) 4361 { 4362 reconnect(); 4363 } 4364 4365 final LDAPConnectionInternals internals = connectionInternals; 4366 if (internals == null) 4367 { 4368 throw new LDAPException(ResultCode.SERVER_DOWN, 4369 ERR_CONN_NOT_ESTABLISHED.get()); 4370 } 4371 else 4372 { 4373 @SuppressWarnings("deprecation") 4374 final boolean autoReconnect = connectionOptions.autoReconnect(); 4375 internals.sendMessage(message, sendTimeoutMillis, autoReconnect); 4376 lastCommunicationTime = System.currentTimeMillis(); 4377 } 4378 } 4379 4380 4381 4382 /** 4383 * Retrieves the message ID that should be used for the next request sent 4384 * over this connection. 4385 * 4386 * @return The message ID that should be used for the next request sent over 4387 * this connection, or -1 if this connection is not established. 4388 */ 4389 int nextMessageID() 4390 { 4391 final LDAPConnectionInternals internals = connectionInternals; 4392 if (internals == null) 4393 { 4394 return -1; 4395 } 4396 else 4397 { 4398 return internals.nextMessageID(); 4399 } 4400 } 4401 4402 4403 4404 /** 4405 * Retrieves the disconnect info object for this connection, if available. 4406 * 4407 * @return The disconnect info for this connection, or {@code null} if none 4408 * is set. 4409 */ 4410 DisconnectInfo getDisconnectInfo() 4411 { 4412 return disconnectInfo.get(); 4413 } 4414 4415 4416 4417 /** 4418 * Sets the disconnect type, message, and cause for this connection, if those 4419 * values have not been previously set. It will not overwrite any values that 4420 * had been previously set. 4421 * <BR><BR> 4422 * This method may be called by code which is not part of the LDAP SDK to 4423 * provide additional information about the reason for the closure. In that 4424 * case, this method must be called before the call to 4425 * {@link LDAPConnection#close}. 4426 * 4427 * @param type The disconnect type. It must not be {@code null}. 4428 * @param message A message providing additional information about the 4429 * disconnect. It may be {@code null} if no message is 4430 * available. 4431 * @param cause The exception that was caught to trigger the disconnect. 4432 * It may be {@code null} if the disconnect was not triggered 4433 * by an exception. 4434 */ 4435 public void setDisconnectInfo(final DisconnectType type, final String message, 4436 final Throwable cause) 4437 { 4438 disconnectInfo.compareAndSet(null, 4439 new DisconnectInfo(this, type, message, cause)); 4440 } 4441 4442 4443 4444 /** 4445 * Sets the disconnect info for this connection, if it is not already set. 4446 * 4447 * @param info The disconnect info to be set, if it is not already set. 4448 * 4449 * @return The disconnect info set for the connection, whether it was 4450 * previously or newly set. 4451 */ 4452 DisconnectInfo setDisconnectInfo(final DisconnectInfo info) 4453 { 4454 disconnectInfo.compareAndSet(null, info); 4455 return disconnectInfo.get(); 4456 } 4457 4458 4459 4460 /** 4461 * Retrieves the disconnect type for this connection, if available. 4462 * 4463 * @return The disconnect type for this connection, or {@code null} if no 4464 * disconnect type has been set. 4465 */ 4466 public DisconnectType getDisconnectType() 4467 { 4468 final DisconnectInfo di = disconnectInfo.get(); 4469 if (di == null) 4470 { 4471 return null; 4472 } 4473 else 4474 { 4475 return di.getType(); 4476 } 4477 } 4478 4479 4480 4481 /** 4482 * Retrieves the disconnect message for this connection, which may provide 4483 * additional information about the reason for the disconnect, if available. 4484 * 4485 * @return The disconnect message for this connection, or {@code null} if 4486 * no disconnect message has been set. 4487 */ 4488 public String getDisconnectMessage() 4489 { 4490 final DisconnectInfo di = disconnectInfo.get(); 4491 if (di == null) 4492 { 4493 return null; 4494 } 4495 else 4496 { 4497 return di.getMessage(); 4498 } 4499 } 4500 4501 4502 4503 /** 4504 * Retrieves the disconnect cause for this connection, which is an exception 4505 * or error that triggered the connection termination, if available. 4506 * 4507 * @return The disconnect cause for this connection, or {@code null} if no 4508 * disconnect cause has been set. 4509 */ 4510 public Throwable getDisconnectCause() 4511 { 4512 final DisconnectInfo di = disconnectInfo.get(); 4513 if (di == null) 4514 { 4515 return null; 4516 } 4517 else 4518 { 4519 return di.getCause(); 4520 } 4521 } 4522 4523 4524 4525 /** 4526 * Indicates that this connection has been closed and is no longer available 4527 * for use. 4528 */ 4529 void setClosed() 4530 { 4531 needsReconnect.set(false); 4532 4533 if (disconnectInfo.get() == null) 4534 { 4535 try 4536 { 4537 final StackTraceElement[] stackElements = 4538 Thread.currentThread().getStackTrace(); 4539 final StackTraceElement[] parentStackElements = 4540 new StackTraceElement[stackElements.length - 1]; 4541 System.arraycopy(stackElements, 1, parentStackElements, 0, 4542 parentStackElements.length); 4543 4544 setDisconnectInfo(DisconnectType.OTHER, 4545 ERR_CONN_CLOSED_BY_UNEXPECTED_CALL_PATH.get( 4546 getStackTrace(parentStackElements)), 4547 null); 4548 } 4549 catch (final Exception e) 4550 { 4551 debugException(e); 4552 } 4553 } 4554 4555 connectionStatistics.incrementNumDisconnects(); 4556 final LDAPConnectionInternals internals = connectionInternals; 4557 if (internals != null) 4558 { 4559 internals.close(); 4560 connectionInternals = null; 4561 } 4562 4563 cachedSchema = null; 4564 lastCommunicationTime = -1L; 4565 4566 synchronized (this) 4567 { 4568 final Timer t = timer; 4569 timer = null; 4570 4571 if (t != null) 4572 { 4573 t.cancel(); 4574 } 4575 } 4576 } 4577 4578 4579 4580 /** 4581 * Registers the provided response acceptor with the connection reader. 4582 * 4583 * @param messageID The message ID for which the acceptor is to be 4584 * registered. 4585 * @param responseAcceptor The response acceptor to register. 4586 * 4587 * @throws LDAPException If another message acceptor is already registered 4588 * with the provided message ID. 4589 */ 4590 void registerResponseAcceptor(final int messageID, 4591 final ResponseAcceptor responseAcceptor) 4592 throws LDAPException 4593 { 4594 if (needsReconnect.compareAndSet(true, false)) 4595 { 4596 reconnect(); 4597 } 4598 4599 final LDAPConnectionInternals internals = connectionInternals; 4600 if (internals == null) 4601 { 4602 throw new LDAPException(ResultCode.SERVER_DOWN, 4603 ERR_CONN_NOT_ESTABLISHED.get()); 4604 } 4605 else 4606 { 4607 internals.registerResponseAcceptor(messageID, responseAcceptor); 4608 } 4609 } 4610 4611 4612 4613 /** 4614 * Deregisters the response acceptor associated with the provided message ID. 4615 * 4616 * @param messageID The message ID for which to deregister the associated 4617 * response acceptor. 4618 */ 4619 void deregisterResponseAcceptor(final int messageID) 4620 { 4621 final LDAPConnectionInternals internals = connectionInternals; 4622 if (internals != null) 4623 { 4624 internals.deregisterResponseAcceptor(messageID); 4625 } 4626 } 4627 4628 4629 4630 /** 4631 * Retrieves a timer for use with this connection, creating one if necessary. 4632 * 4633 * @return A timer for use with this connection. 4634 */ 4635 synchronized Timer getTimer() 4636 { 4637 if (timer == null) 4638 { 4639 timer = new Timer("Timer thread for " + toString(), true); 4640 } 4641 4642 return timer; 4643 } 4644 4645 4646 4647 /** 4648 * {@inheritDoc} 4649 */ 4650 @Override() 4651 public LDAPConnection getReferralConnection(final LDAPURL referralURL, 4652 final LDAPConnection connection) 4653 throws LDAPException 4654 { 4655 final String host = referralURL.getHost(); 4656 final int port = referralURL.getPort(); 4657 4658 BindRequest bindRequest = null; 4659 if (connection.lastBindRequest != null) 4660 { 4661 bindRequest = connection.lastBindRequest.getRebindRequest(host, port); 4662 if (bindRequest == null) 4663 { 4664 throw new LDAPException(ResultCode.REFERRAL, 4665 ERR_CONN_CANNOT_AUTHENTICATE_FOR_REFERRAL.get( 4666 host, port)); 4667 } 4668 } 4669 4670 final ExtendedRequest connStartTLSRequest = connection.startTLSRequest; 4671 4672 final LDAPConnection conn = new LDAPConnection(connection.socketFactory, 4673 connection.connectionOptions, host, port); 4674 4675 if (connStartTLSRequest != null) 4676 { 4677 try 4678 { 4679 final ExtendedResult startTLSResult = 4680 conn.processExtendedOperation(connStartTLSRequest); 4681 if (startTLSResult.getResultCode() != ResultCode.SUCCESS) 4682 { 4683 throw new LDAPException(startTLSResult); 4684 } 4685 } 4686 catch (final LDAPException le) 4687 { 4688 debugException(le); 4689 conn.setDisconnectInfo(DisconnectType.SECURITY_PROBLEM, null, le); 4690 conn.close(); 4691 4692 throw le; 4693 } 4694 } 4695 4696 if (bindRequest != null) 4697 { 4698 try 4699 { 4700 conn.bind(bindRequest); 4701 } 4702 catch (final LDAPException le) 4703 { 4704 debugException(le); 4705 conn.setDisconnectInfo(DisconnectType.BIND_FAILED, null, le); 4706 conn.close(); 4707 4708 throw le; 4709 } 4710 } 4711 4712 return conn; 4713 } 4714 4715 4716 4717 /** 4718 * Retrieves the last successful bind request processed on this connection. 4719 * 4720 * @return The last successful bind request processed on this connection. It 4721 * may be {@code null} if no bind has been performed, or if the last 4722 * bind attempt was not successful. 4723 */ 4724 public BindRequest getLastBindRequest() 4725 { 4726 return lastBindRequest; 4727 } 4728 4729 4730 4731 /** 4732 * Retrieves the StartTLS request used to secure this connection. 4733 * 4734 * @return The StartTLS request used to secure this connection, or 4735 * {@code null} if StartTLS has not been used to secure this 4736 * connection. 4737 */ 4738 public ExtendedRequest getStartTLSRequest() 4739 { 4740 return startTLSRequest; 4741 } 4742 4743 4744 4745 /** 4746 * Retrieves an instance of the {@code LDAPConnectionInternals} object for 4747 * this connection. 4748 * 4749 * @param throwIfDisconnected Indicates whether to throw an 4750 * {@code LDAPException} if the connection is not 4751 * established. 4752 * 4753 * @return The {@code LDAPConnectionInternals} object for this connection, or 4754 * {@code null} if the connection is not established and no exception 4755 * should be thrown. 4756 * 4757 * @throws LDAPException If the connection is not established and 4758 * {@code throwIfDisconnected} is {@code true}. 4759 */ 4760 LDAPConnectionInternals getConnectionInternals( 4761 final boolean throwIfDisconnected) 4762 throws LDAPException 4763 { 4764 final LDAPConnectionInternals internals = connectionInternals; 4765 if ((internals == null) && throwIfDisconnected) 4766 { 4767 throw new LDAPException(ResultCode.SERVER_DOWN, 4768 ERR_CONN_NOT_ESTABLISHED.get()); 4769 } 4770 else 4771 { 4772 return internals; 4773 } 4774 } 4775 4776 4777 4778 /** 4779 * Retrieves the cached schema for this connection, if applicable. 4780 * 4781 * @return The cached schema for this connection, or {@code null} if it is 4782 * not available (e.g., because the connection is not established, 4783 * because {@link LDAPConnectionOptions#useSchema()} is false, or 4784 * because an error occurred when trying to read the server schema). 4785 */ 4786 Schema getCachedSchema() 4787 { 4788 return cachedSchema; 4789 } 4790 4791 4792 4793 /** 4794 * Sets the cached schema for this connection. 4795 * 4796 * @param cachedSchema The cached schema for this connection. It may be 4797 * {@code null} if no cached schema is available. 4798 */ 4799 void setCachedSchema(final Schema cachedSchema) 4800 { 4801 this.cachedSchema = cachedSchema; 4802 } 4803 4804 4805 4806 /** 4807 * Indicates whether this connection is operating in synchronous mode. 4808 * 4809 * @return {@code true} if this connection is operating in synchronous mode, 4810 * or {@code false} if not. 4811 */ 4812 public boolean synchronousMode() 4813 { 4814 final LDAPConnectionInternals internals = connectionInternals; 4815 if (internals == null) 4816 { 4817 return false; 4818 } 4819 else 4820 { 4821 return internals.synchronousMode(); 4822 } 4823 } 4824 4825 4826 4827 /** 4828 * Reads a response from the server, blocking if necessary until the response 4829 * has been received. This should only be used for connections operating in 4830 * synchronous mode. 4831 * 4832 * @param messageID The message ID for the response to be read. Any 4833 * response read with a different message ID will be 4834 * discarded, unless it is an unsolicited notification in 4835 * which case it will be provided to any registered 4836 * unsolicited notification handler. 4837 * 4838 * @return The response read from the server. 4839 * 4840 * @throws LDAPException If a problem occurs while reading the response. 4841 */ 4842 LDAPResponse readResponse(final int messageID) 4843 throws LDAPException 4844 { 4845 final LDAPConnectionInternals internals = connectionInternals; 4846 if (internals != null) 4847 { 4848 final LDAPResponse response = 4849 internals.getConnectionReader().readResponse(messageID); 4850 debugLDAPResult(response, this); 4851 return response; 4852 } 4853 else 4854 { 4855 final DisconnectInfo di = disconnectInfo.get(); 4856 if (di == null) 4857 { 4858 return new ConnectionClosedResponse(ResultCode.CONNECT_ERROR, 4859 ERR_CONN_READ_RESPONSE_NOT_ESTABLISHED.get()); 4860 } 4861 else 4862 { 4863 return new ConnectionClosedResponse(di.getType().getResultCode(), 4864 di.getMessage()); 4865 } 4866 } 4867 } 4868 4869 4870 4871 /** 4872 * Retrieves the time that this connection was established in the number of 4873 * milliseconds since January 1, 1970 UTC (the same format used by 4874 * {@code System.currentTimeMillis}. 4875 * 4876 * @return The time that this connection was established, or -1 if the 4877 * connection is not currently established. 4878 */ 4879 public long getConnectTime() 4880 { 4881 final LDAPConnectionInternals internals = connectionInternals; 4882 if (internals != null) 4883 { 4884 return internals.getConnectTime(); 4885 } 4886 else 4887 { 4888 return -1L; 4889 } 4890 } 4891 4892 4893 4894 /** 4895 * Retrieves the time that this connection was last used to send or receive an 4896 * LDAP message. The value will represent the number of milliseconds since 4897 * January 1, 1970 UTC (the same format used by 4898 * {@code System.currentTimeMillis}. 4899 * 4900 * @return The time that this connection was last used to send or receive an 4901 * LDAP message. If the connection is not established, then -1 will 4902 * be returned. If the connection is established but no 4903 * communication has been performed over the connection since it was 4904 * established, then the value of {@link #getConnectTime()} will be 4905 * returned. 4906 */ 4907 public long getLastCommunicationTime() 4908 { 4909 if (lastCommunicationTime > 0L) 4910 { 4911 return lastCommunicationTime; 4912 } 4913 else 4914 { 4915 return getConnectTime(); 4916 } 4917 } 4918 4919 4920 4921 /** 4922 * Updates the last communication time for this connection to be the current 4923 * time. 4924 */ 4925 void setLastCommunicationTime() 4926 { 4927 lastCommunicationTime = System.currentTimeMillis(); 4928 } 4929 4930 4931 4932 /** 4933 * Retrieves the connection statistics for this LDAP connection. 4934 * 4935 * @return The connection statistics for this LDAP connection. 4936 */ 4937 public LDAPConnectionStatistics getConnectionStatistics() 4938 { 4939 return connectionStatistics; 4940 } 4941 4942 4943 4944 /** 4945 * Retrieves the number of outstanding operations on this LDAP connection 4946 * (i.e., the number of operations currently in progress). The value will 4947 * only be valid for connections not configured to use synchronous mode. 4948 * 4949 * @return The number of outstanding operations on this LDAP connection, or 4950 * -1 if it cannot be determined (e.g., because the connection is not 4951 * established or is operating in synchronous mode). 4952 */ 4953 public int getActiveOperationCount() 4954 { 4955 final LDAPConnectionInternals internals = connectionInternals; 4956 4957 if (internals == null) 4958 { 4959 return -1; 4960 } 4961 else 4962 { 4963 if (internals.synchronousMode()) 4964 { 4965 return -1; 4966 } 4967 else 4968 { 4969 return internals.getConnectionReader().getActiveOperationCount(); 4970 } 4971 } 4972 } 4973 4974 4975 4976 /** 4977 * Retrieves the schema from the provided connection. If the retrieved schema 4978 * matches schema that's already in use by other connections, the common 4979 * schema will be used instead of the newly-retrieved version. 4980 * 4981 * @param c The connection for which to retrieve the schema. 4982 * 4983 * @return The schema retrieved from the given connection, or a cached 4984 * schema if it matched a schema that was already in use. 4985 * 4986 * @throws LDAPException If a problem is encountered while retrieving or 4987 * parsing the schema. 4988 */ 4989 private static Schema getCachedSchema(final LDAPConnection c) 4990 throws LDAPException 4991 { 4992 final Schema s = c.getSchema(); 4993 4994 synchronized (SCHEMA_SET) 4995 { 4996 return SCHEMA_SET.addAndGet(s); 4997 } 4998 } 4999 5000 5001 5002 /** 5003 * Retrieves the connection attachment with the specified name. 5004 * 5005 * @param name The name of the attachment to retrieve. It must not be 5006 * {@code null}. 5007 * 5008 * @return The connection attachment with the specified name, or {@code null} 5009 * if there is no such attachment. 5010 */ 5011 synchronized Object getAttachment(final String name) 5012 { 5013 if (attachments == null) 5014 { 5015 return null; 5016 } 5017 else 5018 { 5019 return attachments.get(name); 5020 } 5021 } 5022 5023 5024 5025 /** 5026 * Sets a connection attachment with the specified name and value. 5027 * 5028 * @param name The name of the attachment to set. It must not be 5029 * {@code null}. 5030 * @param value The value to use for the attachment. It may be {@code null} 5031 * if an attachment with the specified name should be cleared 5032 * rather than overwritten. 5033 */ 5034 synchronized void setAttachment(final String name, final Object value) 5035 { 5036 if (attachments == null) 5037 { 5038 attachments = new HashMap<String,Object>(10); 5039 } 5040 5041 if (value == null) 5042 { 5043 attachments.remove(name); 5044 } 5045 else 5046 { 5047 attachments.put(name, value); 5048 } 5049 } 5050 5051 5052 5053 /** 5054 * Performs any necessary cleanup to ensure that this connection is properly 5055 * closed before it is garbage collected. 5056 * 5057 * @throws Throwable If the superclass finalizer throws an exception. 5058 */ 5059 @Override() 5060 protected void finalize() 5061 throws Throwable 5062 { 5063 super.finalize(); 5064 5065 setDisconnectInfo(DisconnectType.CLOSED_BY_FINALIZER, null, null); 5066 setClosed(); 5067 } 5068 5069 5070 5071 /** 5072 * Retrieves a string representation of this LDAP connection. 5073 * 5074 * @return A string representation of this LDAP connection. 5075 */ 5076 @Override() 5077 public String toString() 5078 { 5079 final StringBuilder buffer = new StringBuilder(); 5080 toString(buffer); 5081 return buffer.toString(); 5082 } 5083 5084 5085 5086 /** 5087 * Appends a string representation of this LDAP connection to the provided 5088 * buffer. 5089 * 5090 * @param buffer The buffer to which to append a string representation of 5091 * this LDAP connection. 5092 */ 5093 public void toString(final StringBuilder buffer) 5094 { 5095 buffer.append("LDAPConnection("); 5096 5097 final String name = connectionName; 5098 final String poolName = connectionPoolName; 5099 if (name != null) 5100 { 5101 buffer.append("name='"); 5102 buffer.append(name); 5103 buffer.append("', "); 5104 } 5105 else if (poolName != null) 5106 { 5107 buffer.append("poolName='"); 5108 buffer.append(poolName); 5109 buffer.append("', "); 5110 } 5111 5112 final LDAPConnectionInternals internals = connectionInternals; 5113 if ((internals != null) && internals.isConnected()) 5114 { 5115 buffer.append("connected to "); 5116 buffer.append(internals.getHost()); 5117 buffer.append(':'); 5118 buffer.append(internals.getPort()); 5119 } 5120 else 5121 { 5122 buffer.append("not connected"); 5123 } 5124 5125 buffer.append(')'); 5126 } 5127}