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