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