001/* 002 * Copyright 2013-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2013-2024 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) 2013-2024 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.unboundidds.controls; 037 038 039 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043import java.util.Iterator; 044import java.util.LinkedHashMap; 045import java.util.List; 046import java.util.Map; 047 048import com.unboundid.asn1.ASN1Boolean; 049import com.unboundid.asn1.ASN1Element; 050import com.unboundid.asn1.ASN1Enumerated; 051import com.unboundid.asn1.ASN1OctetString; 052import com.unboundid.asn1.ASN1Sequence; 053import com.unboundid.ldap.sdk.Control; 054import com.unboundid.ldap.sdk.DecodeableControl; 055import com.unboundid.ldap.sdk.JSONControlDecodeHelper; 056import com.unboundid.ldap.sdk.LDAPException; 057import com.unboundid.ldap.sdk.LDAPResult; 058import com.unboundid.ldap.sdk.ResultCode; 059import com.unboundid.util.Debug; 060import com.unboundid.util.NotMutable; 061import com.unboundid.util.NotNull; 062import com.unboundid.util.Nullable; 063import com.unboundid.util.StaticUtils; 064import com.unboundid.util.ThreadSafety; 065import com.unboundid.util.ThreadSafetyLevel; 066import com.unboundid.util.json.JSONArray; 067import com.unboundid.util.json.JSONBoolean; 068import com.unboundid.util.json.JSONField; 069import com.unboundid.util.json.JSONNumber; 070import com.unboundid.util.json.JSONObject; 071import com.unboundid.util.json.JSONString; 072import com.unboundid.util.json.JSONValue; 073 074import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 075 076 077 078/** 079 * This class provides an implementation of an LDAP control that can be included 080 * in add, bind, modify, modify DN, and certain extended responses to provide 081 * information about the result of replication assurance processing for that 082 * operation. 083 * <BR> 084 * <BLOCKQUOTE> 085 * <B>NOTE:</B> This class, and other classes within the 086 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 087 * supported for use against Ping Identity, UnboundID, and 088 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 089 * for proprietary functionality or for external specifications that are not 090 * considered stable or mature enough to be guaranteed to work in an 091 * interoperable way with other types of LDAP servers. 092 * </BLOCKQUOTE> 093 * <BR> 094 * The OID for this control is 1.3.6.1.4.1.30221.2.5.29. It will have a 095 * criticality of FALSE, and will have a value with the following encoding: 096 * <PRE> 097 * AssuredReplicationResponse ::= SEQUENCE { 098 * localLevel [0] LocalLevel OPTIONAL, 099 * localAssuranceSatisfied [1] BOOLEAN, 100 * localAssuranceMessage [2] OCTET STRING OPTIONAL, 101 * remoteLevel [3] RemoteLevel OPTIONAL, 102 * remoteAssuranceSatisfied [4] BOOLEAN, 103 * remoteAssuranceMessage [5] OCTET STRING OPTIONAL, 104 * csn [6] OCTET STRING OPTIONAL, 105 * serverResults [7] SEQUENCE OF ServerResult OPTIONAL, 106 * ... } 107 * 108 * ServerResult ::= SEQUENCE { 109 * resultCode [0] ENUMERATED { 110 * complete (0), 111 * timeout (1), 112 * conflict (2), 113 * serverShutdown (3), 114 * unavailable (4), 115 * duplicate (5), 116 * ... }, 117 * replicationServerID [1] INTEGER OPTIONAL, 118 * replicaID [2] INTEGER OPTIONAL, 119 * ... } 120 * </PRE> 121 * 122 * @see AssuredReplicationRequestControl 123 */ 124@NotMutable() 125@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 126public final class AssuredReplicationResponseControl 127 extends Control 128 implements DecodeableControl 129{ 130 /** 131 * The OID (1.3.6.1.4.1.30221.2.5.29) for the assured replication response 132 * control. 133 */ 134 @NotNull public static final String ASSURED_REPLICATION_RESPONSE_OID = 135 "1.3.6.1.4.1.30221.2.5.29"; 136 137 138 /** 139 * The BER type for the local level element. 140 */ 141 private static final byte TYPE_LOCAL_LEVEL = (byte) 0x80; 142 143 144 /** 145 * The BER type for the local assurance satisfied element. 146 */ 147 private static final byte TYPE_LOCAL_SATISFIED = (byte) 0x81; 148 149 150 /** 151 * The BER type for the local message element. 152 */ 153 private static final byte TYPE_LOCAL_MESSAGE = (byte) 0x82; 154 155 156 /** 157 * The BER type for the remote level element. 158 */ 159 private static final byte TYPE_REMOTE_LEVEL = (byte) 0x83; 160 161 162 /** 163 * The BER type for the remote assurance satisfied element. 164 */ 165 private static final byte TYPE_REMOTE_SATISFIED = (byte) 0x84; 166 167 168 /** 169 * The BER type for the remote message element. 170 */ 171 private static final byte TYPE_REMOTE_MESSAGE = (byte) 0x85; 172 173 174 /** 175 * The BER type for the CSN element. 176 */ 177 private static final byte TYPE_CSN = (byte) 0x86; 178 179 180 /** 181 * The BER type for the server results element. 182 */ 183 private static final byte TYPE_SERVER_RESULTS = (byte) 0xA7; 184 185 186 187 /** 188 * The name of the field used to specify the replication CSN in the JSON 189 * representation of this control. 190 */ 191 @NotNull private static final String JSON_FIELD_CSN = "csn"; 192 193 194 195 /** 196 * The name of the field used to specify the local assurance level in the JSON 197 * representation of this control. 198 */ 199 @NotNull private static final String JSON_FIELD_LOCAL_LEVEL = "local-level"; 200 201 202 203 /** 204 * The name of the field used to indicate whether the local assurance level 205 * was satisfied in the JSON representation of this control. 206 */ 207 @NotNull private static final String JSON_FIELD_LOCAL_ASSURANCE_SATISFIED = 208 "local-assurance-satisfied"; 209 210 211 212 /** 213 * The name of the field used to provide an additional message about local 214 * assurance processing in the JSON representation of this control. 215 */ 216 @NotNull private static final String JSON_FIELD_LOCAL_ASSURANCE_MESSAGE = 217 "local-assurance-message"; 218 219 220 221 /** 222 * The name of the field used to specify the remote assurance level in the 223 * JSON representation of this control. 224 */ 225 @NotNull private static final String JSON_FIELD_REMOTE_LEVEL = "remote-level"; 226 227 228 229 /** 230 * The name of the field used to indicate whether the remote assurance level 231 * was satisfied in the JSON representation of this control. 232 */ 233 @NotNull private static final String JSON_FIELD_REMOTE_ASSURANCE_SATISFIED = 234 "remote-assurance-satisfied"; 235 236 237 238 /** 239 * The name of the field used to provide an additional message about remote 240 * assurance processing in the JSON representation of this control. 241 */ 242 @NotNull private static final String JSON_FIELD_REMOTE_ASSURANCE_MESSAGE = 243 "remote-assurance-message"; 244 245 246 247 /** 248 * The name of the field used to hold the server results in the JSON 249 * representation of this control. 250 */ 251 @NotNull private static final String JSON_FIELD_SERVER_RESULTS = 252 "server-results"; 253 254 255 256 /** 257 * The name of the field used to hold the result code value in the server 258 * results element in the JSON representation of this control. 259 */ 260 @NotNull private static final String 261 JSON_FIELD_SERVER_RESULTS_RESULT_CODE_VALUE = "result-code-value"; 262 263 264 265 /** 266 * The name of the field used to hold the result code name in the server 267 * results element in the JSON representation of this control. 268 */ 269 @NotNull private static final String 270 JSON_FIELD_SERVER_RESULTS_RESULT_CODE_NAME = "result-code-name"; 271 272 273 274 /** 275 * The name of the field used to hold the replica ID in the server results 276 * element in the JSON representation of this control. 277 */ 278 @NotNull private static final String JSON_FIELD_SERVER_RESULTS_REPLICA_ID = 279 "replica-id"; 280 281 282 283 /** 284 * The name of the field used to hold the replication server ID in the server 285 * results element in the JSON representation of this control. 286 */ 287 @NotNull private static final String 288 JSON_FIELD_SERVER_RESULTS_REPLICATION_SERVER_ID = 289 "replication-server-id"; 290 291 292 293 /** 294 * The serial version UID for this serializable class. 295 */ 296 private static final long serialVersionUID = -4521456074629871607L; 297 298 299 300 // The assurance level for local processing. 301 @Nullable private final AssuredReplicationLocalLevel localLevel; 302 303 // The assurance level for remote processing. 304 @Nullable private final AssuredReplicationRemoteLevel remoteLevel; 305 306 // Indicates whether the desired local assurance has been satisfied. 307 private final boolean localAssuranceSatisfied; 308 309 // Indicates whether the desired remote assurance has been satisfied. 310 private final boolean remoteAssuranceSatisfied; 311 312 // The results from individual replication and/or directory servers. 313 @NotNull private final List<AssuredReplicationServerResult> serverResults; 314 315 // The replication change sequence number for the associated operation. 316 @Nullable private final String csn; 317 318 // An optional message with additional information about local assurance 319 // processing. 320 @Nullable private final String localAssuranceMessage; 321 322 // An optional message with additional information about local assurance 323 // processing. 324 @Nullable private final String remoteAssuranceMessage; 325 326 327 328 /** 329 * Creates a new empty control instance that is intended to be used only for 330 * decoding controls via the {@code DecodeableControl} interface. 331 */ 332 AssuredReplicationResponseControl() 333 { 334 localLevel = null; 335 localAssuranceSatisfied = false; 336 localAssuranceMessage = null; 337 remoteLevel = null; 338 remoteAssuranceSatisfied = false; 339 remoteAssuranceMessage = null; 340 csn = null; 341 serverResults = null; 342 } 343 344 345 346 /** 347 * Creates a new assured replication response control with the provided 348 * information. 349 * 350 * @param localLevel The local assurance level selected by the 351 * server for the associated operation. It 352 * may be {@code null} if this is not 353 * available. 354 * @param localAssuranceSatisfied Indicates whether the desired local level 355 * of assurance is known to have been 356 * satisfied. 357 * @param localAssuranceMessage An optional message providing additional 358 * information about local assurance 359 * processing. This may be {@code null} if 360 * no additional message is needed. 361 * @param remoteLevel The remote assurance level selected by 362 * the server for the associated operation. 363 * It may be {@code null} if this is not 364 * available. 365 * @param remoteAssuranceSatisfied Indicates whether the desired remote 366 * level of assurance is known to have been 367 * satisfied. 368 * @param remoteAssuranceMessage An optional message providing additional 369 * information about remote assurance 370 * processing. This may be {@code null} if 371 * no additional message is needed. 372 * @param csn The change sequence number (CSN) that has 373 * been assigned to the associated 374 * operation. It may be {@code null} if no 375 * CSN is available. 376 * @param serverResults The set of individual results from the 377 * local and/or remote replication servers 378 * and/or directory servers used in 379 * assurance processing. This may be 380 * {@code null} or empty if no server 381 * results are available. 382 */ 383 public AssuredReplicationResponseControl( 384 @Nullable final AssuredReplicationLocalLevel localLevel, 385 final boolean localAssuranceSatisfied, 386 @Nullable final String localAssuranceMessage, 387 @Nullable final AssuredReplicationRemoteLevel remoteLevel, 388 final boolean remoteAssuranceSatisfied, 389 @Nullable final String remoteAssuranceMessage, 390 @Nullable final String csn, 391 @Nullable final Collection<AssuredReplicationServerResult> serverResults) 392 { 393 super(ASSURED_REPLICATION_RESPONSE_OID, false, 394 encodeValue(localLevel, localAssuranceSatisfied, 395 localAssuranceMessage, remoteLevel, remoteAssuranceSatisfied, 396 remoteAssuranceMessage, csn, serverResults)); 397 398 this.localLevel = localLevel; 399 this.localAssuranceSatisfied = localAssuranceSatisfied; 400 this.localAssuranceMessage = localAssuranceMessage; 401 this.remoteLevel = remoteLevel; 402 this.remoteAssuranceSatisfied = remoteAssuranceSatisfied; 403 this.remoteAssuranceMessage = remoteAssuranceMessage; 404 this.csn = csn; 405 406 if (serverResults == null) 407 { 408 this.serverResults = Collections.emptyList(); 409 } 410 else 411 { 412 this.serverResults = Collections.unmodifiableList( 413 new ArrayList<>(serverResults)); 414 } 415 } 416 417 418 419 /** 420 * Creates a new assured replication response control with the provided 421 * information. 422 * 423 * @param oid The OID for the control. 424 * @param isCritical Indicates whether the control should be marked 425 * critical. 426 * @param value The encoded value for the control. This may be 427 * {@code null} if no value was provided. 428 * 429 * @throws LDAPException If the provided control cannot be decoded as an 430 * assured replication response control. 431 */ 432 public AssuredReplicationResponseControl(@NotNull final String oid, 433 final boolean isCritical, 434 @Nullable final ASN1OctetString value) 435 throws LDAPException 436 { 437 super(oid, isCritical, value); 438 439 if (value == null) 440 { 441 throw new LDAPException(ResultCode.DECODING_ERROR, 442 ERR_ASSURED_REPLICATION_RESPONSE_NO_VALUE.get()); 443 } 444 445 AssuredReplicationLocalLevel lLevel = null; 446 Boolean lSatisfied = null; 447 String lMessage = null; 448 AssuredReplicationRemoteLevel rLevel = null; 449 Boolean rSatisfied = null; 450 String rMessage = null; 451 String seqNum = null; 452 List<AssuredReplicationServerResult> sResults = Collections.emptyList(); 453 454 try 455 { 456 for (final ASN1Element e : 457 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 458 { 459 switch (e.getType()) 460 { 461 case TYPE_LOCAL_LEVEL: 462 int intValue = ASN1Enumerated.decodeAsEnumerated(e).intValue(); 463 lLevel = AssuredReplicationLocalLevel.valueOf(intValue); 464 if (lLevel == null) 465 { 466 throw new LDAPException(ResultCode.DECODING_ERROR, 467 ERR_ASSURED_REPLICATION_RESPONSE_INVALID_LOCAL_LEVEL.get( 468 intValue)); 469 } 470 break; 471 472 case TYPE_LOCAL_SATISFIED: 473 lSatisfied = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 474 break; 475 476 case TYPE_LOCAL_MESSAGE: 477 lMessage = ASN1OctetString.decodeAsOctetString(e).stringValue(); 478 break; 479 480 case TYPE_REMOTE_LEVEL: 481 intValue = ASN1Enumerated.decodeAsEnumerated(e).intValue(); 482 rLevel = AssuredReplicationRemoteLevel.valueOf(intValue); 483 if (lLevel == null) 484 { 485 throw new LDAPException(ResultCode.DECODING_ERROR, 486 ERR_ASSURED_REPLICATION_RESPONSE_INVALID_REMOTE_LEVEL.get( 487 intValue)); 488 } 489 break; 490 491 case TYPE_REMOTE_SATISFIED: 492 rSatisfied = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 493 break; 494 495 case TYPE_REMOTE_MESSAGE: 496 rMessage = ASN1OctetString.decodeAsOctetString(e).stringValue(); 497 break; 498 499 case TYPE_CSN: 500 seqNum = ASN1OctetString.decodeAsOctetString(e).stringValue(); 501 break; 502 503 case TYPE_SERVER_RESULTS: 504 final ASN1Element[] srElements = 505 ASN1Sequence.decodeAsSequence(e).elements(); 506 final ArrayList<AssuredReplicationServerResult> srList = 507 new ArrayList<>(srElements.length); 508 for (final ASN1Element srElement : srElements) 509 { 510 try 511 { 512 srList.add(AssuredReplicationServerResult.decode(srElement)); 513 } 514 catch (final Exception ex) 515 { 516 Debug.debugException(ex); 517 throw new LDAPException(ResultCode.DECODING_ERROR, 518 ERR_ASSURED_REPLICATION_RESPONSE_ERROR_DECODING_SR.get( 519 StaticUtils.getExceptionMessage(ex)), 520 ex); 521 } 522 } 523 sResults = Collections.unmodifiableList(srList); 524 break; 525 526 default: 527 throw new LDAPException(ResultCode.DECODING_ERROR, 528 ERR_ASSURED_REPLICATION_RESPONSE_UNEXPECTED_ELEMENT_TYPE.get( 529 StaticUtils.toHex(e.getType()))); 530 } 531 } 532 } 533 catch (final LDAPException le) 534 { 535 Debug.debugException(le); 536 throw le; 537 } 538 catch (final Exception e) 539 { 540 Debug.debugException(e); 541 throw new LDAPException(ResultCode.DECODING_ERROR, 542 ERR_ASSURED_REPLICATION_RESPONSE_ERROR_DECODING_VALUE.get( 543 StaticUtils.getExceptionMessage(e)), 544 e); 545 } 546 547 if (lSatisfied == null) 548 { 549 throw new LDAPException(ResultCode.DECODING_ERROR, 550 ERR_ASSURED_REPLICATION_RESPONSE_NO_LOCAL_SATISFIED.get()); 551 } 552 553 if (rSatisfied == null) 554 { 555 throw new LDAPException(ResultCode.DECODING_ERROR, 556 ERR_ASSURED_REPLICATION_RESPONSE_NO_REMOTE_SATISFIED.get()); 557 } 558 559 localLevel = lLevel; 560 localAssuranceSatisfied = lSatisfied; 561 localAssuranceMessage = lMessage; 562 remoteLevel = rLevel; 563 remoteAssuranceSatisfied = rSatisfied; 564 remoteAssuranceMessage = rMessage; 565 csn = seqNum; 566 serverResults = sResults; 567 } 568 569 570 571 /** 572 * Encodes the provided information to an ASN.1 octet string suitable for 573 * use as an assured replication response control value. 574 * 575 * @param localLevel The local assurance level selected by the 576 * server for the associated operation. It 577 * may be {@code null} if this is not 578 * available. 579 * @param localAssuranceSatisfied Indicates whether the desired local level 580 * of assurance is known to have been 581 * satisfied. 582 * @param localAssuranceMessage An optional message providing additional 583 * information about local assurance 584 * processing. This may be {@code null} if 585 * no additional message is needed. 586 * @param remoteLevel The remote assurance level selected by 587 * the server for the associated operation. 588 * It may be {@code null} if this is not 589 * available. 590 * @param remoteAssuranceSatisfied Indicates whether the desired remote 591 * level of assurance is known to have been 592 * satisfied. 593 * @param remoteAssuranceMessage An optional message providing additional 594 * information about remote assurance 595 * processing. This may be {@code null} if 596 * no additional message is needed. 597 * @param csn The change sequence number (CSN) that has 598 * been assigned to the associated 599 * operation. It may be {@code null} if no 600 * CSN is available. 601 * @param serverResults The set of individual results from the 602 * local and/or remote replication servers 603 * and/or directory servers used in 604 * assurance processing. This may be 605 * {@code null} or empty if no server 606 * results are available. 607 * 608 * @return The ASN.1 octet string containing the encoded value. 609 */ 610 @NotNull() 611 private static ASN1OctetString encodeValue( 612 @Nullable final AssuredReplicationLocalLevel localLevel, 613 final boolean localAssuranceSatisfied, 614 @Nullable final String localAssuranceMessage, 615 @Nullable final AssuredReplicationRemoteLevel remoteLevel, 616 final boolean remoteAssuranceSatisfied, 617 @Nullable final String remoteAssuranceMessage, 618 @Nullable final String csn, 619 @Nullable final Collection<AssuredReplicationServerResult> serverResults) 620 { 621 final ArrayList<ASN1Element> elements = new ArrayList<>(8); 622 623 if (localLevel != null) 624 { 625 elements.add(new ASN1Enumerated(TYPE_LOCAL_LEVEL, localLevel.intValue())); 626 } 627 628 elements.add(new ASN1Boolean(TYPE_LOCAL_SATISFIED, 629 localAssuranceSatisfied)); 630 631 if (localAssuranceMessage != null) 632 { 633 elements.add(new ASN1OctetString(TYPE_LOCAL_MESSAGE, 634 localAssuranceMessage)); 635 } 636 637 if (remoteLevel != null) 638 { 639 elements.add(new ASN1Enumerated(TYPE_REMOTE_LEVEL, 640 remoteLevel.intValue())); 641 } 642 643 elements.add(new ASN1Boolean(TYPE_REMOTE_SATISFIED, 644 remoteAssuranceSatisfied)); 645 646 if (remoteAssuranceMessage != null) 647 { 648 elements.add(new ASN1OctetString(TYPE_REMOTE_MESSAGE, 649 remoteAssuranceMessage)); 650 } 651 652 if (csn != null) 653 { 654 elements.add(new ASN1OctetString(TYPE_CSN, csn)); 655 } 656 657 if ((serverResults != null) && (! serverResults.isEmpty())) 658 { 659 final ArrayList<ASN1Element> srElements = 660 new ArrayList<>(serverResults.size()); 661 for (final AssuredReplicationServerResult r : serverResults) 662 { 663 srElements.add(r.encode()); 664 } 665 elements.add(new ASN1Sequence(TYPE_SERVER_RESULTS, srElements)); 666 } 667 668 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 669 } 670 671 672 673 /** 674 * {@inheritDoc} 675 */ 676 @Override() 677 @NotNull() 678 public AssuredReplicationResponseControl decodeControl( 679 @NotNull final String oid, final boolean isCritical, 680 @Nullable final ASN1OctetString value) 681 throws LDAPException 682 { 683 return new AssuredReplicationResponseControl(oid, isCritical, value); 684 } 685 686 687 688 /** 689 * Extracts an assured replication response control from the provided LDAP 690 * result. If there are multiple assured replication response controls 691 * included in the result, then only the first will be returned. 692 * 693 * @param result The LDAP result from which to retrieve the assured 694 * replication response control. 695 * 696 * @return The assured replication response control contained in the provided 697 * LDAP result, or {@code null} if the result did not contain an 698 * assured replication response control. 699 * 700 * @throws LDAPException If a problem is encountered while attempting to 701 * decode the assured replication response control 702 * contained in the provided result. 703 */ 704 @Nullable() 705 public static AssuredReplicationResponseControl get( 706 @NotNull final LDAPResult result) 707 throws LDAPException 708 { 709 final Control c = 710 result.getResponseControl(ASSURED_REPLICATION_RESPONSE_OID); 711 if (c == null) 712 { 713 return null; 714 } 715 716 if (c instanceof AssuredReplicationResponseControl) 717 { 718 return (AssuredReplicationResponseControl) c; 719 } 720 else 721 { 722 return new AssuredReplicationResponseControl(c.getOID(), c.isCritical(), 723 c.getValue()); 724 } 725 } 726 727 728 729 /** 730 * Extracts all assured replication response controls from the provided LDAP 731 * result. 732 * 733 * @param result The LDAP result from which to retrieve the assured 734 * replication response controls. 735 * 736 * @return A list containing the assured replication response controls 737 * contained in the provided LDAP result, or an empty list if the 738 * result did not contain any assured replication response control. 739 * 740 * @throws LDAPException If a problem is encountered while attempting to 741 * decode any assured replication response control 742 * contained in the provided result. 743 */ 744 @NotNull() 745 public static List<AssuredReplicationResponseControl> getAll( 746 @NotNull final LDAPResult result) 747 throws LDAPException 748 { 749 final Control[] controls = result.getResponseControls(); 750 final ArrayList<AssuredReplicationResponseControl> decodedControls = 751 new ArrayList<>(controls.length); 752 for (final Control c : controls) 753 { 754 if (c.getOID().equals(ASSURED_REPLICATION_RESPONSE_OID)) 755 { 756 if (c instanceof AssuredReplicationResponseControl) 757 { 758 decodedControls.add((AssuredReplicationResponseControl) c); 759 } 760 else 761 { 762 decodedControls.add(new AssuredReplicationResponseControl(c.getOID(), 763 c.isCritical(), c.getValue())); 764 } 765 } 766 } 767 768 return Collections.unmodifiableList(decodedControls); 769 } 770 771 772 773 /** 774 * Retrieves the local assurance level selected by the server for the 775 * associated operation, if available. 776 * 777 * @return The local assurance level selected by the server for the 778 * associated operation, or {@code null} if this is not available. 779 */ 780 @Nullable() 781 public AssuredReplicationLocalLevel getLocalLevel() 782 { 783 return localLevel; 784 } 785 786 787 788 /** 789 * Indicates whether the desired local level of assurance is known to have 790 * been satisfied. 791 * 792 * @return {@code true} if the desired local level of assurance is known to 793 * have been satisfied, or {@code false} if not. 794 */ 795 public boolean localAssuranceSatisfied() 796 { 797 return localAssuranceSatisfied; 798 } 799 800 801 802 /** 803 * Retrieves a message with additional information about local assurance 804 * processing, if available. 805 * 806 * @return A message with additional information about local assurance 807 * processing, or {@code null} if none is available. 808 */ 809 @Nullable() 810 public String getLocalAssuranceMessage() 811 { 812 return localAssuranceMessage; 813 } 814 815 816 817 /** 818 * Retrieves the remote assurance level selected by the server for the 819 * associated operation, if available. 820 * 821 * @return The remote assurance level selected by the server for the 822 * associated operation, or {@code null} if the remote assurance 823 * level is not available. 824 */ 825 @Nullable() 826 public AssuredReplicationRemoteLevel getRemoteLevel() 827 { 828 return remoteLevel; 829 } 830 831 832 833 /** 834 * Indicates whether the desired remote level of assurance is known to have 835 * been satisfied. 836 * 837 * @return {@code true} if the desired remote level of assurance is known to 838 * have been satisfied, or {@code false} if not. 839 */ 840 public boolean remoteAssuranceSatisfied() 841 { 842 return remoteAssuranceSatisfied; 843 } 844 845 846 847 /** 848 * Retrieves a message with additional information about remote assurance 849 * processing, if available. 850 * 851 * @return A message with additional information about remote assurance 852 * processing, or {@code null} if none is available. 853 */ 854 @Nullable() 855 public String getRemoteAssuranceMessage() 856 { 857 return remoteAssuranceMessage; 858 } 859 860 861 862 /** 863 * Retrieves the replication change sequence number (CSN) assigned to the 864 * associated operation, if available. 865 * 866 * @return The replication CSN assigned to the associated operation, or 867 * {@code null} if the CSN is not available. 868 */ 869 @Nullable() 870 public String getCSN() 871 { 872 return csn; 873 } 874 875 876 877 /** 878 * Retrieves a list of the results from individual replication servers and/or 879 * directory servers used in assurance processing. It may be empty if no 880 * server results are available. 881 * 882 * @return A list of the results from individual replication servers and/or 883 * directory servers used in assurance processing. 884 */ 885 @NotNull() 886 public List<AssuredReplicationServerResult> getServerResults() 887 { 888 return serverResults; 889 } 890 891 892 893 /** 894 * {@inheritDoc} 895 */ 896 @Override() 897 @NotNull() 898 public String getControlName() 899 { 900 return INFO_CONTROL_NAME_ASSURED_REPLICATION_RESPONSE.get(); 901 } 902 903 904 905 /** 906 * Retrieves a representation of this assured replication response control as 907 * a JSON object. The JSON object uses the following fields: 908 * <UL> 909 * <LI> 910 * {@code oid} -- A mandatory string field whose value is the object 911 * identifier for this control. For the assured replication response 912 * control, the OID is "1.3.6.1.4.1.30221.2.5.29". 913 * </LI> 914 * <LI> 915 * {@code control-name} -- An optional string field whose value is a 916 * human-readable name for this control. This field is only intended for 917 * descriptive purposes, and when decoding a control, the {@code oid} 918 * field should be used to identify the type of control. 919 * </LI> 920 * <LI> 921 * {@code criticality} -- A mandatory Boolean field used to indicate 922 * whether this control is considered critical. 923 * </LI> 924 * <LI> 925 * {@code value-base64} -- An optional string field whose value is a 926 * base64-encoded representation of the raw value for this assured 927 * replication response control. Exactly one of the {@code value-base64} 928 * and {@code value-json} fields must be present. 929 * </LI> 930 * <LI> 931 * {@code value-json} -- An optional JSON object field whose value is a 932 * user-friendly representation of the value for this assured replication 933 * response control. Exactly one of the {@code value-base64} and 934 * {@code value-json} fields must be present, and if the 935 * {@code value-json} field is used, then it will use the following 936 * fields: 937 * <UL> 938 * <LI> 939 * {@code local-level} -- An optional string field whose value is the 940 * local assurance level used for the operation. If present, its 941 * value will be one of "{@code none}", "{@code received-any-server}", 942 * or "{@code processed-all-servers}". 943 * </LI> 944 * <LI> 945 * {@code local-assurance-satisfied} -- A Boolean field that indicates 946 * whether local assurance was satisfied for the operation. 947 * </LI> 948 * <LI> 949 * {@code local-assurance-message} -- An optional string field whose 950 * value is a message that provides additional information about the 951 * local assurance processing. 952 * </LI> 953 * <LI> 954 * {@code remote-level} -- An optional string field whose value is the 955 * remote assurance level used for the operation. If present, its 956 * value will be one of "{@code none}", 957 * "{@code received-any-remote-location}", 958 * "{@code received-all-remote-locations}", or 959 * "{@code processed-all-remote-servers}". 960 * </LI> 961 * <LI> 962 * {@code remote-assurance-satisfied} -- A Boolean field that 963 * indicates whether remote assurance was satisfied for the operation. 964 * </LI> 965 * <LI> 966 * {@code remote-assurance-message} -- An optional string field whose 967 * value is a message that provides additional information about the 968 * remote assurance processing. 969 * </LI> 970 * <LI> 971 * {@code csn} -- An optional string field whose 972 * value is the change sequence number that the server assigned for 973 * the operation. 974 * </LI> 975 * <LI> 976 * {@code server-results} -- An optional array field whose values are 977 * JSON objects with information about the individual results from the 978 * local and/or remote servers used in replication assurance 979 * processing. These JSON objects will use the following fields: 980 * <UL> 981 * <LI> 982 * {@code result-code-value} -- An integer field whose value is 983 * the numeric value for the 984 * {@link AssuredReplicationServerResultCode} for the server 985 * result. 986 * </LI> 987 * <LI> 988 * {@code result-code-name} -- An optional string field whose 989 * value is the name of the result code for the server result. 990 * </LI> 991 * <LI> 992 * {@code replication-server-id} -- An optional integer field 993 * whose value is the server ID for the associated replication 994 * server. 995 * </LI> 996 * <LI> 997 * {@code replica-id} -- An optional integer field whose value is 998 * the replica ID for the associated replica. 999 * </LI> 1000 * </UL> 1001 * </LI> 1002 * </UL> 1003 * </LI> 1004 * </UL> 1005 * 1006 * @return A JSON object that contains a representation of this control. 1007 */ 1008 @Override() 1009 @NotNull() 1010 public JSONObject toJSONControl() 1011 { 1012 final Map<String,JSONValue> jsonValueFields = new LinkedHashMap<>(); 1013 1014 if (localLevel != null) 1015 { 1016 jsonValueFields.put(JSON_FIELD_LOCAL_LEVEL, 1017 new JSONString(localLevel.getName())); 1018 } 1019 1020 jsonValueFields.put(JSON_FIELD_LOCAL_ASSURANCE_SATISFIED, 1021 new JSONBoolean(localAssuranceSatisfied)); 1022 1023 if (localAssuranceMessage != null) 1024 { 1025 jsonValueFields.put(JSON_FIELD_LOCAL_ASSURANCE_MESSAGE, 1026 new JSONString(localAssuranceMessage)); 1027 } 1028 1029 if (remoteLevel != null) 1030 { 1031 jsonValueFields.put(JSON_FIELD_REMOTE_LEVEL, 1032 new JSONString(remoteLevel.getName())); 1033 } 1034 1035 jsonValueFields.put(JSON_FIELD_REMOTE_ASSURANCE_SATISFIED, 1036 new JSONBoolean(remoteAssuranceSatisfied)); 1037 1038 if (remoteAssuranceMessage != null) 1039 { 1040 jsonValueFields.put(JSON_FIELD_REMOTE_ASSURANCE_MESSAGE, 1041 new JSONString(remoteAssuranceMessage)); 1042 } 1043 1044 if (csn != null) 1045 { 1046 jsonValueFields.put(JSON_FIELD_CSN, new JSONString(csn)); 1047 } 1048 1049 if ((serverResults != null) && (! serverResults.isEmpty())) 1050 { 1051 final List<JSONValue> serverResultValues = 1052 new ArrayList<>(serverResults.size()); 1053 for (final AssuredReplicationServerResult serverResult : serverResults) 1054 { 1055 final Map<String,JSONValue> serverResultFields = new LinkedHashMap<>(); 1056 serverResultFields.put(JSON_FIELD_SERVER_RESULTS_RESULT_CODE_VALUE, 1057 new JSONNumber(serverResult.getResultCode().intValue())); 1058 serverResultFields.put(JSON_FIELD_SERVER_RESULTS_RESULT_CODE_NAME, 1059 new JSONString(serverResult.getResultCode().name())); 1060 1061 final Short replicationServerID = serverResult.getReplicationServerID(); 1062 if (replicationServerID != null) 1063 { 1064 serverResultFields.put( 1065 JSON_FIELD_SERVER_RESULTS_REPLICATION_SERVER_ID, 1066 new JSONNumber(replicationServerID.longValue())); 1067 } 1068 1069 final Short replicaID = serverResult.getReplicaID(); 1070 if (replicaID != null) 1071 { 1072 serverResultFields.put(JSON_FIELD_SERVER_RESULTS_REPLICA_ID, 1073 new JSONNumber(replicaID.longValue())); 1074 } 1075 1076 serverResultValues.add(new JSONObject(serverResultFields)); 1077 } 1078 1079 jsonValueFields.put(JSON_FIELD_SERVER_RESULTS, 1080 new JSONArray(serverResultValues)); 1081 } 1082 1083 1084 return new JSONObject( 1085 new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID, 1086 ASSURED_REPLICATION_RESPONSE_OID), 1087 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME, 1088 INFO_CONTROL_NAME_ASSURED_REPLICATION_RESPONSE.get()), 1089 new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY, 1090 isCritical()), 1091 new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON, 1092 new JSONObject(jsonValueFields))); 1093 } 1094 1095 1096 1097 /** 1098 * Attempts to decode the provided object as a JSON representation of an 1099 * assured replication response control. 1100 * 1101 * @param controlObject The JSON object to be decoded. It must not be 1102 * {@code null}. 1103 * @param strict Indicates whether to use strict mode when decoding 1104 * the provided JSON object. If this is {@code true}, 1105 * then this method will throw an exception if the 1106 * provided JSON object contains any unrecognized 1107 * fields. If this is {@code false}, then unrecognized 1108 * fields will be ignored. 1109 * 1110 * @return The assured replication control that was decoded from the provided 1111 * JSON object. 1112 * 1113 * @throws LDAPException If the provided JSON object cannot be parsed as a 1114 * valid assured replication response control. 1115 */ 1116 @NotNull() 1117 public static AssuredReplicationResponseControl decodeJSONControl( 1118 @NotNull final JSONObject controlObject, 1119 final boolean strict) 1120 throws LDAPException 1121 { 1122 final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper( 1123 controlObject, strict, true, true); 1124 1125 final ASN1OctetString rawValue = jsonControl.getRawValue(); 1126 if (rawValue != null) 1127 { 1128 return new AssuredReplicationResponseControl(jsonControl.getOID(), 1129 jsonControl.getCriticality(), rawValue); 1130 } 1131 1132 1133 AssuredReplicationLocalLevel localLevel = null; 1134 AssuredReplicationRemoteLevel remoteLevel = null; 1135 Boolean localAssuranceSatisfied = null; 1136 Boolean remoteAssuranceSatisfied = null; 1137 String csn = null; 1138 String localAssuranceMessage = null; 1139 String remoteAssuranceMessage = null; 1140 final List<AssuredReplicationServerResult> serverResults = 1141 new ArrayList<>(); 1142 final JSONObject valueObject = jsonControl.getValueObject(); 1143 1144 final String localLevelStr = 1145 valueObject.getFieldAsString(JSON_FIELD_LOCAL_LEVEL); 1146 if (localLevelStr != null) 1147 { 1148 localLevel = AssuredReplicationLocalLevel.forName(localLevelStr); 1149 if (localLevel == null) 1150 { 1151 throw new LDAPException(ResultCode.DECODING_ERROR, 1152 ERR_ASSURED_REPLICATION_RESPONSE_JSON_INVALID_LOCAL_LEVEL.get( 1153 controlObject.toSingleLineString(), localLevelStr)); 1154 } 1155 } 1156 1157 localAssuranceSatisfied = 1158 valueObject.getFieldAsBoolean(JSON_FIELD_LOCAL_ASSURANCE_SATISFIED); 1159 if (localAssuranceSatisfied == null) 1160 { 1161 throw new LDAPException(ResultCode.DECODING_ERROR, 1162 ERR_ASSURED_REPLICATION_RESPONSE_JSON_MISSING_VALUE_FIELD.get( 1163 controlObject.toSingleLineString(), 1164 JSON_FIELD_LOCAL_ASSURANCE_SATISFIED)); 1165 } 1166 1167 localAssuranceMessage = 1168 valueObject.getFieldAsString(JSON_FIELD_LOCAL_ASSURANCE_MESSAGE); 1169 1170 final String remoteLevelStr = 1171 valueObject.getFieldAsString(JSON_FIELD_REMOTE_LEVEL); 1172 if (remoteLevelStr != null) 1173 { 1174 remoteLevel = AssuredReplicationRemoteLevel.forName(remoteLevelStr); 1175 if (remoteLevel == null) 1176 { 1177 throw new LDAPException(ResultCode.DECODING_ERROR, 1178 ERR_ASSURED_REPLICATION_RESPONSE_JSON_INVALID_REMOTE_LEVEL.get( 1179 controlObject.toSingleLineString(), remoteLevelStr)); 1180 } 1181 } 1182 1183 remoteAssuranceSatisfied = 1184 valueObject.getFieldAsBoolean(JSON_FIELD_REMOTE_ASSURANCE_SATISFIED); 1185 if (remoteAssuranceSatisfied == null) 1186 { 1187 throw new LDAPException(ResultCode.DECODING_ERROR, 1188 ERR_ASSURED_REPLICATION_RESPONSE_JSON_MISSING_VALUE_FIELD.get( 1189 controlObject.toSingleLineString(), 1190 JSON_FIELD_REMOTE_ASSURANCE_SATISFIED)); 1191 } 1192 1193 remoteAssuranceMessage = 1194 valueObject.getFieldAsString(JSON_FIELD_REMOTE_ASSURANCE_MESSAGE); 1195 1196 csn = valueObject.getFieldAsString(JSON_FIELD_CSN); 1197 1198 final List<JSONValue> serverResultValues = 1199 valueObject.getFieldAsArray(JSON_FIELD_SERVER_RESULTS); 1200 if (serverResultValues != null) 1201 { 1202 for (final JSONValue serverResultValue : serverResultValues) 1203 { 1204 serverResults.add(decodeServerResult(controlObject, serverResultValue, 1205 strict)); 1206 } 1207 } 1208 1209 1210 if (strict) 1211 { 1212 final List<String> unrecognizedFields = 1213 JSONControlDecodeHelper.getControlObjectUnexpectedFields( 1214 valueObject, JSON_FIELD_LOCAL_LEVEL, 1215 JSON_FIELD_LOCAL_ASSURANCE_SATISFIED, 1216 JSON_FIELD_LOCAL_ASSURANCE_MESSAGE, JSON_FIELD_REMOTE_LEVEL, 1217 JSON_FIELD_REMOTE_ASSURANCE_SATISFIED, 1218 JSON_FIELD_REMOTE_ASSURANCE_MESSAGE, JSON_FIELD_CSN, 1219 JSON_FIELD_SERVER_RESULTS); 1220 if (! unrecognizedFields.isEmpty()) 1221 { 1222 throw new LDAPException(ResultCode.DECODING_ERROR, 1223 ERR_ASSURED_REPLICATION_RESPONSE_JSON_UNRECOGNIZED_VALUE_FIELD.get( 1224 controlObject.toSingleLineString(), 1225 unrecognizedFields.get(0))); 1226 } 1227 } 1228 1229 1230 return new AssuredReplicationResponseControl(localLevel, 1231 localAssuranceSatisfied, localAssuranceMessage, remoteLevel, 1232 remoteAssuranceSatisfied, remoteAssuranceMessage, csn, serverResults); 1233 } 1234 1235 1236 1237 /** 1238 * Decodes the provided JSON value as an assured replication server result 1239 * object. 1240 * 1241 * @param controlObject The JSON object that contains an encoded 1242 * representation of a control being decoded. It 1243 * must not be {@code null}. 1244 * @param serverResultValue The JSON value to be decoded. It must not be 1245 * {@code null}. 1246 * @param strict Indicates whether to use strict mode when 1247 * decoding the server result. 1248 * 1249 * @return The server result value that was decoded. 1250 * 1251 * @throws LDAPException If the provided value cannot be decoded as a server 1252 * result. 1253 */ 1254 @NotNull() 1255 private static AssuredReplicationServerResult decodeServerResult( 1256 @NotNull final JSONObject controlObject, 1257 @NotNull final JSONValue serverResultValue, 1258 final boolean strict) 1259 throws LDAPException 1260 { 1261 final JSONObject resultObject; 1262 if (serverResultValue instanceof JSONObject) 1263 { 1264 resultObject = (JSONObject) serverResultValue; 1265 } 1266 else 1267 { 1268 throw new LDAPException(ResultCode.DECODING_ERROR, 1269 ERR_ASSURED_REPLICATION_RESPONSE_JSON_SERVER_RESULT_NOT_OBJECT.get( 1270 controlObject.toSingleLineString(), 1271 JSON_FIELD_SERVER_RESULTS)); 1272 } 1273 1274 final Integer resultCodeValue = resultObject.getFieldAsInteger( 1275 JSON_FIELD_SERVER_RESULTS_RESULT_CODE_VALUE); 1276 if (resultCodeValue == null) 1277 { 1278 throw new LDAPException(ResultCode.DECODING_ERROR, 1279 ERR_ASSURED_REPLICATION_RESPONSE_JSON_SERVER_RESULT_NO_RC.get( 1280 controlObject.toSingleLineString(), 1281 JSON_FIELD_SERVER_RESULTS_RESULT_CODE_VALUE)); 1282 } 1283 1284 final AssuredReplicationServerResultCode resultCode = 1285 AssuredReplicationServerResultCode.valueOf(resultCodeValue); 1286 if (resultCode == null) 1287 { 1288 throw new LDAPException(ResultCode.DECODING_ERROR, 1289 ERR_ASSURED_REPLICATION_RESPONSE_JSON_SERVER_RESULT_UNKNOWN_RC.get( 1290 controlObject.toSingleLineString(), resultCodeValue)); 1291 } 1292 1293 final Integer replicationServerID = resultObject.getFieldAsInteger( 1294 JSON_FIELD_SERVER_RESULTS_REPLICATION_SERVER_ID); 1295 final Integer replicaID = resultObject.getFieldAsInteger( 1296 JSON_FIELD_SERVER_RESULTS_REPLICA_ID); 1297 1298 1299 if (strict) 1300 { 1301 final List<String> unrecognizedFields = 1302 JSONControlDecodeHelper.getControlObjectUnexpectedFields( 1303 resultObject, JSON_FIELD_SERVER_RESULTS_RESULT_CODE_VALUE, 1304 JSON_FIELD_SERVER_RESULTS_RESULT_CODE_NAME, 1305 JSON_FIELD_SERVER_RESULTS_REPLICATION_SERVER_ID, 1306 JSON_FIELD_SERVER_RESULTS_REPLICA_ID); 1307 if (! unrecognizedFields.isEmpty()) 1308 { 1309 throw new LDAPException(ResultCode.DECODING_ERROR, 1310 ERR_ASSURED_REPLICATION_RESPONSE_JSON_UNRECOGNIZED_SR_FIELD.get( 1311 controlObject.toSingleLineString(), 1312 unrecognizedFields.get(0))); 1313 } 1314 } 1315 1316 1317 return new AssuredReplicationServerResult(resultCode, 1318 (replicationServerID != null) 1319 ? replicationServerID.shortValue() 1320 : null, 1321 (replicaID != null) 1322 ? replicaID.shortValue() 1323 : null); 1324 } 1325 1326 1327 1328 /** 1329 * {@inheritDoc} 1330 */ 1331 @Override() 1332 public void toString(@NotNull final StringBuilder buffer) 1333 { 1334 buffer.append("AssuredReplicationResponseControl(isCritical="); 1335 buffer.append(isCritical()); 1336 1337 if (localLevel != null) 1338 { 1339 buffer.append(", localLevel="); 1340 buffer.append(localLevel.name()); 1341 } 1342 1343 buffer.append(", localAssuranceSatisfied="); 1344 buffer.append(localAssuranceSatisfied); 1345 1346 if (localAssuranceMessage != null) 1347 { 1348 buffer.append(", localMessage='"); 1349 buffer.append(localAssuranceMessage); 1350 buffer.append('\''); 1351 } 1352 1353 if (remoteLevel != null) 1354 { 1355 buffer.append(", remoteLevel="); 1356 buffer.append(remoteLevel.name()); 1357 } 1358 1359 buffer.append(", remoteAssuranceSatisfied="); 1360 buffer.append(remoteAssuranceSatisfied); 1361 1362 if (remoteAssuranceMessage != null) 1363 { 1364 buffer.append(", remoteMessage='"); 1365 buffer.append(remoteAssuranceMessage); 1366 buffer.append('\''); 1367 } 1368 1369 if (csn != null) 1370 { 1371 buffer.append(", csn='"); 1372 buffer.append(csn); 1373 buffer.append('\''); 1374 } 1375 1376 if ((serverResults != null) && (! serverResults.isEmpty())) 1377 { 1378 buffer.append(", serverResults={"); 1379 1380 final Iterator<AssuredReplicationServerResult> iterator = 1381 serverResults.iterator(); 1382 while (iterator.hasNext()) 1383 { 1384 iterator.next().toString(buffer); 1385 1386 if (iterator.hasNext()) 1387 { 1388 buffer.append(", "); 1389 } 1390 } 1391 1392 buffer.append('}'); 1393 } 1394 1395 buffer.append(')'); 1396 } 1397}