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