001/*
002 * Copyright 2017-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.controls;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.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 Alcatel-Lucent 8661
069 *   server products.  These classes provide support for proprietary
070 *   functionality or for external specifications that are not considered stable
071 *   or mature enough to be guaranteed to work in an interoperable way with
072 *   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 */
140@NotMutable()
141@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
142public final class UniquenessRequestControl
143       extends Control
144{
145  /**
146   * The OID (1.3.6.1.4.1.30221.2.5.52) for the uniqueness request control.
147   */
148  public static final String UNIQUENESS_REQUEST_OID =
149       "1.3.6.1.4.1.30221.2.5.52";
150
151
152
153  /**
154   * The BER type for the uniqueness ID element in the value sequence.
155   */
156  private static final byte TYPE_UNIQUENESS_ID = (byte) 0x80;
157
158
159
160  /**
161   * The BER type for the attribute types element in the value sequence.
162   */
163  private static final byte TYPE_ATTRIBUTE_TYPES = (byte) 0xA1;
164
165
166
167  /**
168   * The BER type for the multiple attribute behavior element in the value
169   * sequence.
170   */
171  private static final byte TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR = (byte) 0x82;
172
173
174
175  /**
176   * The BER type for the base DN element in the value sequence.
177   */
178  private static final byte TYPE_BASE_DN = (byte) 0x83;
179
180
181
182  /**
183   * The BER type for the filter element in the value sequence.
184   */
185  private static final byte TYPE_FILTER = (byte) 0xA4;
186
187
188
189  /**
190   * The BER type for the prevent conflicts with soft-deleted entries element in
191   * the value sequence.
192   */
193  private static final byte TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES =
194       (byte) 0x85;
195
196
197
198  /**
199   * The BER type for the pre-commit validation element in the value sequence.
200   */
201  private static final byte TYPE_PRE_COMMIT_VALIDATION_LEVEL = (byte) 0x86;
202
203
204
205  /**
206   * The BER type for the post-commit validation element in the value sequence.
207   */
208  private static final byte TYPE_POST_COMMIT_VALIDATION_LEVEL = (byte) 0x87;
209
210
211
212  /**
213   * The serial version UID for this serializable class.
214   */
215  private static final long serialVersionUID = 7976218379635922852L;
216
217
218
219  // Indicates whether to prevent conflicts with soft-deleted entries.
220  private final boolean preventConflictsWithSoftDeletedEntries;
221
222  // An optional filter that should be used in the course of identifying
223  // uniqueness conflicts.
224  private final Filter filter;
225
226  // A potentially-empty set of attribute types that should be checked for
227  // uniqueness conflicts.
228  private final Set<String> attributeTypes;
229
230  // An optional base DN to use when checking for conflicts.
231  private final String baseDN;
232
233  // A value that will be used to correlate this request control with its
234  // corresponding response control.
235  private final String uniquenessID;
236
237  // The behavior that the server should exhibit if multiple attribute types
238  // are configured.
239  private final UniquenessMultipleAttributeBehavior multipleAttributeBehavior;
240
241  // The level of validation that the server should perform before processing
242  // the associated change.
243  private final UniquenessValidationLevel postCommitValidationLevel;
244
245  // The level of validation that the server should perform after processing the
246  // associated change.
247  private final UniquenessValidationLevel preCommitValidationLevel;
248
249
250
251  /**
252   * Creates a new uniqueness request control with the provided information.
253   *
254   * @param  isCritical    Indicates whether the control should be considered
255   *                       critical.
256   * @param  uniquenessID  A value that will be used to correlate this request
257   *                       control with its corresponding response control.  If
258   *                       this is {@code null}, then a unique identifier will
259   *                       be automatically generated.
260   * @param  properties    The set of properties for this control.  It must not
261   *                       be {@code null}.
262   *
263   * @throws  LDAPException  If the provided properties cannot be used to create
264   *                         a valid uniqueness request control.
265   */
266  public UniquenessRequestControl(final boolean isCritical,
267              final String uniquenessID,
268              final UniquenessRequestControlProperties properties)
269         throws LDAPException
270  {
271    this((uniquenessID == null
272              ? UUID.randomUUID().toString()
273              : uniquenessID),
274         properties, isCritical);
275  }
276
277
278
279  /**
280   * Creates a new uniqueness request control with the provided information.
281   * Note that this version of the constructor takes the same set of arguments
282   * as the above constructor, but in a different order (to distinguish between
283   * the two versions), and with the additional constraint that the uniqueness
284   * ID must not be {@code null}.
285   *
286   * @param  isCritical    Indicates whether the control should be considered
287   *                       critical.
288   * @param  uniquenessID  A value that will be used to correlate this request
289   *                       control with its corresponding response control.  It
290   *                       must not be {@code null}.
291   * @param  properties    The set of properties for this control.  It must not
292   *                       be {@code null}.
293   *
294   * @throws  LDAPException  If the provided properties cannot be used to create
295   *                         a valid uniqueness request control.
296   */
297  private UniquenessRequestControl(final String uniquenessID,
298               final UniquenessRequestControlProperties properties,
299               final boolean isCritical)
300          throws LDAPException
301  {
302    super(UNIQUENESS_REQUEST_OID, isCritical,
303         encodeValue(uniquenessID, properties));
304
305    Validator.ensureNotNull(uniquenessID);
306    this.uniquenessID = uniquenessID;
307
308    attributeTypes = properties.getAttributeTypes();
309    multipleAttributeBehavior = properties.getMultipleAttributeBehavior();
310    baseDN = properties.getBaseDN();
311    filter = properties.getFilter();
312    preventConflictsWithSoftDeletedEntries =
313         properties.preventConflictsWithSoftDeletedEntries();
314    preCommitValidationLevel = properties.getPreCommitValidationLevel();
315    postCommitValidationLevel = properties.getPostCommitValidationLevel();
316
317    if (attributeTypes.isEmpty() && (filter == null))
318    {
319      throw new LDAPException(ResultCode.PARAM_ERROR,
320           ERR_UNIQUENESS_REQ_NO_ATTRS_OR_FILTER.get());
321    }
322  }
323
324
325
326  /**
327   * Encodes the provided information into an octet string that is suitable for
328   * use as the value of this control.
329   *
330   * @param  uniquenessID  A value that will be used to correlate this request
331   *                       control with its corresponding response control.  It
332   *                       must not be {@code null}.
333   * @param  properties    The set of properties for this control.  It must not
334   *                       be {@code null}.
335   *
336   * @return  The encoded value that was created.
337   */
338  private static ASN1OctetString encodeValue(final String uniquenessID,
339                      final UniquenessRequestControlProperties properties)
340  {
341    final ArrayList<ASN1Element> elements = new ArrayList<>(8);
342
343    elements.add(new ASN1OctetString(TYPE_UNIQUENESS_ID, uniquenessID));
344
345    final Set<String> attributeTypes = properties.getAttributeTypes();
346    if (!attributeTypes.isEmpty())
347    {
348      final ArrayList<ASN1Element> attributeTypeElements =
349           new ArrayList<>(attributeTypes.size());
350      for (final String attributeType : attributeTypes)
351      {
352        attributeTypeElements.add(new ASN1OctetString(attributeType));
353      }
354      elements.add(new ASN1Set(TYPE_ATTRIBUTE_TYPES, attributeTypeElements));
355    }
356
357    final UniquenessMultipleAttributeBehavior multipleAttributeBehavior =
358         properties.getMultipleAttributeBehavior();
359    if (multipleAttributeBehavior !=
360         UniquenessMultipleAttributeBehavior.UNIQUE_WITHIN_EACH_ATTRIBUTE)
361    {
362      elements.add(new ASN1Enumerated(TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR,
363           multipleAttributeBehavior.intValue()));
364    }
365
366    final String baseDN = properties.getBaseDN();
367    if (baseDN != null)
368    {
369      elements.add(new ASN1OctetString(TYPE_BASE_DN, baseDN));
370    }
371
372    final Filter filter = properties.getFilter();
373    if (filter != null)
374    {
375      elements.add(new ASN1Element(TYPE_FILTER, filter.encode().encode()));
376    }
377
378    if (properties.preventConflictsWithSoftDeletedEntries())
379    {
380      elements.add(new ASN1Boolean(
381           TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES, true));
382    }
383
384    final UniquenessValidationLevel preCommitValidationLevel =
385         properties.getPreCommitValidationLevel();
386    if (preCommitValidationLevel != UniquenessValidationLevel.ALL_SUBTREE_VIEWS)
387    {
388      elements.add(new ASN1Enumerated(TYPE_PRE_COMMIT_VALIDATION_LEVEL,
389           preCommitValidationLevel.intValue()));
390    }
391
392    final UniquenessValidationLevel postCommitValidationLevel =
393         properties.getPostCommitValidationLevel();
394    if (postCommitValidationLevel !=
395         UniquenessValidationLevel.ALL_SUBTREE_VIEWS)
396    {
397      elements.add(new ASN1Enumerated(TYPE_POST_COMMIT_VALIDATION_LEVEL,
398           postCommitValidationLevel.intValue()));
399    }
400
401    return new ASN1OctetString(new ASN1Sequence(elements).encode());
402  }
403
404
405
406  /**
407   * Creates a new uniqueness request control that is decoded from the provided
408   * generic control.
409   *
410   * @param  control  The control to be decoded as a uniqueness request control.
411   *                  It must not be {@code null}.
412   *
413   * @throws  LDAPException  If the provided control cannot be decoded as a
414   *                         valid uniqueness request control.
415   */
416  public UniquenessRequestControl(final Control control)
417         throws LDAPException
418  {
419    super(control);
420
421    final ASN1OctetString value = control.getValue();
422    if (value == null)
423    {
424      throw new LDAPException(ResultCode.DECODING_ERROR,
425           ERR_UNIQUENESS_REQ_DECODE_NO_VALUE.get());
426    }
427
428    try
429    {
430      boolean decodedPreventSoftDeletedConflicts = false;
431      Filter decodedFilter = null;
432      Set<String> decodedAttributeTypes = Collections.emptySet();
433      String decodedBaseDN = null;
434      String decodedUniquenessID = null;
435      UniquenessMultipleAttributeBehavior decodedMultipleAttributeBehavior =
436           UniquenessMultipleAttributeBehavior.UNIQUE_WITHIN_EACH_ATTRIBUTE;
437      UniquenessValidationLevel decodedPreCommitLevel =
438           UniquenessValidationLevel.ALL_SUBTREE_VIEWS;
439      UniquenessValidationLevel decodedPostCommitLevel =
440           UniquenessValidationLevel.ALL_SUBTREE_VIEWS;
441
442      final ASN1Element[] elements =
443           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
444      for (final ASN1Element e : elements)
445      {
446        switch (e.getType())
447        {
448          case TYPE_UNIQUENESS_ID:
449            decodedUniquenessID =
450                 ASN1OctetString.decodeAsOctetString(e).stringValue();
451            break;
452          case TYPE_ATTRIBUTE_TYPES:
453            final ASN1Element[] atElements = ASN1Set.decodeAsSet(e).elements();
454            final LinkedHashSet<String> atNames =
455                 new LinkedHashSet<>(atElements.length);
456            for (final ASN1Element atElement : atElements)
457            {
458              atNames.add(ASN1OctetString.decodeAsOctetString(
459                   atElement).stringValue());
460            }
461            decodedAttributeTypes = Collections.unmodifiableSet(atNames);
462            break;
463          case TYPE_MULTIPLE_ATTRIBUTE_BEHAVIOR:
464            final int mabIntValue =
465                 ASN1Enumerated.decodeAsEnumerated(e).intValue();
466            decodedMultipleAttributeBehavior =
467                 UniquenessMultipleAttributeBehavior.valueOf(mabIntValue);
468            if (decodedMultipleAttributeBehavior == null)
469            {
470              throw new LDAPException(ResultCode.DECODING_ERROR,
471                   ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_MULTIPLE_ATTR_BEHAVIOR.get(
472                        mabIntValue));
473            }
474            break;
475          case TYPE_BASE_DN:
476            decodedBaseDN =
477                 ASN1OctetString.decodeAsOctetString(e).stringValue();
478            break;
479          case TYPE_FILTER:
480            decodedFilter = Filter.decode(ASN1Element.decode(e.getValue()));
481            break;
482          case TYPE_PREVENT_CONFLICTS_WITH_SOFT_DELETED_ENTRIES:
483            decodedPreventSoftDeletedConflicts =
484                 ASN1Boolean.decodeAsBoolean(e).booleanValue();
485            break;
486          case TYPE_PRE_COMMIT_VALIDATION_LEVEL:
487            final int preCommitIntValue =
488                 ASN1Enumerated.decodeAsEnumerated(e).intValue();
489            decodedPreCommitLevel =
490                 UniquenessValidationLevel.valueOf(preCommitIntValue);
491            if (decodedPreCommitLevel == null)
492            {
493              throw new LDAPException(ResultCode.DECODING_ERROR,
494                   ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_PRE_COMMIT_LEVEL.get(
495                        preCommitIntValue));
496            }
497            break;
498          case TYPE_POST_COMMIT_VALIDATION_LEVEL:
499            final int postCommitIntValue =
500                 ASN1Enumerated.decodeAsEnumerated(e).intValue();
501            decodedPostCommitLevel =
502                 UniquenessValidationLevel.valueOf(postCommitIntValue);
503            if (decodedPostCommitLevel == null)
504            {
505              throw new LDAPException(ResultCode.DECODING_ERROR,
506                   ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_POST_COMMIT_LEVEL.get(
507                        postCommitIntValue));
508            }
509            break;
510          default:
511            throw new LDAPException(ResultCode.DECODING_ERROR,
512                 ERR_UNIQUENESS_REQ_DECODE_UNKNOWN_ELEMENT_TYPE.get(
513                      StaticUtils.toHex(e.getType())));
514        }
515      }
516
517      if (decodedUniquenessID == null)
518      {
519        throw new LDAPException(ResultCode.DECODING_ERROR,
520             ERR_UNIQUENESS_REQ_MISSING_UNIQUENESS_ID.get());
521      }
522
523      if (decodedAttributeTypes.isEmpty() && (decodedFilter == null))
524      {
525        throw new LDAPException(ResultCode.DECODING_ERROR,
526             ERR_UNIQUENESS_REQ_NO_ATTRS_OR_FILTER.get());
527      }
528
529      uniquenessID = decodedUniquenessID;
530      attributeTypes = decodedAttributeTypes;
531      multipleAttributeBehavior = decodedMultipleAttributeBehavior;
532      baseDN = decodedBaseDN;
533      filter = decodedFilter;
534      preventConflictsWithSoftDeletedEntries =
535           decodedPreventSoftDeletedConflicts;
536      preCommitValidationLevel = decodedPreCommitLevel;
537      postCommitValidationLevel = decodedPostCommitLevel;
538    }
539    catch (final LDAPException le)
540    {
541      Debug.debugException(le);
542      throw le;
543    }
544    catch (final Exception e)
545    {
546      Debug.debugException(e);
547      throw new LDAPException(ResultCode.DECODING_ERROR,
548           ERR_UNIQUENESS_REQ_DECODE_ERROR_DECODING_VALUE.get(
549                StaticUtils.getExceptionMessage(e)),
550           e);
551    }
552  }
553
554
555
556  /**
557   * Retrieves the uniqueness identifier for this control, which may be used to
558   * identify the response control that corresponds to this request control.
559   * This is primarily useful for requests that contain multiple uniqueness
560   * controls, as there may be a separate response control for each.
561   *
562   * @return  The uniqueness identifier for this control.
563   */
564  public String getUniquenessID()
565  {
566    return uniquenessID;
567  }
568
569
570
571  /**
572   * Retrieves the set of attribute types that the server will check for
573   * uniqueness conflicts.
574   *
575   * @return  The set of attribute types that the server will check for
576   *          uniqueness conflicts, or an empty set if only a filter should be
577   *          used to identify conflicts.
578   */
579  public Set<String> getAttributeTypes()
580  {
581    return attributeTypes;
582  }
583
584
585
586  /**
587   * Retrieves the behavior that the server should exhibit if multiple attribute
588   * types are configured.
589   *
590   * @return  The behavior that the server should exhibit if multiple attribute
591   *          types are configured.
592   */
593  public UniquenessMultipleAttributeBehavior getMultipleAttributeBehavior()
594  {
595    return multipleAttributeBehavior;
596  }
597
598
599
600  /**
601   * Retrieves the base DN that will be used for searches used to identify
602   * uniqueness conflicts, if defined.
603   *
604   * @return  The base DN that will be used for searches used to identify
605   *          uniqueness conflicts, or {@code null} if the server should search
606   *          below all public naming contexts.
607   */
608  public String getBaseDN()
609  {
610    return baseDN;
611  }
612
613
614
615  /**
616   * Retrieves a filter that will be used to identify uniqueness conflicts, if
617   * defined.
618   *
619   * @return  A filter that will be used to identify uniqueness conflicts, or
620   *          {@code null} if no filter has been defined.
621   */
622  public Filter getFilter()
623  {
624    return filter;
625  }
626
627
628
629  /**
630   * Indicates whether the server should attempt to identify conflicts with
631   * soft-deleted entries.
632   *
633   * @return  {@code true} if the server should identify conflicts with both
634   *          regular entries and soft-deleted entries, or {@code false} if the
635   *          server should only identify conflicts with regular entries.
636   */
637  public boolean preventConflictsWithSoftDeletedEntries()
638  {
639    return preventConflictsWithSoftDeletedEntries;
640  }
641
642
643
644  /**
645   * Retrieves the pre-commit validation level, which will be used to identify
646   * any conflicts before the associated request is processed.
647   *
648   * @return  The pre-commit validation level.
649   */
650  public UniquenessValidationLevel getPreCommitValidationLevel()
651  {
652    return preCommitValidationLevel;
653  }
654
655
656
657  /**
658   * Retrieves the post-commit validation level, which will be used to identify
659   * any conflicts that were introduced by the request with which the control is
660   * associated, or by some other concurrent changed processed in the server.
661   *
662   * @return  The post-commit validation level.
663   */
664  public UniquenessValidationLevel getPostCommitValidationLevel()
665  {
666    return postCommitValidationLevel;
667  }
668
669
670
671  /**
672   * {@inheritDoc}
673   */
674  @Override()
675  public String getControlName()
676  {
677    return INFO_UNIQUENESS_REQ_CONTROL_NAME.get();
678  }
679
680
681
682  /**
683   * {@inheritDoc}
684   */
685  @Override()
686  public void toString(final StringBuilder buffer)
687  {
688    buffer.append("UniquenessRequestControl(isCritical=");
689    buffer.append(isCritical());
690    buffer.append(", uniquenessID='");
691    buffer.append(uniquenessID);
692    buffer.append("', attributeTypes={");
693
694    final Iterator<String> attributeTypesIterator = attributeTypes.iterator();
695    while (attributeTypesIterator.hasNext())
696    {
697      buffer.append('\'');
698      buffer.append(attributeTypesIterator.next());
699      buffer.append('\'');
700
701      if (attributeTypesIterator.hasNext())
702      {
703        buffer.append(", ");
704      }
705    }
706
707    buffer.append("}, multipleAttributeBehavior=");
708    buffer.append(multipleAttributeBehavior);
709
710    if (baseDN != null)
711    {
712      buffer.append(", baseDN='");
713      buffer.append(baseDN);
714      buffer.append('\'');
715    }
716
717    if (filter != null)
718    {
719      buffer.append(", filter='");
720      buffer.append(filter);
721      buffer.append('\'');
722    }
723
724    buffer.append(", preventConflictsWithSoftDeletedEntries=");
725    buffer.append(preventConflictsWithSoftDeletedEntries);
726    buffer.append(", preCommitValidationLevel=");
727    buffer.append(preCommitValidationLevel);
728    buffer.append(", postCommitValidationLevel=");
729    buffer.append(postCommitValidationLevel);
730    buffer.append(')');
731  }
732}