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