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