001/*
002 * Copyright 2012-2023 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2012-2023 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) 2012-2023 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.List;
041
042import com.unboundid.asn1.ASN1OctetString;
043import com.unboundid.ldap.sdk.Control;
044import com.unboundid.ldap.sdk.DecodeableControl;
045import com.unboundid.ldap.sdk.DN;
046import com.unboundid.ldap.sdk.JSONControlDecodeHelper;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.LDAPResult;
049import com.unboundid.ldap.sdk.ResultCode;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.NotNull;
052import com.unboundid.util.Nullable;
053import com.unboundid.util.ThreadSafety;
054import com.unboundid.util.ThreadSafetyLevel;
055import com.unboundid.util.Validator;
056import com.unboundid.util.json.JSONField;
057import com.unboundid.util.json.JSONObject;
058
059import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
060
061
062
063/**
064 * This class provides a response control that holds information about the
065 * soft-deleted entry that results from a soft delete request, and may also be
066 * included in a search result entry which represents a soft-deleted entry.  The
067 * value of this control will be the DN of the soft-deleted entry.
068 * <BR>
069 * <BLOCKQUOTE>
070 *   <B>NOTE:</B>  This class, and other classes within the
071 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
072 *   supported for use against Ping Identity, UnboundID, and
073 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
074 *   for proprietary functionality or for external specifications that are not
075 *   considered stable or mature enough to be guaranteed to work in an
076 *   interoperable way with other types of LDAP servers.
077 * </BLOCKQUOTE>
078 * <BR>
079 * This control has an OID of 1.3.6.1.4.1.30221.2.5.21, a criticality of false,
080 * and a value that is simply the string representation of the new DN for the
081 * soft-deleted entry.
082 * <BR><BR>
083 * See the documentation for the {@link SoftDeleteRequestControl} class for an
084 * example demonstrating the use of this control.
085 *
086 * @see  SoftDeleteRequestControl
087 */
088@NotMutable()
089@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
090public final class SoftDeleteResponseControl
091       extends Control
092       implements DecodeableControl
093{
094  /**
095   * The OID (1.3.6.1.4.1.30221.2.5.21) for the soft delete response control.
096   */
097  @NotNull public static final String SOFT_DELETE_RESPONSE_OID =
098       "1.3.6.1.4.1.30221.2.5.21";
099
100
101
102  /**
103   * The name of the field used to hold the soft-deleted entry DN in the JSON
104   * representation of this control.
105   */
106  @NotNull private static final String JSON_FIELD_SOFT_DELETED_ENTRY_DN =
107       "soft-deleted-entry-dn";
108
109
110
111  /**
112   * The serial version UID for this serializable class.
113   */
114  private static final long serialVersionUID = 3163679387266190228L;
115
116
117
118  // The DN of the soft-deleted representation of the target entry.
119  @NotNull private final String softDeletedEntryDN;
120
121
122
123  /**
124   * Creates a new empty control instance that is intended to be used only for
125   * decoding controls via the {@code DecodeableControl} interface.
126   */
127  SoftDeleteResponseControl()
128  {
129    softDeletedEntryDN = null;
130  }
131
132
133
134  /**
135   * Creates a new soft delete response control with the provided information.
136   *
137   * @param  softDeletedEntryDN  The DN of the soft-deleted representation of
138   *                             the target entry.
139   */
140  public SoftDeleteResponseControl(@NotNull final String softDeletedEntryDN)
141  {
142    super(SOFT_DELETE_RESPONSE_OID, false,
143         new ASN1OctetString(softDeletedEntryDN));
144
145    Validator.ensureNotNull(softDeletedEntryDN);
146
147    this.softDeletedEntryDN = softDeletedEntryDN;
148  }
149
150
151
152  /**
153   * Creates a new soft delete response control with the provided information.
154   *
155   * @param  oid         The OID for the control.
156   * @param  isCritical  Indicates whether the control should be considered
157   *                     critical.
158   * @param  value       The value for the control.
159   *
160   * @throws  LDAPException  If the provided information cannot be used to
161   *                         create a valid soft delete response control.
162   */
163  public SoftDeleteResponseControl(@NotNull final String oid,
164                                   final boolean isCritical,
165                                   @Nullable final ASN1OctetString value)
166         throws LDAPException
167  {
168    super(oid, isCritical, value);
169
170    if (value == null)
171    {
172      throw new LDAPException(ResultCode.DECODING_ERROR,
173           ERR_SOFT_DELETE_RESPONSE_NO_VALUE.get());
174    }
175
176    softDeletedEntryDN = value.stringValue();
177    if (! DN.isValidDN(softDeletedEntryDN))
178    {
179      throw new LDAPException(ResultCode.DECODING_ERROR,
180           ERR_SOFT_DELETE_RESPONSE_VALUE_NOT_DN.get());
181    }
182  }
183
184
185
186  /**
187   * {@inheritDoc}
188   */
189  @Override()
190  @NotNull()
191  public SoftDeleteResponseControl decodeControl(@NotNull final String oid,
192              final boolean isCritical,
193              @Nullable final ASN1OctetString value)
194         throws LDAPException
195  {
196    return new SoftDeleteResponseControl(oid, isCritical, value);
197  }
198
199
200
201  /**
202   * Retrieves the DN of the entry containing the soft-deleted representation of
203   * the target entry.
204   *
205   * @return  The DN of the entry containing the soft-deleted representation of
206   *          the target entry.
207   */
208  @NotNull()
209  public String getSoftDeletedEntryDN()
210  {
211    return softDeletedEntryDN;
212  }
213
214
215
216  /**
217   * Extracts a soft delete response control from the provided delete result.
218   *
219   * @param  deleteResult  The delete result from which to retrieve the soft
220   *                       delete response control.
221   *
222   * @return  The soft delete response control contained in the provided delete
223   *          result, or {@code null} if the result did not contain a soft
224   *          delete response control.
225   *
226   * @throws  LDAPException  If a problem is encountered while attempting to
227   *                         decode the soft delete response control contained
228   *                         in the provided result.
229   */
230  @Nullable()
231  public static SoftDeleteResponseControl get(
232                     @NotNull final LDAPResult deleteResult)
233         throws LDAPException
234  {
235    final Control c = deleteResult.getResponseControl(SOFT_DELETE_RESPONSE_OID);
236    if (c == null)
237    {
238      return null;
239    }
240
241    if (c instanceof SoftDeleteResponseControl)
242    {
243      return (SoftDeleteResponseControl) c;
244    }
245    else
246    {
247      return new SoftDeleteResponseControl(c.getOID(), c.isCritical(),
248           c.getValue());
249    }
250  }
251
252
253
254  /**
255   * {@inheritDoc}
256   */
257  @Override()
258  @NotNull()
259  public String getControlName()
260  {
261    return INFO_CONTROL_NAME_SOFT_DELETE_RESPONSE.get();
262  }
263
264
265
266  /**
267   * Retrieves a representation of this soft delete response control as a JSON
268   * object.  The JSON object uses the following fields:
269   * <UL>
270   *   <LI>
271   *     {@code oid} -- A mandatory string field whose value is the object
272   *     identifier for this control.  For the soft delete response control,
273   *     the OID is "1.3.6.1.4.1.30221.2.5.21".
274   *   </LI>
275   *   <LI>
276   *     {@code control-name} -- An optional string field whose value is a
277   *     human-readable name for this control.  This field is only intended for
278   *     descriptive purposes, and when decoding a control, the {@code oid}
279   *     field should be used to identify the type of control.
280   *   </LI>
281   *   <LI>
282   *     {@code criticality} -- A mandatory Boolean field used to indicate
283   *     whether this control is considered critical.
284   *   </LI>
285   *   <LI>
286   *     {@code value-base64} -- An optional string field whose value is a
287   *     base64-encoded representation of the raw value for this soft delete
288   *     response control.  Exactly one of the {@code value-base64} and
289   *     {@code value-json} fields must be present.
290   *   </LI>
291   *   <LI>
292   *     {@code value-json} -- An optional JSON object field whose value is a
293   *     user-friendly representation of the value for this soft delete response
294   *     control.  Exactly one of the {@code value-base64} and
295   *     {@code value-json} fields must be present, and if the
296   *     {@code value-json} field is used, then it will use the following
297   *     fields:
298   *     <UL>
299   *       <LI>
300   *         {@code soft-deleted-entry-dn} -- A string field whose value is the
301   *         DN of the soft-deleted entry that was created from the original
302   *         entry.
303   *       </LI>
304   *     </UL>
305   *   </LI>
306   * </UL>
307   *
308   * @return  A JSON object that contains a representation of this control.
309   */
310  @Override()
311  @NotNull()
312  public JSONObject toJSONControl()
313  {
314    return new JSONObject(
315         new JSONField(JSONControlDecodeHelper.JSON_FIELD_OID,
316              SOFT_DELETE_RESPONSE_OID),
317         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CONTROL_NAME,
318              INFO_CONTROL_NAME_SOFT_DELETE_RESPONSE.get()),
319         new JSONField(JSONControlDecodeHelper.JSON_FIELD_CRITICALITY,
320              isCritical()),
321         new JSONField(JSONControlDecodeHelper.JSON_FIELD_VALUE_JSON,
322              new JSONObject(
323                   new JSONField(JSON_FIELD_SOFT_DELETED_ENTRY_DN,
324                        softDeletedEntryDN))));
325  }
326
327
328
329  /**
330   * Attempts to decode the provided object as a JSON representation of a soft
331   * delete response control.
332   *
333   * @param  controlObject  The JSON object to be decoded.  It must not be
334   *                        {@code null}.
335   * @param  strict         Indicates whether to use strict mode when decoding
336   *                        the provided JSON object.  If this is {@code true},
337   *                        then this method will throw an exception if the
338   *                        provided JSON object contains any unrecognized
339   *                        fields.  If this is {@code false}, then unrecognized
340   *                        fields will be ignored.
341   *
342   * @return  The soft delete response control that was decoded from
343   *          the provided JSON object.
344   *
345   * @throws  LDAPException  If the provided JSON object cannot be parsed as a
346   *                         valid soft delete response control.
347   */
348  @NotNull()
349  public static SoftDeleteResponseControl decodeJSONControl(
350              @NotNull final JSONObject controlObject,
351              final boolean strict)
352         throws LDAPException
353  {
354    final JSONControlDecodeHelper jsonControl = new JSONControlDecodeHelper(
355         controlObject, strict, true, true);
356
357    final ASN1OctetString rawValue = jsonControl.getRawValue();
358    if (rawValue != null)
359    {
360      return new SoftDeleteResponseControl(jsonControl.getOID(),
361           jsonControl.getCriticality(), rawValue);
362    }
363
364
365    final JSONObject valueObject = jsonControl.getValueObject();
366
367    final String softDeletedEntryDN =
368         valueObject.getFieldAsString(JSON_FIELD_SOFT_DELETED_ENTRY_DN);
369    if (softDeletedEntryDN == null)
370    {
371      throw new LDAPException(ResultCode.DECODING_ERROR,
372           ERR_SOFT_DELETE_RESPONSE_JSON_MISSING_FIELD.get(
373                controlObject.toSingleLineString(),
374                JSON_FIELD_SOFT_DELETED_ENTRY_DN));
375    }
376
377
378    if (strict)
379    {
380      final List<String> unrecognizedFields =
381           JSONControlDecodeHelper.getControlObjectUnexpectedFields(
382                valueObject, JSON_FIELD_SOFT_DELETED_ENTRY_DN);
383      if (! unrecognizedFields.isEmpty())
384      {
385        throw new LDAPException(ResultCode.DECODING_ERROR,
386             ERR_SOFT_DELETE_RESPONSE_JSON_UNRECOGNIZED_FIELD.get(
387                  controlObject.toSingleLineString(),
388                  unrecognizedFields.get(0)));
389      }
390    }
391
392
393    return new SoftDeleteResponseControl(softDeletedEntryDN);
394  }
395
396
397
398  /**
399   * {@inheritDoc}
400   */
401  @Override()
402  public void toString(@NotNull final StringBuilder buffer)
403  {
404    buffer.append("SoftDeleteResponseControl(softDeletedEntryDN='");
405    buffer.append(softDeletedEntryDN);
406    buffer.append("')");
407  }
408}