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