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