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