001/* 002 * Copyright 2017-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2019 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.Iterator; 028import java.util.LinkedHashSet; 029import java.util.Set; 030import java.util.UUID; 031 032import com.unboundid.asn1.ASN1Boolean; 033import com.unboundid.asn1.ASN1Element; 034import com.unboundid.asn1.ASN1Enumerated; 035import com.unboundid.asn1.ASN1OctetString; 036import com.unboundid.asn1.ASN1Sequence; 037import com.unboundid.asn1.ASN1Set; 038import com.unboundid.ldap.sdk.Control; 039import com.unboundid.ldap.sdk.Filter; 040import com.unboundid.ldap.sdk.LDAPException; 041import com.unboundid.ldap.sdk.ResultCode; 042import com.unboundid.util.Debug; 043import com.unboundid.util.NotMutable; 044import com.unboundid.util.StaticUtils; 045import com.unboundid.util.ThreadSafety; 046import com.unboundid.util.ThreadSafetyLevel; 047import com.unboundid.util.Validator; 048 049import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*; 050 051 052 053/** 054 * This class provides a request control that may be included in an add, modify, 055 * or modify DN request to ensure that the contents of that request will not 056 * result in a uniqueness conflict with any other entry in the server. Each 057 * instance of this control should define exactly one uniqueness constraint for 058 * the associated operation. Multiple instances of this control can be included 059 * in the same request to define multiple independent uniqueness constraints 060 * that must all be satisfied. If any of the uniqueness constraints is not 061 * satisfied, then the corresponding LDAP result should have a result code of 062 * {@link ResultCode#ASSERTION_FAILED} and a {@link UniquenessResponseControl} 063 * for each uniqueness constraint that was not satisfied. 064 * <BR> 065 * <BLOCKQUOTE> 066 * <B>NOTE:</B> This class, and other classes within the 067 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 068 * supported for use against Ping Identity, UnboundID, and 069 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 070 * for proprietary functionality or for external specifications that are not 071 * considered stable or mature enough to be guaranteed to work in an 072 * interoperable way with other types of LDAP servers. 073 * </BLOCKQUOTE> 074 * <BR> 075 * The request properties must contain either one or more attribute types, a 076 * filter, or both. If only a filter is specified, then the server will use 077 * that filter to identify conflicts (for an add request, any matches at all 078 * will be considered a conflict; for a modify or modify DN request, any matches 079 * with any entry other than the one being updated will be considered a 080 * conflict). If a single attribute type is specified with no filter, then any 081 * change that would result in multiple entries having the same value for that 082 * attribute will be considered a conflict. If multiple attribute types are 083 * specified, then the multiple attribute behavior will be used to determine how 084 * to identify conflicts, as documented in the 085 * {@link UniquenessMultipleAttributeBehavior} enum. If both a set of attribute 086 * types and a filter are provided, then only entries matching both sets of 087 * criteria will be considered a conflict. 088 * <BR><BR> 089 * The server can perform two different searches in an attempt to identify 090 * conflicts. In the pre-commit phase, it will attempt to identify any 091 * conflicts that already exist, and will reject the associated change if there 092 * are any. In the post-commit phase, it can see if there were any conflicts 093 * introduced by the change itself or by another change happening at the same 094 * time. If a conflict is detected in the post-commit phase, then the server 095 * won't have prevented it, but at least the control can be used to provide 096 * notification about it. 097 * <BR><BR> 098 * This request control may be sent either directly to a Directory Server 099 * instance, or it may be sent to a Directory Proxy Server with or without entry 100 * balancing. If the request is sent directly to a Directory Server, then only 101 * that one server will be checked for uniqueness conflicts, and it is possible 102 * that concurrent conflicts may be introduced on other servers that have not 103 * yet been replicated by the time control processing has completed. If the 104 * request is sent to a Directory Proxy Server instance, then search may be 105 * processed in one or more backend servers based on the pre-commit and 106 * post-commit validation levels, and at the most paranoid levels, it is highly 107 * unlikely that any conflicts will go unnoticed. 108 * <BR><BR> 109 * The request control has an OID of 1.3.6.1.4.1.30221.2.5.52, a criticality of 110 * either {@code true} or {@code false}, and a value with the following 111 * encoding: 112 * <PRE> 113 * UniquenessRequestValue ::= SEQUENCE { 114 * uniquenessID [0] OCTET STRING, 115 * attributeTypes [1] SET OF OCTET STRING OPTIONAL, 116 * multipleAttributeBehavior [2] ENUMERATED { 117 * uniqueWithinEachAttribute (0), 118 * uniqueAcrossAllAttributesIncludingInSameEntry (1), 119 * uniqueAcrossAllAttributesExceptInSameEntry (2), 120 * uniqueInCombination (3), 121 * ... } DEFAULT uniqueWithinEachAttribute, 122 * baseDN [3] LDAPDN OPTIONAL, 123 * filter [4] Filter OPTIONAL, 124 * preventConflictsWithSoftDeletedEntries [5] BOOLEAN DEFAULT FALSE, 125 * preCommitValidationLevel [6] ENUMERATED { 126 * none (0), 127 * allSubtreeViews (1), 128 * allBackendSets (2), 129 * allAvailableBackendServers (3), 130 * ... } DEFAULT allSubtreeViews, 131 * postCommitValidationLevel [7] ENUMERATED { 132 * none (0), 133 * allSubtreeViews (1), 134 * allBackendSets (2), 135 * allAvailableBackendServers (3), 136 * ... } DEFAULT allSubtreeViews, 137 * ... } 138 * </PRE> 139 * <BR><BR> 140 * <H2>Example</H2> 141 * The following example demonstrates how to use the uniqueness request control 142 * to only process an add operation if it does not result in multiple entries 143 * that have the same uid value: 144 * <BR><BR> 145 * <PRE> 146 * // Create the properties to build a uniqueness request control that 147 * // will try to prevent an add operation from creating a new entry 148 * // that has the same uid as an existing entry in the server. During 149 * // pre-commit processing (which happens before the server actually 150 * // processes the add), the server will check at least one server in 151 * // each entry-balancing backend set (or just one server in a 152 * // non-entry-balanced deployment). During post-commit processing 153 * // (which happens if the add succeeds), the server will double-check 154 * // that no conflicting entry was added on any available server in the 155 * // topology. Also ensure that the server will not allow conflicts 156 * // with soft-deleted entries. 157 * final UniquenessRequestControlProperties uniquenessProperties = 158 * new UniquenessRequestControlProperties("uid"); 159 * uniquenessProperties.setPreCommitValidationLevel( 160 * UniquenessValidationLevel.ALL_BACKEND_SETS); 161 * uniquenessProperties.setPostCommitValidationLevel( 162 * UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS); 163 * uniquenessProperties.setPreventConflictsWithSoftDeletedEntries(true); 164 * 165 * // Create the request control. It will be critical so that the 166 * // server will not attempt to process the add if it can't honor the 167 * // uniqueness request. 168 * final boolean isCritical = true; 169 * final String uniquenessID = "uid-uniqueness"; 170 * final UniquenessRequestControl uniquenessRequestControl = 171 * new UniquenessRequestControl(isCritical, uniquenessID, 172 * uniquenessProperties); 173 * 174 * // Attach the control to an add request. 175 * addRequest.addControl(uniquenessRequestControl); 176 * 177 * // Send the add request to the server and read the result. 178 * try 179 * { 180 * final LDAPResult addResult = connection.add(addRequest); 181 * 182 * // The add operation succeeded, so the entry should have been 183 * // created, but there is still the possibility that a post-commit 184 * // conflict was discovered, indicating that another request 185 * // processed at about the same time as our add introduced a 186 * // conflicting entry. 187 * final Map<String,UniquenessResponseControl> uniquenessResponses; 188 * try 189 * { 190 * uniquenessResponses = UniquenessResponseControl.get(addResult); 191 * } 192 * catch (final LDAPException e) 193 * { 194 * throw new RuntimeException( 195 * "The add succeeded, but an error occurred while trying " + 196 * "to decode a uniqueness response control in add " + 197 * "result " + addResult + ": " + 198 * StaticUtils.getExceptionMessage(e), 199 * e); 200 * } 201 * 202 * final UniquenessResponseControl uniquenessResponseControl = 203 * uniquenessResponses.get(uniquenessID); 204 * if ((uniquenessResponseControl != null) && 205 * uniquenessResponseControl.uniquenessConflictFound()) 206 * { 207 * throw new RuntimeException( 208 * "The add succeeded, but a uniqueness conflict was found " + 209 * "Uniqueness validation message: " + 210 * uniquenessResponseControl.getValidationMessage()); 211 * } 212 * } 213 * catch (final LDAPException e) 214 * { 215 * // The add attempt failed. It might have been because of a 216 * // uniqueness problem, or it could have been for some other reason. 217 * // To figure out which it was, look to see if there is an 218 * // appropriate uniqueness response control. 219 * final Map<String, UniquenessResponseControl> uniquenessResponses; 220 * try 221 * { 222 * uniquenessResponses = 223 * UniquenessResponseControl.get(e.toLDAPResult()); 224 * } 225 * catch (final LDAPException e2) 226 * { 227 * throw new LDAPException(e.getResultCode(), 228 * "The add attempt failed with result " + e.toLDAPResult() + 229 * ", and an error occurred while trying to decode a " + 230 * "uniqueness response control in the result: " + 231 * StaticUtils.getExceptionMessage(e2), 232 * e); 233 * } 234 * 235 * final UniquenessResponseControl uniquenessResponseControl = 236 * uniquenessResponses.get(uniquenessID); 237 * if (uniquenessResponseControl == null) 238 * { 239 * // The add result didn't include a uniqueness response control, 240 * // indicating that the failure was not because of a uniqueness 241 * // conflict. 242 * throw e; 243 * } 244 * 245 * if (uniquenessResponseControl.uniquenessConflictFound()) 246 * { 247 * // The add failed, and the uniqueness response control indicates 248 * // that the failure was because of a uniqueness conflict. 249 * 250 * final UniquenessValidationResult preCommitResult = 251 * uniquenessResponseControl.getPreCommitValidationResult(); 252 * final UniquenessValidationResult postCommitResult = 253 * uniquenessResponseControl.getPreCommitValidationResult(); 254 * final String validationMessage = 255 * uniquenessResponseControl.getValidationMessage(); 256 * 257 * throw e; 258 * } 259 * else 260 * { 261 * // The add failed, but the uniqueness response control indicates 262 * // that the failure was not because of a uniqueness conflict. 263 * throw e; 264 * } 265 * } 266 * </PRE> 267 */ 268@NotMutable() 269@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 270public final class UniquenessRequestControl 271 extends Control 272{ 273 /** 274 * The OID (1.3.6.1.4.1.30221.2.5.52) for the uniqueness request control. 275 */ 276 public static final String UNIQUENESS_REQUEST_OID = 277 "1.3.6.1.4.1.30221.2.5.52"; 278 279 280 281 /** 282 * The BER type for the uniqueness ID element in the value sequence. 283 */ 284 private static final byte TYPE_UNIQUENESS_ID = (byte) 0x80; 285 286 287 288 /** 289 * The BER type for the attribute types element in the value sequence. 290 */ 291 private static final byte TYPE_ATTRIBUTE_TYPES = (byte) 0xA1; 292 293 294 295 /** 296 * The BER type for the multiple attribute behavior element in the value 297 * sequence. 298 */ 299 private static final byte TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR = (byte) 0x82; 300 301 302 303 /** 304 * The BER type for the base DN element in the value sequence. 305 */ 306 private static final byte TYPE_BASE_DN = (byte) 0x83; 307 308 309 310 /** 311 * The BER type for the filter element in the value sequence. 312 */ 313 private static final byte TYPE_FILTER = (byte) 0xA4; 314 315 316 317 /** 318 * The BER type for the prevent conflicts with soft-deleted entries element in 319 * the value sequence. 320 */ 321 private static final byte TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES = 322 (byte) 0x85; 323 324 325 326 /** 327 * The BER type for the pre-commit validation element in the value sequence. 328 */ 329 private static final byte TYPE_PRE_COMMIT_VALIDATION_LEVEL = (byte) 0x86; 330 331 332 333 /** 334 * The BER type for the post-commit validation element in the value sequence. 335 */ 336 private static final byte TYPE_POST_COMMIT_VALIDATION_LEVEL = (byte) 0x87; 337 338 339 340 /** 341 * The serial version UID for this serializable class. 342 */ 343 private static final long serialVersionUID = 7976218379635922852L; 344 345 346 347 // Indicates whether to prevent conflicts with soft-deleted entries. 348 private final boolean preventConflictsWithSoftDeletedEntries; 349 350 // An optional filter that should be used in the course of identifying 351 // uniqueness conflicts. 352 private final Filter filter; 353 354 // A potentially-empty set of attribute types that should be checked for 355 // uniqueness conflicts. 356 private final Set<String> attributeTypes; 357 358 // An optional base DN to use when checking for conflicts. 359 private final String baseDN; 360 361 // A value that will be used to correlate this request control with its 362 // corresponding response control. 363 private final String uniquenessID; 364 365 // The behavior that the server should exhibit if multiple attribute types 366 // are configured. 367 private final UniquenessMultipleAttributeBehavior multipleAttributeBehavior; 368 369 // The level of validation that the server should perform before processing 370 // the associated change. 371 private final UniquenessValidationLevel postCommitValidationLevel; 372 373 // The level of validation that the server should perform after processing the 374 // associated change. 375 private final UniquenessValidationLevel preCommitValidationLevel; 376 377 378 379 /** 380 * Creates a new uniqueness request control with the provided information. 381 * 382 * @param isCritical Indicates whether the control should be considered 383 * critical. 384 * @param uniquenessID A value that will be used to correlate this request 385 * control with its corresponding response control. If 386 * this is {@code null}, then a unique identifier will 387 * be automatically generated. 388 * @param properties The set of properties for this control. It must not 389 * be {@code null}. 390 * 391 * @throws LDAPException If the provided properties cannot be used to create 392 * a valid uniqueness request control. 393 */ 394 public UniquenessRequestControl(final boolean isCritical, 395 final String uniquenessID, 396 final UniquenessRequestControlProperties properties) 397 throws LDAPException 398 { 399 this((uniquenessID == null 400 ? UUID.randomUUID().toString() 401 : uniquenessID), 402 properties, isCritical); 403 } 404 405 406 407 /** 408 * Creates a new uniqueness request control with the provided information. 409 * Note that this version of the constructor takes the same set of arguments 410 * as the above constructor, but in a different order (to distinguish between 411 * the two versions), and with the additional constraint that the uniqueness 412 * ID must not be {@code null}. 413 * 414 * @param isCritical Indicates whether the control should be considered 415 * critical. 416 * @param uniquenessID A value that will be used to correlate this request 417 * control with its corresponding response control. It 418 * must not be {@code null}. 419 * @param properties The set of properties for this control. It must not 420 * be {@code null}. 421 * 422 * @throws LDAPException If the provided properties cannot be used to create 423 * a valid uniqueness request control. 424 */ 425 private UniquenessRequestControl(final String uniquenessID, 426 final UniquenessRequestControlProperties properties, 427 final boolean isCritical) 428 throws LDAPException 429 { 430 super(UNIQUENESS_REQUEST_OID, isCritical, 431 encodeValue(uniquenessID, properties)); 432 433 Validator.ensureNotNull(uniquenessID); 434 this.uniquenessID = uniquenessID; 435 436 attributeTypes = properties.getAttributeTypes(); 437 multipleAttributeBehavior = properties.getMultipleAttributeBehavior(); 438 baseDN = properties.getBaseDN(); 439 filter = properties.getFilter(); 440 preventConflictsWithSoftDeletedEntries = 441 properties.preventConflictsWithSoftDeletedEntries(); 442 preCommitValidationLevel = properties.getPreCommitValidationLevel(); 443 postCommitValidationLevel = properties.getPostCommitValidationLevel(); 444 445 if (attributeTypes.isEmpty() && (filter == null)) 446 { 447 throw new LDAPException(ResultCode.PARAM_ERROR, 448 ERR_UNIQUENESS_REQ_NO_ATTRS_OR_FILTER.get()); 449 } 450 } 451 452 453 454 /** 455 * Encodes the provided information into an octet string that is suitable for 456 * use as the value of this control. 457 * 458 * @param uniquenessID A value that will be used to correlate this request 459 * control with its corresponding response control. It 460 * must not be {@code null}. 461 * @param properties The set of properties for this control. It must not 462 * be {@code null}. 463 * 464 * @return The encoded value that was created. 465 */ 466 private static ASN1OctetString encodeValue(final String uniquenessID, 467 final UniquenessRequestControlProperties properties) 468 { 469 final ArrayList<ASN1Element> elements = new ArrayList<>(8); 470 471 elements.add(new ASN1OctetString(TYPE_UNIQUENESS_ID, uniquenessID)); 472 473 final Set<String> attributeTypes = properties.getAttributeTypes(); 474 if (!attributeTypes.isEmpty()) 475 { 476 final ArrayList<ASN1Element> attributeTypeElements = 477 new ArrayList<>(attributeTypes.size()); 478 for (final String attributeType : attributeTypes) 479 { 480 attributeTypeElements.add(new ASN1OctetString(attributeType)); 481 } 482 elements.add(new ASN1Set(TYPE_ATTRIBUTE_TYPES, attributeTypeElements)); 483 } 484 485 final UniquenessMultipleAttributeBehavior multipleAttributeBehavior = 486 properties.getMultipleAttributeBehavior(); 487 if (multipleAttributeBehavior != 488 UniquenessMultipleAttributeBehavior.UNIQUE_WITHIN_EACH_ATTRIBUTE) 489 { 490 elements.add(new ASN1Enumerated(TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR, 491 multipleAttributeBehavior.intValue())); 492 } 493 494 final String baseDN = properties.getBaseDN(); 495 if (baseDN != null) 496 { 497 elements.add(new ASN1OctetString(TYPE_BASE_DN, baseDN)); 498 } 499 500 final Filter filter = properties.getFilter(); 501 if (filter != null) 502 { 503 elements.add(new ASN1Element(TYPE_FILTER, filter.encode().encode())); 504 } 505 506 if (properties.preventConflictsWithSoftDeletedEntries()) 507 { 508 elements.add(new ASN1Boolean( 509 TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES, true)); 510 } 511 512 final UniquenessValidationLevel preCommitValidationLevel = 513 properties.getPreCommitValidationLevel(); 514 if (preCommitValidationLevel != UniquenessValidationLevel.ALL_SUBTREE_VIEWS) 515 { 516 elements.add(new ASN1Enumerated(TYPE_PRE_COMMIT_VALIDATION_LEVEL, 517 preCommitValidationLevel.intValue())); 518 } 519 520 final UniquenessValidationLevel postCommitValidationLevel = 521 properties.getPostCommitValidationLevel(); 522 if (postCommitValidationLevel != 523 UniquenessValidationLevel.ALL_SUBTREE_VIEWS) 524 { 525 elements.add(new ASN1Enumerated(TYPE_POST_COMMIT_VALIDATION_LEVEL, 526 postCommitValidationLevel.intValue())); 527 } 528 529 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 530 } 531 532 533 534 /** 535 * Creates a new uniqueness request control that is decoded from the provided 536 * generic control. 537 * 538 * @param control The control to be decoded as a uniqueness request control. 539 * It must not be {@code null}. 540 * 541 * @throws LDAPException If the provided control cannot be decoded as a 542 * valid uniqueness request control. 543 */ 544 public UniquenessRequestControl(final Control control) 545 throws LDAPException 546 { 547 super(control); 548 549 final ASN1OctetString value = control.getValue(); 550 if (value == null) 551 { 552 throw new LDAPException(ResultCode.DECODING_ERROR, 553 ERR_UNIQUENESS_REQ_DECODE_NO_VALUE.get()); 554 } 555 556 try 557 { 558 boolean decodedPreventSoftDeletedConflicts = false; 559 Filter decodedFilter = null; 560 Set<String> decodedAttributeTypes = Collections.emptySet(); 561 String decodedBaseDN = null; 562 String decodedUniquenessID = null; 563 UniquenessMultipleAttributeBehavior decodedMultipleAttributeBehavior = 564 UniquenessMultipleAttributeBehavior.UNIQUE_WITHIN_EACH_ATTRIBUTE; 565 UniquenessValidationLevel decodedPreCommitLevel = 566 UniquenessValidationLevel.ALL_SUBTREE_VIEWS; 567 UniquenessValidationLevel decodedPostCommitLevel = 568 UniquenessValidationLevel.ALL_SUBTREE_VIEWS; 569 570 final ASN1Element[] elements = 571 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 572 for (final ASN1Element e : elements) 573 { 574 switch (e.getType()) 575 { 576 case TYPE_UNIQUENESS_ID: 577 decodedUniquenessID = 578 ASN1OctetString.decodeAsOctetString(e).stringValue(); 579 break; 580 case TYPE_ATTRIBUTE_TYPES: 581 final ASN1Element[] atElements = ASN1Set.decodeAsSet(e).elements(); 582 final LinkedHashSet<String> atNames = new LinkedHashSet<>( 583 StaticUtils.computeMapCapacity(atElements.length)); 584 for (final ASN1Element atElement : atElements) 585 { 586 atNames.add(ASN1OctetString.decodeAsOctetString( 587 atElement).stringValue()); 588 } 589 decodedAttributeTypes = Collections.unmodifiableSet(atNames); 590 break; 591 case TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR: 592 final int mabIntValue = 593 ASN1Enumerated.decodeAsEnumerated(e).intValue(); 594 decodedMultipleAttributeBehavior = 595 UniquenessMultipleAttributeBehavior.valueOf(mabIntValue); 596 if (decodedMultipleAttributeBehavior == null) 597 { 598 throw new LDAPException(ResultCode.DECODING_ERROR, 599 ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_MULTIPLE_ATTR_BEHAVIOR.get( 600 mabIntValue)); 601 } 602 break; 603 case TYPE_BASE_DN: 604 decodedBaseDN = 605 ASN1OctetString.decodeAsOctetString(e).stringValue(); 606 break; 607 case TYPE_FILTER: 608 decodedFilter = Filter.decode(ASN1Element.decode(e.getValue())); 609 break; 610 case TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES: 611 decodedPreventSoftDeletedConflicts = 612 ASN1Boolean.decodeAsBoolean(e).booleanValue(); 613 break; 614 case TYPE_PRE_COMMIT_VALIDATION_LEVEL: 615 final int preCommitIntValue = 616 ASN1Enumerated.decodeAsEnumerated(e).intValue(); 617 decodedPreCommitLevel = 618 UniquenessValidationLevel.valueOf(preCommitIntValue); 619 if (decodedPreCommitLevel == null) 620 { 621 throw new LDAPException(ResultCode.DECODING_ERROR, 622 ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_PRE_COMMIT_LEVEL.get( 623 preCommitIntValue)); 624 } 625 break; 626 case TYPE_POST_COMMIT_VALIDATION_LEVEL: 627 final int postCommitIntValue = 628 ASN1Enumerated.decodeAsEnumerated(e).intValue(); 629 decodedPostCommitLevel = 630 UniquenessValidationLevel.valueOf(postCommitIntValue); 631 if (decodedPostCommitLevel == null) 632 { 633 throw new LDAPException(ResultCode.DECODING_ERROR, 634 ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_POST_COMMIT_LEVEL.get( 635 postCommitIntValue)); 636 } 637 break; 638 default: 639 throw new LDAPException(ResultCode.DECODING_ERROR, 640 ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_ELEMENT_TYPE.get( 641 StaticUtils.toHex(e.getType()))); 642 } 643 } 644 645 if (decodedUniquenessID == null) 646 { 647 throw new LDAPException(ResultCode.DECODING_ERROR, 648 ERR_UNIQUENESS_REQ_MISSING_UNIQUENESS_ID.get()); 649 } 650 651 if (decodedAttributeTypes.isEmpty() && (decodedFilter == null)) 652 { 653 throw new LDAPException(ResultCode.DECODING_ERROR, 654 ERR_UNIQUENESS_REQ_NO_ATTRS_OR_FILTER.get()); 655 } 656 657 uniquenessID = decodedUniquenessID; 658 attributeTypes = decodedAttributeTypes; 659 multipleAttributeBehavior = decodedMultipleAttributeBehavior; 660 baseDN = decodedBaseDN; 661 filter = decodedFilter; 662 preventConflictsWithSoftDeletedEntries = 663 decodedPreventSoftDeletedConflicts; 664 preCommitValidationLevel = decodedPreCommitLevel; 665 postCommitValidationLevel = decodedPostCommitLevel; 666 } 667 catch (final LDAPException le) 668 { 669 Debug.debugException(le); 670 throw le; 671 } 672 catch (final Exception e) 673 { 674 Debug.debugException(e); 675 throw new LDAPException(ResultCode.DECODING_ERROR, 676 ERR_UNIQUENESS_REQ_DECODE_ERROR_DECODING_VALUE.get( 677 StaticUtils.getExceptionMessage(e)), 678 e); 679 } 680 } 681 682 683 684 /** 685 * Retrieves the uniqueness identifier for this control, which may be used to 686 * identify the response control that corresponds to this request control. 687 * This is primarily useful for requests that contain multiple uniqueness 688 * controls, as there may be a separate response control for each. 689 * 690 * @return The uniqueness identifier for this control. 691 */ 692 public String getUniquenessID() 693 { 694 return uniquenessID; 695 } 696 697 698 699 /** 700 * Retrieves the set of attribute types that the server will check for 701 * uniqueness conflicts. 702 * 703 * @return The set of attribute types that the server will check for 704 * uniqueness conflicts, or an empty set if only a filter should be 705 * used to identify conflicts. 706 */ 707 public Set<String> getAttributeTypes() 708 { 709 return attributeTypes; 710 } 711 712 713 714 /** 715 * Retrieves the behavior that the server should exhibit if multiple attribute 716 * types are configured. 717 * 718 * @return The behavior that the server should exhibit if multiple attribute 719 * types are configured. 720 */ 721 public UniquenessMultipleAttributeBehavior getMultipleAttributeBehavior() 722 { 723 return multipleAttributeBehavior; 724 } 725 726 727 728 /** 729 * Retrieves the base DN that will be used for searches used to identify 730 * uniqueness conflicts, if defined. 731 * 732 * @return The base DN that will be used for searches used to identify 733 * uniqueness conflicts, or {@code null} if the server should search 734 * below all public naming contexts. 735 */ 736 public String getBaseDN() 737 { 738 return baseDN; 739 } 740 741 742 743 /** 744 * Retrieves a filter that will be used to identify uniqueness conflicts, if 745 * defined. 746 * 747 * @return A filter that will be used to identify uniqueness conflicts, or 748 * {@code null} if no filter has been defined. 749 */ 750 public Filter getFilter() 751 { 752 return filter; 753 } 754 755 756 757 /** 758 * Indicates whether the server should attempt to identify conflicts with 759 * soft-deleted entries. 760 * 761 * @return {@code true} if the server should identify conflicts with both 762 * regular entries and soft-deleted entries, or {@code false} if the 763 * server should only identify conflicts with regular entries. 764 */ 765 public boolean preventConflictsWithSoftDeletedEntries() 766 { 767 return preventConflictsWithSoftDeletedEntries; 768 } 769 770 771 772 /** 773 * Retrieves the pre-commit validation level, which will be used to identify 774 * any conflicts before the associated request is processed. 775 * 776 * @return The pre-commit validation level. 777 */ 778 public UniquenessValidationLevel getPreCommitValidationLevel() 779 { 780 return preCommitValidationLevel; 781 } 782 783 784 785 /** 786 * Retrieves the post-commit validation level, which will be used to identify 787 * any conflicts that were introduced by the request with which the control is 788 * associated, or by some other concurrent changed processed in the server. 789 * 790 * @return The post-commit validation level. 791 */ 792 public UniquenessValidationLevel getPostCommitValidationLevel() 793 { 794 return postCommitValidationLevel; 795 } 796 797 798 799 /** 800 * {@inheritDoc} 801 */ 802 @Override() 803 public String getControlName() 804 { 805 return INFO_UNIQUENESS_REQ_CONTROL_NAME.get(); 806 } 807 808 809 810 /** 811 * {@inheritDoc} 812 */ 813 @Override() 814 public void toString(final StringBuilder buffer) 815 { 816 buffer.append("UniquenessRequestControl(isCritical="); 817 buffer.append(isCritical()); 818 buffer.append(", uniquenessID='"); 819 buffer.append(uniquenessID); 820 buffer.append("', attributeTypes={"); 821 822 final Iterator<String> attributeTypesIterator = attributeTypes.iterator(); 823 while (attributeTypesIterator.hasNext()) 824 { 825 buffer.append('\''); 826 buffer.append(attributeTypesIterator.next()); 827 buffer.append('\''); 828 829 if (attributeTypesIterator.hasNext()) 830 { 831 buffer.append(", "); 832 } 833 } 834 835 buffer.append("}, multipleAttributeBehavior="); 836 buffer.append(multipleAttributeBehavior); 837 838 if (baseDN != null) 839 { 840 buffer.append(", baseDN='"); 841 buffer.append(baseDN); 842 buffer.append('\''); 843 } 844 845 if (filter != null) 846 { 847 buffer.append(", filter='"); 848 buffer.append(filter); 849 buffer.append('\''); 850 } 851 852 buffer.append(", preventConflictsWithSoftDeletedEntries="); 853 buffer.append(preventConflictsWithSoftDeletedEntries); 854 buffer.append(", preCommitValidationLevel="); 855 buffer.append(preCommitValidationLevel); 856 buffer.append(", postCommitValidationLevel="); 857 buffer.append(postCommitValidationLevel); 858 buffer.append(')'); 859 } 860}