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