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