001/*
002 * Copyright 2017-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2017-2024 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-2024 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.LinkedHashMap;
043import java.util.List;
044import java.util.Map;
045
046import com.unboundid.asn1.ASN1Boolean;
047import com.unboundid.asn1.ASN1Element;
048import com.unboundid.asn1.ASN1OctetString;
049import com.unboundid.asn1.ASN1Sequence;
050import com.unboundid.ldap.sdk.Control;
051import com.unboundid.ldap.sdk.DecodeableControl;
052import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
053import com.unboundid.ldap.sdk.LDAPException;
054import com.unboundid.ldap.sdk.LDAPResult;
055import com.unboundid.ldap.sdk.ResultCode;
056import com.unboundid.util.Debug;
057import com.unboundid.util.NotMutable;
058import com.unboundid.util.NotNull;
059import com.unboundid.util.Nullable;
060import com.unboundid.util.StaticUtils;
061import com.unboundid.util.ThreadSafety;
062import com.unboundid.util.ThreadSafetyLevel;
063import com.unboundid.util.Validator;
064import com.unboundid.util.json.JSONBoolean;
065import com.unboundid.util.json.JSONField;
066import com.unboundid.util.json.JSONObject;
067import com.unboundid.util.json.JSONString;
068import com.unboundid.util.json.JSONValue;
069
070import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
071
072
073
074/**
075 * This class provides a response control that may be included in the response
076 * to add, modify, and modify DN requests that included the
077 * {@link UniquenessRequestControl}.  It provides information about the
078 * uniqueness processing that was performed.
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 control has an OID of 1.3.6.1.4.1.30221.2.5.53 and a criticality of
091 * false.  It must have a value with the following encoding:
092 * <PRE>
093 *   UniquenessResponseValue ::= SEQUENCE {
094 *     uniquenessID                [0] OCTET STRING,
095 *     preCommitValidationPassed   [1] BOOLEAN OPTIONAL,
096 *     postCommitValidationPassed  [2] BOOLEAN OPTIONAL,
097 *     validationMessage           [3] OCTET STRING OPTIONAL,
098 *     ... }
099 * </PRE>
100 */
101@NotMutable()
102@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
103public final class UniquenessResponseControl
104       extends Control
105       implements DecodeableControl
106{
107  /**
108   * The OID (1.3.6.1.4.1.30221.2.5.53) for the uniqueness response control.
109   */
110  @NotNull public static final String UNIQUENESS_RESPONSE_OID =
111       "1.3.6.1.4.1.30221.2.5.53";
112
113
114
115  /**
116   * The BER type for the uniqueness ID element in the value sequence.
117   */
118  private static final byte TYPE_UNIQUENESS_ID = (byte) 0x80;
119
120
121
122  /**
123   * The BER type for the pre-commit validation passed element in the value
124   * sequence.
125   */
126  private static final byte TYPE_PRE_COMMIT_VALIDATION_PASSED = (byte) 0x81;
127
128
129
130  /**
131   * The BER type for the post-commit validation passed element in the value
132   * sequence.
133   */
134  private static final byte TYPE_POST_COMMIT_VALIDATION_PASSED = (byte) 0x82;
135
136
137
138  /**
139   * The BER type for the validation message element in the value sequence.
140   */
141  private static final byte TYPE_VALIDATION_MESSAGE = (byte) 0x83;
142
143
144
145  /**
146   * The name of the field used to hold the post-commit-validation-passed flag
147   * in the JSON representation of this control.
148   */
149  @NotNull private static final String
150       JSON_FIELD_POST_COMMIT_VALIDATION_PASSED =
151            "post-commit-validation-passed";
152
153
154
155  /**
156   * The name of the field used to hold the pre-commit-validation-passed flag in
157   * the JSON representation of this control.
158   */
159  @NotNull private static final String JSON_FIELD_PRE_COMMIT_VALIDATION_PASSED =
160       "pre-commit-validation-passed";
161
162
163
164  /**
165   * The name of the field used to hold the uniqueness ID in the JSON
166   * representation of this control.
167   */
168  @NotNull private static final String JSON_FIELD_UNIQUENESS_ID =
169       "uniqueness-id";
170
171
172
173  /**
174   * The name of the field used to hold the validation message in the JSON
175   * representation of this control.
176   */
177  @NotNull private static final String JSON_FIELD_VALIDATION_MESSAGE =
178       "validation-message";
179
180
181
182  /**
183   * The serial version UID for this serializable class.
184   */
185  private static final long serialVersionUID = 5090348902351420617L;
186
187
188
189  // Indicates whether post-commit validation passed.
190  @Nullable private final Boolean postCommitValidationPassed;
191
192  // Indicates whether pre-commit validation passed.
193  @Nullable private final Boolean preCommitValidationPassed;
194
195  // A value that will be used to correlate this response control with its
196  // corresponding request control.
197  @NotNull private final String uniquenessID;
198
199  // The validation message, if any.
200  @Nullable private final String validationMessage;
201
202
203
204  /**
205   * Creates a new empty control instance that is intended to be used only for
206   * decoding controls via the {@code DecodeableControl} interface.
207   */
208  UniquenessResponseControl()
209  {
210    uniquenessID = null;
211    preCommitValidationPassed = null;
212    postCommitValidationPassed = null;
213    validationMessage = null;
214  }
215
216
217
218  /**
219   * Creates a new uniqueness response control with the provided information.
220   *
221   * @param  uniquenessID                The uniqueness ID that may be used to
222   *                                     correlate this uniqueness response
223   *                                     control with the corresponding request
224   *                                     control.  This must not be
225   *                                     {@code null}.
226   * @param  preCommitValidationPassed   Indicates whether the pre-commit
227   *                                     validation was successful.  This may be
228   *                                     {@code null} if no pre-commit
229   *                                     validation was attempted.
230   * @param  postCommitValidationPassed  Indicates whether the post-commit
231   *                                     validation was successful.  This may be
232   *                                     {@code null} if no post-commit
233   *                                     validation was attempted.
234   * @param  validationMessage           A message with additional information
235   *                                     about the validation processing.  This
236   *                                     may be {@code null} if no validation
237   *                                     message is needed.
238   */
239  public UniquenessResponseControl(@NotNull final String uniquenessID,
240              @Nullable final Boolean preCommitValidationPassed,
241              @Nullable final Boolean postCommitValidationPassed,
242              @Nullable final String validationMessage)
243  {
244    super(UNIQUENESS_RESPONSE_OID, false,
245         encodeValue(uniquenessID, preCommitValidationPassed,
246              postCommitValidationPassed, validationMessage));
247
248    Validator.ensureNotNull(uniquenessID);
249
250    this.uniquenessID = uniquenessID;
251    this.preCommitValidationPassed = preCommitValidationPassed;
252    this.postCommitValidationPassed = postCommitValidationPassed;
253    this.validationMessage = validationMessage;
254  }
255
256
257
258  /**
259   * Encodes the provided information into an ASN.1 octet string suitable for
260   * use as the value of this control.
261   *
262   * @param  uniquenessID                The uniqueness ID that may be used to
263   *                                     correlate this uniqueness response
264   *                                     control with the corresponding request
265   *                                     control.  This must not be
266   *                                     {@code null}.
267   * @param  preCommitValidationPassed   Indicates whether the pre-commit
268   *                                     validation was successful.  This may be
269   *                                     {@code null} if no pre-commit
270   *                                     validation was attempted.
271   * @param  postCommitValidationPassed  Indicates whether the post-commit
272   *                                     validation was successful.  This may be
273   *                                     {@code null} if no post-commit
274   *                                     validation was attempted.
275   * @param  validationMessage           A message with additional information
276   *                                     about the validation processing.  This
277   *                                     may be {@code null} if no validation
278   *                                     message is needed.
279   *
280   * @return  The encoded control value.
281   */
282  @NotNull()
283  private static ASN1OctetString encodeValue(
284               @NotNull final String uniquenessID,
285               @Nullable final Boolean preCommitValidationPassed,
286               @Nullable final Boolean postCommitValidationPassed,
287               @Nullable final String validationMessage)
288  {
289    final ArrayList<ASN1Element> elements = new ArrayList<>(4);
290    elements.add(new ASN1OctetString(TYPE_UNIQUENESS_ID, uniquenessID));
291
292    if (preCommitValidationPassed != null)
293    {
294      elements.add(new ASN1Boolean(TYPE_PRE_COMMIT_VALIDATION_PASSED,
295           preCommitValidationPassed));
296    }
297
298    if (postCommitValidationPassed != null)
299    {
300      elements.add(new ASN1Boolean(TYPE_POST_COMMIT_VALIDATION_PASSED,
301           postCommitValidationPassed));
302    }
303
304    if (validationMessage != null)
305    {
306      elements.add(new ASN1OctetString(TYPE_VALIDATION_MESSAGE,
307           validationMessage));
308    }
309
310    return new ASN1OctetString(new ASN1Sequence(elements).encode());
311  }
312
313
314
315  /**
316   * Creates a new uniqueness response control with the provided information.
317   *
318   * @param  oid         The OID for the control.
319   * @param  isCritical  Indicates whether the control should be marked
320   *                     critical.
321   * @param  value       The encoded value for the control.  This may be
322   *                     {@code null} if no value was provided.
323   *
324   * @throws  LDAPException  If the provided control cannot be decoded as a
325   *                         uniqueness response control.
326   */
327  public UniquenessResponseControl(@NotNull final String oid,
328                                   final boolean isCritical,
329                                   @Nullable final ASN1OctetString value)
330         throws LDAPException
331  {
332    super(oid, isCritical, value);
333
334    if (value == null)
335    {
336      throw new LDAPException(ResultCode.DECODING_ERROR,
337           ERR_UNIQUENESS_RES_DECODE_NO_VALUE.get());
338    }
339
340    try
341    {
342      String id = null;
343      Boolean prePassed = null;
344      Boolean postPassed = null;
345      String message = null;
346      for (final ASN1Element e :
347           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
348      {
349        switch (e.getType())
350        {
351          case TYPE_UNIQUENESS_ID:
352            id = ASN1OctetString.decodeAsOctetString(e).stringValue();
353            break;
354          case TYPE_PRE_COMMIT_VALIDATION_PASSED:
355            prePassed = ASN1Boolean.decodeAsBoolean(e).booleanValue();
356            break;
357          case TYPE_POST_COMMIT_VALIDATION_PASSED:
358            postPassed = ASN1Boolean.decodeAsBoolean(e).booleanValue();
359            break;
360          case TYPE_VALIDATION_MESSAGE:
361            message = ASN1OctetString.decodeAsOctetString(e).stringValue();
362            break;
363          default:
364            throw new LDAPException(ResultCode.DECODING_ERROR,
365                 ERR_UNIQUENESS_RES_DECODE_UNKNOWN_ELEMENT_TYPE.get(
366                      StaticUtils.toHex(e.getType())));
367        }
368      }
369
370      if (id == null)
371      {
372        throw new LDAPException(ResultCode.DECODING_ERROR,
373             ERR_UNIQUENESS_RES_DECODE_NO_UNIQUENESS_ID.get());
374      }
375
376      uniquenessID = id;
377      preCommitValidationPassed = prePassed;
378      postCommitValidationPassed = postPassed;
379      validationMessage = message;
380    }
381    catch (final LDAPException le)
382    {
383      Debug.debugException(le);
384      throw le;
385    }
386    catch (final Exception e)
387    {
388      Debug.debugException(e);
389      throw new LDAPException(ResultCode.DECODING_ERROR,
390           ERR_UNIQUENESS_RES_DECODE_ERROR.get(
391                StaticUtils.getExceptionMessage(e)),
392           e);
393    }
394  }
395
396
397
398  /**
399   * {@inheritDoc}
400   */
401  @Override()
402  @NotNull()
403  public UniquenessResponseControl decodeControl(@NotNull final String oid,
404              final boolean isCritical,
405              @Nullable final ASN1OctetString value)
406         throws LDAPException
407  {
408    return new UniquenessResponseControl(oid, isCritical, value);
409  }
410
411
412
413  /**
414   * Retrieves the set of uniqueness response controls included in the provided
415   * result.
416   *
417   * @param  result  The result to process.
418   *
419   * @return  The set of uniqueness response controls included in the provided
420   *          result, indexed by uniqueness ID.  It may be empty if the result
421   *          does not include any uniqueness response controls.
422   *
423   * @throws  LDAPException  If a problem is encountered while getting the set
424   *                         of uniqueness response controls contained in the
425   *                         provided result.
426   */
427  @NotNull()
428  public static Map<String,UniquenessResponseControl> get(
429                     @NotNull final LDAPResult result)
430         throws LDAPException
431  {
432    final Control[] responseControls = result.getResponseControls();
433    if (responseControls.length == 0)
434    {
435      return Collections.emptyMap();
436    }
437
438    final LinkedHashMap<String,UniquenessResponseControl> controlMap =
439         new LinkedHashMap<>(StaticUtils.computeMapCapacity(
440              responseControls.length));
441    for (final Control c : responseControls)
442    {
443      if (! c.getOID().equals(UNIQUENESS_RESPONSE_OID))
444      {
445        continue;
446      }
447
448      final UniquenessResponseControl urc;
449      if (c instanceof UniquenessResponseControl)
450      {
451        urc = (UniquenessResponseControl) c;
452      }
453      else
454      {
455        urc = new UniquenessResponseControl().decodeControl(c.getOID(),
456             c.isCritical(), c.getValue());
457      }
458
459      final String uniquenessID = urc.getUniquenessID();
460      if (controlMap.containsKey(uniquenessID))
461      {
462        throw new LDAPException(ResultCode.DECODING_ERROR,
463             ERR_UNIQUENESS_RES_GET_ID_CONFLICT.get(uniquenessID));
464      }
465      else
466      {
467        controlMap.put(uniquenessID, urc);
468      }
469    }
470
471    return Collections.unmodifiableMap(controlMap);
472  }
473
474
475
476  /**
477   * Indicates whether a uniqueness conflict was found during processing.
478   *
479   * @return  {@code true} if a uniqueness conflict was found during processing,
480   *          or {@code false} if no conflict was found or if no validation was
481   *          attempted.
482   */
483  public boolean uniquenessConflictFound()
484  {
485    return ((preCommitValidationPassed == Boolean.FALSE) ||
486         (postCommitValidationPassed == Boolean.FALSE));
487  }
488
489
490
491  /**
492   * Retrieves the identifier that may be used to correlate this uniqueness
493   * response control with the corresponding request control.  This is primarily
494   * useful for requests that contain multiple uniqueness controls, as there may
495   * be a separate response control for each.
496   *
497   * @return  The identifier that may be used to correlate this uniqueness
498   *          response control with the corresponding request control.
499   */
500  @NotNull()
501  public String getUniquenessID()
502  {
503    return uniquenessID;
504  }
505
506
507
508  /**
509   * Retrieves the result of the server's pre-commit validation processing.
510   * The same information can be inferred from the
511   * {@link #getPreCommitValidationPassed()} method, but this method may provide
512   * a more intuitive result and does not have the possibility of a {@code null}
513   * return value.
514   *
515   * @return  {@link UniquenessValidationResult#VALIDATION_PASSED} if the
516   *          server did not find any conflicting entries during the pre-commit
517   *          check, {@link UniquenessValidationResult#VALIDATION_FAILED} if
518   *          the server found at least one conflicting entry during the
519   *          pre-commit check, or
520   *          {@link UniquenessValidationResult#VALIDATION_NOT_ATTEMPTED} if
521   *          the server did not attempt any pre-commit validation.
522   */
523  @NotNull()
524  public UniquenessValidationResult getPreCommitValidationResult()
525  {
526    if (preCommitValidationPassed == null)
527    {
528      return UniquenessValidationResult.VALIDATION_NOT_ATTEMPTED;
529    }
530    else if (preCommitValidationPassed)
531    {
532      return UniquenessValidationResult.VALIDATION_PASSED;
533    }
534    else
535    {
536      return UniquenessValidationResult.VALIDATION_FAILED;
537    }
538  }
539
540
541
542  /**
543   * Retrieves a value that indicates whether pre-commit validation was
544   * attempted, and whether that validation passed.  Note that this method is
545   * still supported and is not deprecated at this time, but the
546   * {@link #getPreCommitValidationResult()} is now the recommended way to get
547   * this information.
548   *
549   * @return  {@code Boolean.TRUE} if pre-commit validation was attempted and
550   *          passed, {@code Boolean.FALSE} if pre-commit validation was
551   *          attempted and did not pass, or {@code null} if pre-commit
552   *          validation was not attempted.
553   */
554  @Nullable()
555  public Boolean getPreCommitValidationPassed()
556  {
557    return preCommitValidationPassed;
558  }
559
560
561
562  /**
563   * Retrieves the result of the server's post-commit validation processing.
564   * The same information can be inferred from the
565   * {@link #getPostCommitValidationPassed()} method, but this method may
566   * provide a more intuitive result and does not have the possibility of a
567   * {@code null} return value.
568   *
569   * @return  {@link UniquenessValidationResult#VALIDATION_PASSED} if the
570   *          server did not find any conflicting entries during the post-commit
571   *          check, {@link UniquenessValidationResult#VALIDATION_FAILED} if
572   *          the server found at least one conflicting entry during the
573   *          post-commit check, or
574   *          {@link UniquenessValidationResult#VALIDATION_NOT_ATTEMPTED} if
575   *          the server did not attempt any post-commit validation.
576   */
577  @NotNull()
578  public UniquenessValidationResult getPostCommitValidationResult()
579  {
580    if (postCommitValidationPassed == null)
581    {
582      return UniquenessValidationResult.VALIDATION_NOT_ATTEMPTED;
583    }
584    else if (postCommitValidationPassed)
585    {
586      return UniquenessValidationResult.VALIDATION_PASSED;
587    }
588    else
589    {
590      return UniquenessValidationResult.VALIDATION_FAILED;
591    }
592  }
593
594
595
596  /**
597   * Retrieves a value that indicates whether post-commit validation was
598   * attempted, and whether that validation passed.
599   *
600   * @return  {@code Boolean.TRUE} if post-commit validation was attempted and
601   *          passed, {@code Boolean.FALSE} if post-commit validation was
602   *          attempted and did not pass, or {@code null} if post-commit
603   *          validation was not attempted.
604   */
605  @Nullable()
606  public Boolean getPostCommitValidationPassed()
607  {
608    return postCommitValidationPassed;
609  }
610
611
612
613  /**
614   * Retrieves a message with additional information about the validation
615   * processing that was performed.
616   *
617   * @return  A message with additional information about the validation
618   *          processing that was performed, or {@code null} if no validation
619   *          message is available.
620   */
621  @Nullable()
622  public String getValidationMessage()
623  {
624    return validationMessage;
625  }
626
627
628
629  /**
630   * {@inheritDoc}
631   */
632  @Override()
633  @NotNull()
634  public String getControlName()
635  {
636    return INFO_UNIQUENESS_RES_CONTROL_NAME.get();
637  }
638
639
640
641  /**
642   * Retrieves a representation of this uniqueness response control as a JSON
643   * object.  The JSON object uses the following fields:
644   * <UL>
645   *   <LI>
646   *     {@code oid} -- A mandatory string field whose value is the object
647   *     identifier for this control.  For the uniqueness response control, the
648   *     OID is "1.3.6.1.4.1.30221.2.5.53".
649   *   </LI>
650   *   <LI>
651   *     {@code control-name} -- An optional string field whose value is a
652   *     human-readable name for this control.  This field is only intended for
653   *     descriptive purposes, and when decoding a control, the {@code oid}
654   *     field should be used to identify the type of control.
655   *   </LI>
656   *   <LI>
657   *     {@code criticality} -- A mandatory Boolean field used to indicate
658   *     whether this control is considered critical.
659   *   </LI>
660   *   <LI>
661   *     {@code value-base64} -- An optional string field whose value is a
662   *     base64-encoded representation of the raw value for this uniqueness
663   *     response control.  Exactly one of the {@code value-base64} and
664   *     {@code value-json} fields must be present.
665   *   </LI>
666   *   <LI>
667   *     {@code value-json} -- An optional JSON object field whose value is a
668   *     user-friendly representation of the value for this uniqueness response
669   *     control.  Exactly one of the {@code value-base64} and
670   *     {@code value-json} fields must be present, and if the
671   *     {@code value-json} field is used, then it will use the following
672   *     fields:
673   *     <UL>
674   *       <LI>
675   *         {@code uniqueness-id} -- A string field whose value is the
676   *         uniqueness ID from the uniqueness request control with which this
677   *         response control is associated.
678   *       </LI>
679   *       <LI>
680   *         {@code pre-commit-validation-passed} -- An optional Boolean field
681   *         that indicates whether pre-commit validation passed without
682   *         identifying any conflicts.
683   *       </LI>
684   *       <LI>
685   *         {@code post-commit-validation-passed} -- An optional Boolean field
686   *         that indicates whether post-commit validation passed without
687   *         identifying any conflicts.
688   *       </LI>
689   *       <LI>
690   *         {@code validation-message} -- An optional String field whose value
691   *         is a message with additional information about the uniqueness
692   *         processing.
693   *       </LI>
694   *     </UL>
695   *   </LI>
696   * </UL>
697   *
698   * @return  A JSON object that contains a representation of this control.
699   */
700  @Override()
701  @NotNull()
702  public JSONObject toJSONControl()
703  {
704    final Map<String,JSONValue> valueFields = new LinkedHashMap<>();
705    valueFields.put(JSON_FIELD_UNIQUENESS_ID, new JSONString(uniquenessID));
706
707    if (preCommitValidationPassed != null)
708    {
709      valueFields.put(JSON_FIELD_PRE_COMMIT_VALIDATION_PASSED,
710           new JSONBoolean(preCommitValidationPassed));
711    }
712
713    if (postCommitValidationPassed != null)
714    {
715      valueFields.put(JSON_FIELD_POST_COMMIT_VALIDATION_PASSED,
716           new JSONBoolean(postCommitValidationPassed));
717    }
718
719    if (validationMessage != null)
720    {
721      valueFields.put(JSON_FIELD_VALIDATION_MESSAGE,
722           new JSONString(validationMessage));
723    }
724
725    return new JSONObject(
726         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
727              UNIQUENESS_RESPONSE_OID),
728         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
729              INFO_UNIQUENESS_RES_CONTROL_NAME.get()),
730         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
731              isCritical()),
732         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
733              new JSONObject(valueFields)));
734  }
735
736
737
738  /**
739   * Attempts to decode the provided object as a JSON representation of a
740   * uniqueness response control.
741   *
742   * @param  controlObject  The JSON object to be decoded.  It must not be
743   *                        {@code null}.
744   * @param  strict         Indicates whether to use strict mode when decoding
745   *                        the provided JSON object.  If this is {@code true},
746   *                        then this method will throw an exception if the
747   *                        provided JSON object contains any unrecognized
748   *                        fields.  If this is {@code false}, then unrecognized
749   *                        fields will be ignored.
750   *
751   * @return  The uniqueness response control that was decoded from
752   *          the provided JSON object.
753   *
754   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
755   *                         valid uniqueness response control.
756   */
757  @NotNull()
758  public static UniquenessResponseControl decodeJSONControl(
759              @NotNull final JSONObject controlObject,
760              final boolean strict)
761         throws LDAPException
762  {
763    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
764         controlObject, strict, true, true);
765
766    final ASN1OctetString rawValue = jsonControl.getRawValue();
767    if (rawValue != null)
768    {
769      return new UniquenessResponseControl(jsonControl.getOID(),
770           jsonControl.getCriticality(), rawValue);
771    }
772
773
774    final JSONObject valueObject = jsonControl.getValueObject();
775
776    final String uniquenessID =
777         valueObject.getFieldAsString(JSON_FIELD_UNIQUENESS_ID);
778    if (uniquenessID == null)
779    {
780      throw new LDAPException(ResultCode.DECODING_ERROR,
781           ERR_UNIQUENESS_RES_JSON_MISSING_UNIQUENESS_ID.get(
782                controlObject.toSingleLineString(), JSON_FIELD_UNIQUENESS_ID));
783    }
784
785    final Boolean preCommitValidationPassed =
786         valueObject.getFieldAsBoolean(JSON_FIELD_PRE_COMMIT_VALIDATION_PASSED);
787
788    final Boolean postCommitValidationPassed =
789         valueObject.getFieldAsBoolean(
790              JSON_FIELD_POST_COMMIT_VALIDATION_PASSED);
791
792    final String validationMessage =
793         valueObject.getFieldAsString(JSON_FIELD_VALIDATION_MESSAGE);
794
795
796    if (strict)
797    {
798      final List<String> unrecognizedFields =
799           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
800                valueObject, JSON_FIELD_UNIQUENESS_ID,
801                JSON_FIELD_PRE_COMMIT_VALIDATION_PASSED,
802                JSON_FIELD_POST_COMMIT_VALIDATION_PASSED,
803                JSON_FIELD_VALIDATION_MESSAGE);
804      if (! unrecognizedFields.isEmpty())
805      {
806        throw new LDAPException(ResultCode.DECODING_ERROR,
807             ERR_UNIQUENESS_RES_JSON_UNRECOGNIZED_FIELD.get(
808                  controlObject.toSingleLineString(),
809                  unrecognizedFields.get(0)));
810      }
811    }
812
813
814    return new UniquenessResponseControl(uniquenessID,
815         preCommitValidationPassed, postCommitValidationPassed,
816         validationMessage);
817  }
818
819
820
821  /**
822   * {@inheritDoc}
823   */
824  @Override()
825  public void toString(@NotNull final StringBuilder buffer)
826  {
827    buffer.append("UniquenessResponseControl(uniquenessID='");
828    buffer.append(uniquenessID);
829    buffer.append("', preCommitValidationResult='");
830    buffer.append(getPreCommitValidationResult().getName());
831    buffer.append("', preCommitValidationResult='");
832    buffer.append(getPostCommitValidationResult().getName());
833    buffer.append('\'');
834
835    if (validationMessage != null)
836    {
837      buffer.append(", validationMessage='");
838      buffer.append(validationMessage);
839      buffer.append('\'');
840    }
841    buffer.append(')');
842  }
843}