001/* 002 * Copyright 2017-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.unboundidds.controls; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.LinkedHashMap; 028import java.util.Map; 029 030import com.unboundid.asn1.ASN1Boolean; 031import com.unboundid.asn1.ASN1Element; 032import com.unboundid.asn1.ASN1OctetString; 033import com.unboundid.asn1.ASN1Sequence; 034import com.unboundid.ldap.sdk.Control; 035import com.unboundid.ldap.sdk.DecodeableControl; 036import com.unboundid.ldap.sdk.LDAPException; 037import com.unboundid.ldap.sdk.LDAPResult; 038import com.unboundid.ldap.sdk.ResultCode; 039import com.unboundid.util.Debug; 040import com.unboundid.util.NotMutable; 041import com.unboundid.util.StaticUtils; 042import com.unboundid.util.ThreadSafety; 043import com.unboundid.util.ThreadSafetyLevel; 044import com.unboundid.util.Validator; 045 046import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 047 048 049 050/** 051 * This class provides a response control that may be included in the response 052 * to add, modify, and modify DN requests that included the 053 * {@link UniquenessRequestControl}. It provides information about the 054 * uniqueness processing that was performed. 055 * <BR> 056 * <BLOCKQUOTE> 057 * <B>NOTE:</B> This class, and other classes within the 058 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 059 * supported for use against Ping Identity, UnboundID, and 060 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 061 * for proprietary functionality or for external specifications that are not 062 * considered stable or mature enough to be guaranteed to work in an 063 * interoperable way with other types of LDAP servers. 064 * </BLOCKQUOTE> 065 * <BR> 066 * The control has an OID of 1.3.6.1.4.1.30221.2.5.53 and a criticality of 067 * false. It must have a value with the following encoding: 068 * <PRE> 069 * UniquenessResponseValue ::= SEQUENCE { 070 * uniquenessID [0] OCTET STRING, 071 * preCommitValidationPassed [1] BOOLEAN OPTIONAL, 072 * postCommitValidationPassed [2] BOOLEAN OPTIONAL, 073 * validationMessage [3] OCTET STRING OPTIONAL, 074 * ... } 075 * </PRE> 076 */ 077@NotMutable() 078@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 079public final class UniquenessResponseControl 080 extends Control 081 implements DecodeableControl 082{ 083 /** 084 * The OID (1.3.6.1.4.1.30221.2.5.53) for the uniqueness response control. 085 */ 086 public static final String UNIQUENESS_RESPONSE_OID = 087 "1.3.6.1.4.1.30221.2.5.53"; 088 089 090 091 /** 092 * The BER type for the uniqueness ID element in the value sequence. 093 */ 094 private static final byte TYPE_UNIQUENESS_ID = (byte) 0x80; 095 096 097 098 /** 099 * The BER type for the pre-commit validation passed element in the value 100 * sequence. 101 */ 102 private static final byte TYPE_PRE_COMMIT_VALIDATION_PASSED = (byte) 0x81; 103 104 105 106 /** 107 * The BER type for the post-commit validation passed element in the value 108 * sequence. 109 */ 110 private static final byte TYPE_POST_COMMIT_VALIDATION_PASSED = (byte) 0x82; 111 112 113 114 /** 115 * The BER type for the validation message element in the value sequence. 116 */ 117 private static final byte TYPE_VALIDATION_MESSAGE = (byte) 0x83; 118 119 120 121 /** 122 * The serial version UID for this serializable class. 123 */ 124 private static final long serialVersionUID = 5090348902351420617L; 125 126 127 128 // Indicates whether post-commit validation passed. 129 private final Boolean postCommitValidationPassed; 130 131 // Indicates whether pre-commit validation passed. 132 private final Boolean preCommitValidationPassed; 133 134 // A value that will be used to correlate this response control with its 135 // corresponding request control. 136 private final String uniquenessID; 137 138 // The validation message, if any. 139 private final String validationMessage; 140 141 142 143 /** 144 * Creates a new empty control instance that is intended to be used only for 145 * decoding controls via the {@code DecodeableControl} interface. 146 */ 147 UniquenessResponseControl() 148 { 149 uniquenessID = null; 150 preCommitValidationPassed = null; 151 postCommitValidationPassed = null; 152 validationMessage = null; 153 } 154 155 156 157 /** 158 * Creates a new uniqueness response control with the provided information. 159 * 160 * @param uniquenessID The uniqueness ID that may be used to 161 * correlate this uniqueness response 162 * control with the corresponding request 163 * control. This must not be 164 * {@code null}. 165 * @param preCommitValidationPassed Indicates whether the pre-commit 166 * validation was successful. This may be 167 * {@code null} if no pre-commit 168 * validation was attempted. 169 * @param postCommitValidationPassed Indicates whether the post-commit 170 * validation was successful. This may be 171 * {@code null} if no post-commit 172 * validation was attempted. 173 * @param validationMessage A message with additional information 174 * about the validation processing. This 175 * may be {@code null} if no validation 176 * message is needed. 177 */ 178 public UniquenessResponseControl(final String uniquenessID, 179 final Boolean preCommitValidationPassed, 180 final Boolean postCommitValidationPassed, 181 final String validationMessage) 182 { 183 super(UNIQUENESS_RESPONSE_OID, false, 184 encodeValue(uniquenessID, preCommitValidationPassed, 185 postCommitValidationPassed, validationMessage)); 186 187 Validator.ensureNotNull(uniquenessID); 188 189 this.uniquenessID = uniquenessID; 190 this.preCommitValidationPassed = preCommitValidationPassed; 191 this.postCommitValidationPassed = postCommitValidationPassed; 192 this.validationMessage = validationMessage; 193 } 194 195 196 197 /** 198 * Encodes the provided information into an ASN.1 octet string suitable for 199 * use as the value of this control. 200 * 201 * @param uniquenessID The uniqueness ID that may be used to 202 * correlate this uniqueness response 203 * control with the corresponding request 204 * control. This must not be 205 * {@code null}. 206 * @param preCommitValidationPassed Indicates whether the pre-commit 207 * validation was successful. This may be 208 * {@code null} if no pre-commit 209 * validation was attempted. 210 * @param postCommitValidationPassed Indicates whether the post-commit 211 * validation was successful. This may be 212 * {@code null} if no post-commit 213 * validation was attempted. 214 * @param validationMessage A message with additional information 215 * about the validation processing. This 216 * may be {@code null} if no validation 217 * message is needed. 218 * 219 * @return The encoded control value. 220 */ 221 private static ASN1OctetString encodeValue(final String uniquenessID, 222 final Boolean preCommitValidationPassed, 223 final Boolean postCommitValidationPassed, 224 final String validationMessage) 225 { 226 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 227 elements.add(new ASN1OctetString(TYPE_UNIQUENESS_ID, uniquenessID)); 228 229 if (preCommitValidationPassed != null) 230 { 231 elements.add(new ASN1Boolean(TYPE_PRE_COMMIT_VALIDATION_PASSED, 232 preCommitValidationPassed)); 233 } 234 235 if (postCommitValidationPassed != null) 236 { 237 elements.add(new ASN1Boolean(TYPE_POST_COMMIT_VALIDATION_PASSED, 238 postCommitValidationPassed)); 239 } 240 241 if (validationMessage != null) 242 { 243 elements.add(new ASN1OctetString(TYPE_VALIDATION_MESSAGE, 244 validationMessage)); 245 } 246 247 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 248 } 249 250 251 252 /** 253 * Creates a new uniqueness response control with the provided information. 254 * 255 * @param oid The OID for the control. 256 * @param isCritical Indicates whether the control should be marked 257 * critical. 258 * @param value The encoded value for the control. This may be 259 * {@code null} if no value was provided. 260 * 261 * @throws LDAPException If the provided control cannot be decoded as a 262 * uniqueness response control. 263 */ 264 public UniquenessResponseControl(final String oid, final boolean isCritical, 265 final ASN1OctetString value) 266 throws LDAPException 267 { 268 super(oid, isCritical, value); 269 270 if (value == null) 271 { 272 throw new LDAPException(ResultCode.DECODING_ERROR, 273 ERR_UNIQUENESS_RES_DECODE_NO_VALUE.get()); 274 } 275 276 try 277 { 278 String id = null; 279 Boolean prePassed = null; 280 Boolean postPassed = null; 281 String message = null; 282 for (final ASN1Element e : 283 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 284 { 285 switch (e.getType()) 286 { 287 case TYPE_UNIQUENESS_ID: 288 id = ASN1OctetString.decodeAsOctetString(e).stringValue(); 289 break; 290 case TYPE_PRE_COMMIT_VALIDATION_PASSED: 291 prePassed = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 292 break; 293 case TYPE_POST_COMMIT_VALIDATION_PASSED: 294 postPassed = ASN1Boolean.decodeAsBoolean(e).booleanValue(); 295 break; 296 case TYPE_VALIDATION_MESSAGE: 297 message = ASN1OctetString.decodeAsOctetString(e).stringValue(); 298 break; 299 default: 300 throw new LDAPException(ResultCode.DECODING_ERROR, 301 ERR_UNIQUENESS_RES_DECODE_UNKNOWN_ELEMENT_TYPE.get( 302 StaticUtils.toHex(e.getType()))); 303 } 304 } 305 306 if (id == null) 307 { 308 throw new LDAPException(ResultCode.DECODING_ERROR, 309 ERR_UNIQUENESS_RES_DECODE_NO_UNIQUENESS_ID.get()); 310 } 311 312 uniquenessID = id; 313 preCommitValidationPassed = prePassed; 314 postCommitValidationPassed = postPassed; 315 validationMessage = message; 316 } 317 catch (final LDAPException le) 318 { 319 Debug.debugException(le); 320 throw le; 321 } 322 catch (final Exception e) 323 { 324 Debug.debugException(e); 325 throw new LDAPException(ResultCode.DECODING_ERROR, 326 ERR_UNIQUENESS_RES_DECODE_ERROR.get( 327 StaticUtils.getExceptionMessage(e)), 328 e); 329 } 330 } 331 332 333 334 /** 335 * {@inheritDoc} 336 */ 337 @Override() 338 public UniquenessResponseControl decodeControl(final String oid, 339 final boolean isCritical, 340 final ASN1OctetString value) 341 throws LDAPException 342 { 343 return new UniquenessResponseControl(oid, isCritical, value); 344 } 345 346 347 348 /** 349 * Retrieves the set of uniqueness response controls included in the provided 350 * result. 351 * 352 * @param result The result to process. 353 * 354 * @return The set of uniqueness response controls included in the provided 355 * result, indexed by uniqueness ID. It may be empty if the result 356 * does not include any uniqueness response controls. 357 * 358 * @throws LDAPException If a problem is encountered while getting the set 359 * of uniqueness response controls contained in the 360 * provided result. 361 */ 362 public static Map<String,UniquenessResponseControl> 363 get(final LDAPResult result) 364 throws LDAPException 365 { 366 final Control[] responseControls = result.getResponseControls(); 367 if (responseControls.length == 0) 368 { 369 return Collections.emptyMap(); 370 } 371 372 final LinkedHashMap<String,UniquenessResponseControl> controlMap = 373 new LinkedHashMap<>(responseControls.length); 374 for (final Control c : responseControls) 375 { 376 if (! c.getOID().equals(UNIQUENESS_RESPONSE_OID)) 377 { 378 continue; 379 } 380 381 final UniquenessResponseControl urc; 382 if (c instanceof UniquenessResponseControl) 383 { 384 urc = (UniquenessResponseControl) c; 385 } 386 else 387 { 388 urc = new UniquenessResponseControl().decodeControl(c.getOID(), 389 c.isCritical(), c.getValue()); 390 } 391 392 final String uniquenessID = urc.getUniquenessID(); 393 if (controlMap.containsKey(uniquenessID)) 394 { 395 throw new LDAPException(ResultCode.DECODING_ERROR, 396 ERR_UNIQUENESS_RES_GET_ID_CONFLICT.get(uniquenessID)); 397 } 398 else 399 { 400 controlMap.put(uniquenessID, urc); 401 } 402 } 403 404 return Collections.unmodifiableMap(controlMap); 405 } 406 407 408 409 /** 410 * Retrieves the identifier that may be used to correlate this uniqueness 411 * response control with the corresponding request control. This is primarily 412 * useful for requests that contain multiple uniqueness controls, as there may 413 * be a separate response control for each. 414 * 415 * @return The identifier that may be used to correlate this uniqueness 416 * response control with the corresponding request control. 417 */ 418 public String getUniquenessID() 419 { 420 return uniquenessID; 421 } 422 423 424 425 /** 426 * Retrieves a value that indicates whether pre-commit validation was 427 * attempted, and whether that validation passed. 428 * 429 * @return {@code Boolean.TRUE} if pre-commit validation was attempted and 430 * passed, {@code Boolean.FALSE} if pre-commit validation was 431 * attempted and did not pass, or {@code null} if pre-commit 432 * validation was not attempted. 433 */ 434 public Boolean getPreCommitValidationPassed() 435 { 436 return preCommitValidationPassed; 437 } 438 439 440 441 /** 442 * Retrieves a value that indicates whether post-commit validation was 443 * attempted, and whether that validation passed. 444 * 445 * @return {@code Boolean.TRUE} if post-commit validation was attempted and 446 * passed, {@code Boolean.FALSE} if post-commit validation was 447 * attempted and did not pass, or {@code null} if post-commit 448 * validation was not attempted. 449 */ 450 public Boolean getPostCommitValidationPassed() 451 { 452 return postCommitValidationPassed; 453 } 454 455 456 457 /** 458 * Retrieves a message with additional information about the validation 459 * processing that was performed. 460 * 461 * @return A message with additional information about the validation 462 * processing that was performed, or {@code null} if no validation 463 * message is available. 464 */ 465 public String getValidationMessage() 466 { 467 return validationMessage; 468 } 469 470 471 472 /** 473 * {@inheritDoc} 474 */ 475 @Override() 476 public String getControlName() 477 { 478 return INFO_UNIQUENESS_RES_CONTROL_NAME.get(); 479 } 480 481 482 483 /** 484 * {@inheritDoc} 485 */ 486 @Override() 487 public void toString(final StringBuilder buffer) 488 { 489 buffer.append("UniquenessResponseControl(uniquenessID='"); 490 buffer.append(uniquenessID); 491 buffer.append('\''); 492 493 if (preCommitValidationPassed == null) 494 { 495 buffer.append(", preCommitValidationAttempted=false"); 496 } 497 else 498 { 499 buffer.append(", preCommitValidationPassed="); 500 buffer.append(preCommitValidationPassed); 501 } 502 503 if (postCommitValidationPassed == null) 504 { 505 buffer.append(", postCommitValidationAttempted=false"); 506 } 507 else 508 { 509 buffer.append(", postCommitValidationPassed="); 510 buffer.append(postCommitValidationPassed); 511 } 512 513 if (validationMessage != null) 514 { 515 buffer.append(", validationMessage='"); 516 buffer.append(validationMessage); 517 buffer.append('\''); 518 } 519 buffer.append(')'); 520 } 521}