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