001 /* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021 package com.unboundid.ldap.sdk; 022 023 024 025 import java.util.Timer; 026 import java.util.concurrent.LinkedBlockingQueue; 027 import java.util.concurrent.TimeUnit; 028 029 import com.unboundid.asn1.ASN1Buffer; 030 import com.unboundid.asn1.ASN1BufferSequence; 031 import com.unboundid.asn1.ASN1Element; 032 import com.unboundid.asn1.ASN1OctetString; 033 import com.unboundid.asn1.ASN1Sequence; 034 import com.unboundid.ldap.protocol.LDAPMessage; 035 import com.unboundid.ldap.protocol.LDAPResponse; 036 import com.unboundid.ldap.protocol.ProtocolOp; 037 import com.unboundid.util.InternalUseOnly; 038 039 import static com.unboundid.ldap.sdk.LDAPMessages.*; 040 import static com.unboundid.util.Debug.*; 041 import static com.unboundid.util.StaticUtils.*; 042 import static com.unboundid.util.Validator.*; 043 044 045 046 /** 047 * This class implements the processing necessary to perform an LDAPv3 compare 048 * operation, which may be used to determine whether a specified entry contains 049 * a given attribute value. Compare requests include the DN of the target 050 * entry, the name of the target attribute, and the value for which to make the 051 * determination. It may also include a set of controls to send to the server. 052 * <BR><BR> 053 * The assertion value may be specified as either a string or a byte array. If 054 * it is specified as a byte array, then it may represent either a binary or a 055 * string value. If a string value is provided as a byte array, then it should 056 * use the UTF-8 encoding for that value. 057 * <BR><BR> 058 * {@code CompareRequest} objects are mutable and therefore can be altered and 059 * re-used for multiple requests. Note, however, that {@code CompareRequest} 060 * objects are not threadsafe and therefore a single {@code CompareRequest} 061 * object instance should not be used to process multiple requests at the same 062 * time. 063 * <BR><BR> 064 * <H2>Example</H2> 065 * The following example demonstrates the process for performing a compare 066 * operation: 067 * <PRE> 068 * CompareRequest compareRequest = 069 * new CompareRequest("dc=example,dc=com", "description", "test"); 070 * CompareResult compareResult; 071 * try 072 * { 073 * compareResult = connection.compare(compareRequest); 074 * 075 * // The compare operation didn't throw an exception, so we can try to 076 * // determine whether the compare matched. 077 * if (compareResult.compareMatched()) 078 * { 079 * // The entry does have a description value of test. 080 * } 081 * else 082 * { 083 * // The entry does not have a description value of test. 084 * } 085 * } 086 * catch (LDAPException le) 087 * { 088 * // The compare operation failed. 089 * compareResult = new CompareResult(le.toLDAPResult()); 090 * ResultCode resultCode = le.getResultCode(); 091 * String errorMessageFromServer = le.getDiagnosticMessage(); 092 * } 093 * </PRE> 094 */ 095 public final class CompareRequest 096 extends UpdatableLDAPRequest 097 implements ReadOnlyCompareRequest, ResponseAcceptor, ProtocolOp 098 { 099 /** 100 * The serial version UID for this serializable class. 101 */ 102 private static final long serialVersionUID = 6343453776330347024L; 103 104 105 106 // The queue that will be used to receive response messages from the server. 107 private final LinkedBlockingQueue<LDAPResponse> responseQueue = 108 new LinkedBlockingQueue<LDAPResponse>(); 109 110 // The assertion value for this compare request. 111 private ASN1OctetString assertionValue; 112 113 // The message ID from the last LDAP message sent from this request. 114 private int messageID = -1; 115 116 // The name of the target attribute. 117 private String attributeName; 118 119 // The DN of the entry in which the comparison is to be performed. 120 private String dn; 121 122 123 124 /** 125 * Creates a new compare request with the provided information. 126 * 127 * @param dn The DN of the entry in which the comparison is to 128 * be performed. It must not be {@code null}. 129 * @param attributeName The name of the target attribute for which the 130 * comparison is to be performed. It must not be 131 * {@code null}. 132 * @param assertionValue The assertion value to verify within the entry. It 133 * must not be {@code null}. 134 */ 135 public CompareRequest(final String dn, final String attributeName, 136 final String assertionValue) 137 { 138 super(null); 139 140 ensureNotNull(dn, attributeName, assertionValue); 141 142 this.dn = dn; 143 this.attributeName = attributeName; 144 this.assertionValue = new ASN1OctetString(assertionValue); 145 } 146 147 148 149 /** 150 * Creates a new compare request with the provided information. 151 * 152 * @param dn The DN of the entry in which the comparison is to 153 * be performed. It must not be {@code null}. 154 * @param attributeName The name of the target attribute for which the 155 * comparison is to be performed. It must not be 156 * {@code null}. 157 * @param assertionValue The assertion value to verify within the entry. It 158 * must not be {@code null}. 159 */ 160 public CompareRequest(final String dn, final String attributeName, 161 final byte[] assertionValue) 162 { 163 super(null); 164 165 ensureNotNull(dn, attributeName, assertionValue); 166 167 this.dn = dn; 168 this.attributeName = attributeName; 169 this.assertionValue = new ASN1OctetString(assertionValue); 170 } 171 172 173 174 /** 175 * Creates a new compare request with the provided information. 176 * 177 * @param dn The DN of the entry in which the comparison is to 178 * be performed. It must not be {@code null}. 179 * @param attributeName The name of the target attribute for which the 180 * comparison is to be performed. It must not be 181 * {@code null}. 182 * @param assertionValue The assertion value to verify within the entry. It 183 * must not be {@code null}. 184 */ 185 public CompareRequest(final DN dn, final String attributeName, 186 final String assertionValue) 187 { 188 super(null); 189 190 ensureNotNull(dn, attributeName, assertionValue); 191 192 this.dn = dn.toString(); 193 this.attributeName = attributeName; 194 this.assertionValue = new ASN1OctetString(assertionValue); 195 } 196 197 198 199 /** 200 * Creates a new compare request with the provided information. 201 * 202 * @param dn The DN of the entry in which the comparison is to 203 * be performed. It must not be {@code null}. 204 * @param attributeName The name of the target attribute for which the 205 * comparison is to be performed. It must not be 206 * {@code null}. 207 * @param assertionValue The assertion value to verify within the entry. It 208 * must not be {@code null}. 209 */ 210 public CompareRequest(final DN dn, final String attributeName, 211 final byte[] assertionValue) 212 { 213 super(null); 214 215 ensureNotNull(dn, attributeName, assertionValue); 216 217 this.dn = dn.toString(); 218 this.attributeName = attributeName; 219 this.assertionValue = new ASN1OctetString(assertionValue); 220 } 221 222 223 224 /** 225 * Creates a new compare request with the provided information. 226 * 227 * @param dn The DN of the entry in which the comparison is to 228 * be performed. It must not be {@code null}. 229 * @param attributeName The name of the target attribute for which the 230 * comparison is to be performed. It must not be 231 * {@code null}. 232 * @param assertionValue The assertion value to verify within the entry. It 233 * must not be {@code null}. 234 * @param controls The set of controls for this compare request. 235 */ 236 public CompareRequest(final String dn, final String attributeName, 237 final String assertionValue, final Control[] controls) 238 { 239 super(controls); 240 241 ensureNotNull(dn, attributeName, assertionValue); 242 243 this.dn = dn; 244 this.attributeName = attributeName; 245 this.assertionValue = new ASN1OctetString(assertionValue); 246 } 247 248 249 250 /** 251 * Creates a new compare request with the provided information. 252 * 253 * @param dn The DN of the entry in which the comparison is to 254 * be performed. It must not be {@code null}. 255 * @param attributeName The name of the target attribute for which the 256 * comparison is to be performed. It must not be 257 * {@code null}. 258 * @param assertionValue The assertion value to verify within the entry. It 259 * must not be {@code null}. 260 * @param controls The set of controls for this compare request. 261 */ 262 public CompareRequest(final String dn, final String attributeName, 263 final byte[] assertionValue, final Control[] controls) 264 { 265 super(controls); 266 267 ensureNotNull(dn, attributeName, assertionValue); 268 269 this.dn = dn; 270 this.attributeName = attributeName; 271 this.assertionValue = new ASN1OctetString(assertionValue); 272 } 273 274 275 276 /** 277 * Creates a new compare request with the provided information. 278 * 279 * @param dn The DN of the entry in which the comparison is to 280 * be performed. It must not be {@code null}. 281 * @param attributeName The name of the target attribute for which the 282 * comparison is to be performed. It must not be 283 * {@code null}. 284 * @param assertionValue The assertion value to verify within the entry. It 285 * must not be {@code null}. 286 * @param controls The set of controls for this compare request. 287 */ 288 public CompareRequest(final DN dn, final String attributeName, 289 final String assertionValue, final Control[] controls) 290 { 291 super(controls); 292 293 ensureNotNull(dn, attributeName, assertionValue); 294 295 this.dn = dn.toString(); 296 this.attributeName = attributeName; 297 this.assertionValue = new ASN1OctetString(assertionValue); 298 } 299 300 301 302 /** 303 * Creates a new compare request with the provided information. 304 * 305 * @param dn The DN of the entry in which the comparison is to 306 * be performed. It must not be {@code null}. 307 * @param attributeName The name of the target attribute for which the 308 * comparison is to be performed. It must not be 309 * {@code null}. 310 * @param assertionValue The assertion value to verify within the entry. It 311 * must not be {@code null}. 312 * @param controls The set of controls for this compare request. 313 */ 314 public CompareRequest(final DN dn, final String attributeName, 315 final ASN1OctetString assertionValue, 316 final Control[] controls) 317 { 318 super(controls); 319 320 ensureNotNull(dn, attributeName, assertionValue); 321 322 this.dn = dn.toString(); 323 this.attributeName = attributeName; 324 this.assertionValue = assertionValue; 325 } 326 327 328 329 /** 330 * Creates a new compare request with the provided information. 331 * 332 * @param dn The DN of the entry in which the comparison is to 333 * be performed. It must not be {@code null}. 334 * @param attributeName The name of the target attribute for which the 335 * comparison is to be performed. It must not be 336 * {@code null}. 337 * @param assertionValue The assertion value to verify within the entry. It 338 * must not be {@code null}. 339 * @param controls The set of controls for this compare request. 340 */ 341 public CompareRequest(final DN dn, final String attributeName, 342 final byte[] assertionValue, final Control[] controls) 343 { 344 super(controls); 345 346 ensureNotNull(dn, attributeName, assertionValue); 347 348 this.dn = dn.toString(); 349 this.attributeName = attributeName; 350 this.assertionValue = new ASN1OctetString(assertionValue); 351 } 352 353 354 355 /** 356 * {@inheritDoc} 357 */ 358 public String getDN() 359 { 360 return dn; 361 } 362 363 364 365 /** 366 * Specifies the DN of the entry in which the comparison is to be performed. 367 * 368 * @param dn The DN of the entry in which the comparison is to be performed. 369 * It must not be {@code null}. 370 */ 371 public void setDN(final String dn) 372 { 373 ensureNotNull(dn); 374 375 this.dn = dn; 376 } 377 378 379 380 /** 381 * Specifies the DN of the entry in which the comparison is to be performed. 382 * 383 * @param dn The DN of the entry in which the comparison is to be performed. 384 * It must not be {@code null}. 385 */ 386 public void setDN(final DN dn) 387 { 388 ensureNotNull(dn); 389 390 this.dn = dn.toString(); 391 } 392 393 394 395 /** 396 * {@inheritDoc} 397 */ 398 public String getAttributeName() 399 { 400 return attributeName; 401 } 402 403 404 405 /** 406 * Specifies the name of the attribute for which the comparison is to be 407 * performed. 408 * 409 * @param attributeName The name of the attribute for which the comparison 410 * is to be performed. It must not be {@code null}. 411 */ 412 public void setAttributeName(final String attributeName) 413 { 414 ensureNotNull(attributeName); 415 416 this.attributeName = attributeName; 417 } 418 419 420 421 /** 422 * {@inheritDoc} 423 */ 424 public String getAssertionValue() 425 { 426 return assertionValue.stringValue(); 427 } 428 429 430 431 /** 432 * {@inheritDoc} 433 */ 434 public byte[] getAssertionValueBytes() 435 { 436 return assertionValue.getValue(); 437 } 438 439 440 441 /** 442 * {@inheritDoc} 443 */ 444 public ASN1OctetString getRawAssertionValue() 445 { 446 return assertionValue; 447 } 448 449 450 451 /** 452 * Specifies the assertion value to specify within the target entry. 453 * 454 * @param assertionValue The assertion value to specify within the target 455 * entry. It must not be {@code null}. 456 */ 457 public void setAssertionValue(final String assertionValue) 458 { 459 ensureNotNull(assertionValue); 460 461 this.assertionValue = new ASN1OctetString(assertionValue); 462 } 463 464 465 466 /** 467 * Specifies the assertion value to specify within the target entry. 468 * 469 * @param assertionValue The assertion value to specify within the target 470 * entry. It must not be {@code null}. 471 */ 472 public void setAssertionValue(final byte[] assertionValue) 473 { 474 ensureNotNull(assertionValue); 475 476 this.assertionValue = new ASN1OctetString(assertionValue); 477 } 478 479 480 481 /** 482 * Specifies the assertion value to specify within the target entry. 483 * 484 * @param assertionValue The assertion value to specify within the target 485 * entry. It must not be {@code null}. 486 */ 487 public void setAssertionValue(final ASN1OctetString assertionValue) 488 { 489 this.assertionValue = assertionValue; 490 } 491 492 493 494 /** 495 * {@inheritDoc} 496 */ 497 public byte getProtocolOpType() 498 { 499 return LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST; 500 } 501 502 503 504 /** 505 * {@inheritDoc} 506 */ 507 public void writeTo(final ASN1Buffer buffer) 508 { 509 final ASN1BufferSequence requestSequence = 510 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST); 511 buffer.addOctetString(dn); 512 513 final ASN1BufferSequence avaSequence = buffer.beginSequence(); 514 buffer.addOctetString(attributeName); 515 buffer.addElement(assertionValue); 516 avaSequence.end(); 517 requestSequence.end(); 518 } 519 520 521 522 /** 523 * Encodes the compare request protocol op to an ASN.1 element. 524 * 525 * @return The ASN.1 element with the encoded compare request protocol op. 526 */ 527 public ASN1Element encodeProtocolOp() 528 { 529 // Create the compare request protocol op. 530 final ASN1Element[] avaElements = 531 { 532 new ASN1OctetString(attributeName), 533 assertionValue 534 }; 535 536 final ASN1Element[] protocolOpElements = 537 { 538 new ASN1OctetString(dn), 539 new ASN1Sequence(avaElements) 540 }; 541 542 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST, 543 protocolOpElements); 544 } 545 546 547 548 /** 549 * Sends this delete request to the directory server over the provided 550 * connection and returns the associated response. 551 * 552 * @param connection The connection to use to communicate with the directory 553 * server. 554 * @param depth The current referral depth for this request. It should 555 * always be one for the initial request, and should only 556 * be incremented when following referrals. 557 * 558 * @return An LDAP result object that provides information about the result 559 * of the delete processing. 560 * 561 * @throws LDAPException If a problem occurs while sending the request or 562 * reading the response. 563 */ 564 @Override() 565 protected CompareResult process(final LDAPConnection connection, 566 final int depth) 567 throws LDAPException 568 { 569 if (connection.synchronousMode()) 570 { 571 return processSync(connection, depth, 572 connection.getConnectionOptions().autoReconnect()); 573 } 574 575 final long requestTime = System.nanoTime(); 576 processAsync(connection, null); 577 578 try 579 { 580 // Wait for and process the response. 581 final LDAPResponse response; 582 try 583 { 584 final long responseTimeout = getResponseTimeoutMillis(connection); 585 if (responseTimeout > 0) 586 { 587 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS); 588 } 589 else 590 { 591 response = responseQueue.take(); 592 } 593 } 594 catch (InterruptedException ie) 595 { 596 debugException(ie); 597 throw new LDAPException(ResultCode.LOCAL_ERROR, 598 ERR_COMPARE_INTERRUPTED.get(connection.getHostPort()), ie); 599 } 600 601 return handleResponse(connection, response, requestTime, depth, false); 602 } 603 finally 604 { 605 connection.deregisterResponseAcceptor(messageID); 606 } 607 } 608 609 610 611 /** 612 * Sends this compare request to the directory server over the provided 613 * connection and returns the message ID for the request. 614 * 615 * @param connection The connection to use to communicate with the 616 * directory server. 617 * @param resultListener The async result listener that is to be notified 618 * when the response is received. It may be 619 * {@code null} only if the result is to be processed 620 * by this class. 621 * 622 * @return The async request ID created for the operation, or {@code null} if 623 * the provided {@code resultListener} is {@code null} and the 624 * operation will not actually be processed asynchronously. 625 * 626 * @throws LDAPException If a problem occurs while sending the request. 627 */ 628 AsyncRequestID processAsync(final LDAPConnection connection, 629 final AsyncCompareResultListener resultListener) 630 throws LDAPException 631 { 632 // Create the LDAP message. 633 messageID = connection.nextMessageID(); 634 final LDAPMessage message = new LDAPMessage(messageID, this, getControls()); 635 636 637 // If the provided async result listener is {@code null}, then we'll use 638 // this class as the message acceptor. Otherwise, create an async helper 639 // and use it as the message acceptor. 640 final AsyncRequestID asyncRequestID; 641 if (resultListener == null) 642 { 643 asyncRequestID = null; 644 connection.registerResponseAcceptor(messageID, this); 645 } 646 else 647 { 648 final AsyncCompareHelper compareHelper = 649 new AsyncCompareHelper(connection, messageID, resultListener, 650 getIntermediateResponseListener()); 651 connection.registerResponseAcceptor(messageID, compareHelper); 652 asyncRequestID = compareHelper.getAsyncRequestID(); 653 654 final long timeout = getResponseTimeoutMillis(connection); 655 if (timeout > 0L) 656 { 657 final Timer timer = connection.getTimer(); 658 final AsyncTimeoutTimerTask timerTask = 659 new AsyncTimeoutTimerTask(compareHelper); 660 timer.schedule(timerTask, timeout); 661 asyncRequestID.setTimerTask(timerTask); 662 } 663 } 664 665 666 // Send the request to the server. 667 try 668 { 669 debugLDAPRequest(this); 670 connection.getConnectionStatistics().incrementNumCompareRequests(); 671 connection.sendMessage(message); 672 return asyncRequestID; 673 } 674 catch (LDAPException le) 675 { 676 debugException(le); 677 678 connection.deregisterResponseAcceptor(messageID); 679 throw le; 680 } 681 } 682 683 684 685 /** 686 * Processes this compare operation in synchronous mode, in which the same 687 * thread will send the request and read the response. 688 * 689 * @param connection The connection to use to communicate with the directory 690 * server. 691 * @param depth The current referral depth for this request. It should 692 * always be one for the initial request, and should only 693 * be incremented when following referrals. 694 * @param allowRetry Indicates whether the request may be re-tried on a 695 * re-established connection if the initial attempt fails 696 * in a way that indicates the connection is no longer 697 * valid and autoReconnect is true. 698 * 699 * @return An LDAP result object that provides information about the result 700 * of the compare processing. 701 * 702 * @throws LDAPException If a problem occurs while sending the request or 703 * reading the response. 704 */ 705 private CompareResult processSync(final LDAPConnection connection, 706 final int depth, final boolean allowRetry) 707 throws LDAPException 708 { 709 // Create the LDAP message. 710 messageID = connection.nextMessageID(); 711 final LDAPMessage message = 712 new LDAPMessage(messageID, this, getControls()); 713 714 715 // Set the appropriate timeout on the socket. 716 try 717 { 718 connection.getConnectionInternals(true).getSocket().setSoTimeout( 719 (int) getResponseTimeoutMillis(connection)); 720 } 721 catch (Exception e) 722 { 723 debugException(e); 724 } 725 726 727 // Send the request to the server. 728 final long requestTime = System.nanoTime(); 729 debugLDAPRequest(this); 730 connection.getConnectionStatistics().incrementNumCompareRequests(); 731 try 732 { 733 connection.sendMessage(message); 734 } 735 catch (final LDAPException le) 736 { 737 debugException(le); 738 739 if (allowRetry) 740 { 741 final CompareResult retryResult = reconnectAndRetry(connection, depth, 742 le.getResultCode()); 743 if (retryResult != null) 744 { 745 return retryResult; 746 } 747 } 748 749 throw le; 750 } 751 752 while (true) 753 { 754 final LDAPResponse response; 755 try 756 { 757 response = connection.readResponse(messageID); 758 } 759 catch (final LDAPException le) 760 { 761 debugException(le); 762 763 if ((le.getResultCode() == ResultCode.TIMEOUT) && 764 connection.getConnectionOptions().abandonOnTimeout()) 765 { 766 connection.abandon(messageID); 767 } 768 769 if (allowRetry) 770 { 771 final CompareResult retryResult = reconnectAndRetry(connection, depth, 772 le.getResultCode()); 773 if (retryResult != null) 774 { 775 return retryResult; 776 } 777 } 778 779 throw le; 780 } 781 782 if (response instanceof IntermediateResponse) 783 { 784 final IntermediateResponseListener listener = 785 getIntermediateResponseListener(); 786 if (listener != null) 787 { 788 listener.intermediateResponseReturned( 789 (IntermediateResponse) response); 790 } 791 } 792 else 793 { 794 return handleResponse(connection, response, requestTime, depth, 795 allowRetry); 796 } 797 } 798 } 799 800 801 802 /** 803 * Performs the necessary processing for handling a response. 804 * 805 * @param connection The connection used to read the response. 806 * @param response The response to be processed. 807 * @param requestTime The time the request was sent to the server. 808 * @param depth The current referral depth for this request. It 809 * should always be one for the initial request, and 810 * should only be incremented when following referrals. 811 * @param allowRetry Indicates whether the request may be re-tried on a 812 * re-established connection if the initial attempt fails 813 * in a way that indicates the connection is no longer 814 * valid and autoReconnect is true. 815 * 816 * @return The compare result. 817 * 818 * @throws LDAPException If a problem occurs. 819 */ 820 private CompareResult handleResponse(final LDAPConnection connection, 821 final LDAPResponse response, 822 final long requestTime, final int depth, 823 final boolean allowRetry) 824 throws LDAPException 825 { 826 if (response == null) 827 { 828 final long waitTime = nanosToMillis(System.nanoTime() - requestTime); 829 if (connection.getConnectionOptions().abandonOnTimeout()) 830 { 831 connection.abandon(messageID); 832 } 833 834 throw new LDAPException(ResultCode.TIMEOUT, 835 ERR_COMPARE_CLIENT_TIMEOUT.get(waitTime, messageID, dn, 836 connection.getHostPort())); 837 } 838 839 connection.getConnectionStatistics().incrementNumCompareResponses( 840 System.nanoTime() - requestTime); 841 if (response instanceof ConnectionClosedResponse) 842 { 843 // The connection was closed while waiting for the response. 844 if (allowRetry) 845 { 846 final CompareResult retryResult = reconnectAndRetry(connection, depth, 847 ResultCode.SERVER_DOWN); 848 if (retryResult != null) 849 { 850 return retryResult; 851 } 852 } 853 854 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 855 final String message = ccr.getMessage(); 856 if (message == null) 857 { 858 throw new LDAPException(ccr.getResultCode(), 859 ERR_CONN_CLOSED_WAITING_FOR_COMPARE_RESPONSE.get( 860 connection.getHostPort(), toString())); 861 } 862 else 863 { 864 throw new LDAPException(ccr.getResultCode(), 865 ERR_CONN_CLOSED_WAITING_FOR_COMPARE_RESPONSE_WITH_MESSAGE.get( 866 connection.getHostPort(), toString(), message)); 867 } 868 } 869 870 final CompareResult result; 871 if (response instanceof CompareResult) 872 { 873 result = (CompareResult) response; 874 } 875 else 876 { 877 result = new CompareResult((LDAPResult) response); 878 } 879 880 if ((result.getResultCode().equals(ResultCode.REFERRAL)) && 881 followReferrals(connection)) 882 { 883 if (depth >= connection.getConnectionOptions().getReferralHopLimit()) 884 { 885 return new CompareResult(messageID, 886 ResultCode.REFERRAL_LIMIT_EXCEEDED, 887 ERR_TOO_MANY_REFERRALS.get(), 888 result.getMatchedDN(), 889 result.getReferralURLs(), 890 result.getResponseControls()); 891 } 892 893 return followReferral(result, connection, depth); 894 } 895 else 896 { 897 if (allowRetry) 898 { 899 final CompareResult retryResult = reconnectAndRetry(connection, depth, 900 result.getResultCode()); 901 if (retryResult != null) 902 { 903 return retryResult; 904 } 905 } 906 907 return result; 908 } 909 } 910 911 912 913 /** 914 * Attempts to re-establish the connection and retry processing this request 915 * on it. 916 * 917 * @param connection The connection to be re-established. 918 * @param depth The current referral depth for this request. It should 919 * always be one for the initial request, and should only 920 * be incremented when following referrals. 921 * @param resultCode The result code for the previous operation attempt. 922 * 923 * @return The result from re-trying the compare, or {@code null} if it could 924 * not be re-tried. 925 */ 926 private CompareResult reconnectAndRetry(final LDAPConnection connection, 927 final int depth, 928 final ResultCode resultCode) 929 { 930 try 931 { 932 // We will only want to retry for certain result codes that indicate a 933 // connection problem. 934 switch (resultCode.intValue()) 935 { 936 case ResultCode.SERVER_DOWN_INT_VALUE: 937 case ResultCode.DECODING_ERROR_INT_VALUE: 938 case ResultCode.CONNECT_ERROR_INT_VALUE: 939 connection.reconnect(); 940 return processSync(connection, depth, false); 941 } 942 } 943 catch (final Exception e) 944 { 945 debugException(e); 946 } 947 948 return null; 949 } 950 951 952 953 /** 954 * Attempts to follow a referral to perform a compare operation in the target 955 * server. 956 * 957 * @param referralResult The LDAP result object containing information about 958 * the referral to follow. 959 * @param connection The connection on which the referral was received. 960 * @param depth The number of referrals followed in the course of 961 * processing this request. 962 * 963 * @return The result of attempting to process the compare operation by 964 * following the referral. 965 * 966 * @throws LDAPException If a problem occurs while attempting to establish 967 * the referral connection, sending the request, or 968 * reading the result. 969 */ 970 private CompareResult followReferral(final CompareResult referralResult, 971 final LDAPConnection connection, 972 final int depth) 973 throws LDAPException 974 { 975 for (final String urlString : referralResult.getReferralURLs()) 976 { 977 try 978 { 979 final LDAPURL referralURL = new LDAPURL(urlString); 980 final String host = referralURL.getHost(); 981 982 if (host == null) 983 { 984 // We can't handle a referral in which there is no host. 985 continue; 986 } 987 988 final CompareRequest compareRequest; 989 if (referralURL.baseDNProvided()) 990 { 991 compareRequest = new CompareRequest(referralURL.getBaseDN(), 992 attributeName, assertionValue, 993 getControls()); 994 } 995 else 996 { 997 compareRequest = this; 998 } 999 1000 final LDAPConnection referralConn = connection.getReferralConnector(). 1001 getReferralConnection(referralURL, connection); 1002 try 1003 { 1004 return compareRequest.process(referralConn, depth+1); 1005 } 1006 finally 1007 { 1008 referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null); 1009 referralConn.close(); 1010 } 1011 } 1012 catch (LDAPException le) 1013 { 1014 debugException(le); 1015 } 1016 } 1017 1018 // If we've gotten here, then we could not follow any of the referral URLs, 1019 // so we'll just return the original referral result. 1020 return referralResult; 1021 } 1022 1023 1024 1025 /** 1026 * {@inheritDoc} 1027 */ 1028 @InternalUseOnly() 1029 public void responseReceived(final LDAPResponse response) 1030 throws LDAPException 1031 { 1032 try 1033 { 1034 responseQueue.put(response); 1035 } 1036 catch (Exception e) 1037 { 1038 debugException(e); 1039 throw new LDAPException(ResultCode.LOCAL_ERROR, 1040 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e); 1041 } 1042 } 1043 1044 1045 1046 /** 1047 * {@inheritDoc} 1048 */ 1049 @Override() 1050 public int getLastMessageID() 1051 { 1052 return messageID; 1053 } 1054 1055 1056 1057 /** 1058 * {@inheritDoc} 1059 */ 1060 @Override() 1061 public OperationType getOperationType() 1062 { 1063 return OperationType.COMPARE; 1064 } 1065 1066 1067 1068 /** 1069 * {@inheritDoc} 1070 */ 1071 public CompareRequest duplicate() 1072 { 1073 return duplicate(getControls()); 1074 } 1075 1076 1077 1078 /** 1079 * {@inheritDoc} 1080 */ 1081 public CompareRequest duplicate(final Control[] controls) 1082 { 1083 final CompareRequest r = new CompareRequest(dn, attributeName, 1084 assertionValue.getValue(), controls); 1085 1086 if (followReferralsInternal() != null) 1087 { 1088 r.setFollowReferrals(followReferralsInternal()); 1089 } 1090 1091 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1092 1093 return r; 1094 } 1095 1096 1097 1098 /** 1099 * {@inheritDoc} 1100 */ 1101 @Override() 1102 public void toString(final StringBuilder buffer) 1103 { 1104 buffer.append("CompareRequest(dn='"); 1105 buffer.append(dn); 1106 buffer.append("', attr='"); 1107 buffer.append(attributeName); 1108 buffer.append("', value='"); 1109 buffer.append(assertionValue.stringValue()); 1110 buffer.append('\''); 1111 1112 final Control[] controls = getControls(); 1113 if (controls.length > 0) 1114 { 1115 buffer.append(", controls={"); 1116 for (int i=0; i < controls.length; i++) 1117 { 1118 if (i > 0) 1119 { 1120 buffer.append(", "); 1121 } 1122 1123 buffer.append(controls[i]); 1124 } 1125 buffer.append('}'); 1126 } 1127 1128 buffer.append(')'); 1129 } 1130 }