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