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