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