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&lt;String,UniquenessResponseControl&gt; 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) &amp;&amp;
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&lt;String, UniquenessResponseControl&gt; 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}