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