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<>(StaticUtils.computeMapCapacity( 374 responseControls.length)); 375 for (final Control c : responseControls) 376 { 377 if (! c.getOID().equals(UNIQUENESS_RESPONSE_OID)) 378 { 379 continue; 380 } 381 382 final UniquenessResponseControl urc; 383 if (c instanceof UniquenessResponseControl) 384 { 385 urc = (UniquenessResponseControl) c; 386 } 387 else 388 { 389 urc = new UniquenessResponseControl().decodeControl(c.getOID(), 390 c.isCritical(), c.getValue()); 391 } 392 393 final String uniquenessID = urc.getUniquenessID(); 394 if (controlMap.containsKey(uniquenessID)) 395 { 396 throw new LDAPException(ResultCode.DECODING_ERROR, 397 ERR_UNIQUENESS_RES_GET_ID_CONFLICT.get(uniquenessID)); 398 } 399 else 400 { 401 controlMap.put(uniquenessID, urc); 402 } 403 } 404 405 return Collections.unmodifiableMap(controlMap); 406 } 407 408 409 410 /** 411 * Retrieves the identifier that may be used to correlate this uniqueness 412 * response control with the corresponding request control. This is primarily 413 * useful for requests that contain multiple uniqueness controls, as there may 414 * be a separate response control for each. 415 * 416 * @return The identifier that may be used to correlate this uniqueness 417 * response control with the corresponding request control. 418 */ 419 public String getUniquenessID() 420 { 421 return uniquenessID; 422 } 423 424 425 426 /** 427 * Retrieves a value that indicates whether pre-commit validation was 428 * attempted, and whether that validation passed. 429 * 430 * @return {@code Boolean.TRUE} if pre-commit validation was attempted and 431 * passed, {@code Boolean.FALSE} if pre-commit validation was 432 * attempted and did not pass, or {@code null} if pre-commit 433 * validation was not attempted. 434 */ 435 public Boolean getPreCommitValidationPassed() 436 { 437 return preCommitValidationPassed; 438 } 439 440 441 442 /** 443 * Retrieves a value that indicates whether post-commit validation was 444 * attempted, and whether that validation passed. 445 * 446 * @return {@code Boolean.TRUE} if post-commit validation was attempted and 447 * passed, {@code Boolean.FALSE} if post-commit validation was 448 * attempted and did not pass, or {@code null} if post-commit 449 * validation was not attempted. 450 */ 451 public Boolean getPostCommitValidationPassed() 452 { 453 return postCommitValidationPassed; 454 } 455 456 457 458 /** 459 * Retrieves a message with additional information about the validation 460 * processing that was performed. 461 * 462 * @return A message with additional information about the validation 463 * processing that was performed, or {@code null} if no validation 464 * message is available. 465 */ 466 public String getValidationMessage() 467 { 468 return validationMessage; 469 } 470 471 472 473 /** 474 * {@inheritDoc} 475 */ 476 @Override() 477 public String getControlName() 478 { 479 return INFO_UNIQUENESS_RES_CONTROL_NAME.get(); 480 } 481 482 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override() 488 public void toString(final StringBuilder buffer) 489 { 490 buffer.append("UniquenessResponseControl(uniquenessID='"); 491 buffer.append(uniquenessID); 492 buffer.append('\''); 493 494 if (preCommitValidationPassed == null) 495 { 496 buffer.append(", preCommitValidationAttempted=false"); 497 } 498 else 499 { 500 buffer.append(", preCommitValidationPassed="); 501 buffer.append(preCommitValidationPassed); 502 } 503 504 if (postCommitValidationPassed == null) 505 { 506 buffer.append(", postCommitValidationAttempted=false"); 507 } 508 else 509 { 510 buffer.append(", postCommitValidationPassed="); 511 buffer.append(postCommitValidationPassed); 512 } 513 514 if (validationMessage != null) 515 { 516 buffer.append(", validationMessage='"); 517 buffer.append(validationMessage); 518 buffer.append('\''); 519 } 520 buffer.append(')'); 521 } 522}