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