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