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