001/*
002 * Copyright 2020-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-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) 2020-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.extensions;
037
038
039
040import com.unboundid.asn1.ASN1Boolean;
041import com.unboundid.asn1.ASN1Element;
042import com.unboundid.asn1.ASN1Long;
043import com.unboundid.asn1.ASN1OctetString;
044import com.unboundid.asn1.ASN1Sequence;
045import com.unboundid.ldap.sdk.Control;
046import com.unboundid.ldap.sdk.IntermediateResponse;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.util.Debug;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.NotNull;
052import com.unboundid.util.Nullable;
053import com.unboundid.util.StaticUtils;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056
057import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
058
059
060
061/**
062 * This class provides an implementation of an intermediate response that can
063 * provide the client with a portion of the support data archive generated in
064 * response to a {@link CollectSupportDataExtendedRequest}.
065 * <BR>
066 * <BLOCKQUOTE>
067 *   <B>NOTE:</B>  This class, and other classes within the
068 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
069 *   supported for use against Ping Identity, UnboundID, and
070 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
071 *   for proprietary functionality or for external specifications that are not
072 *   considered stable or mature enough to be guaranteed to work in an
073 *   interoperable way with other types of LDAP servers.
074 * </BLOCKQUOTE>
075 * <BR>
076 * The collect support data archive fragment intermediate response has an OID of
077 * 1.3.6.1.4.1.30221.2.6.66 and a value with the following encoding:
078 * <BR>
079 * <PRE>
080 *   CollectSupportDataArchiveDataIntermediateResponse ::= SEQUENCE {
081 *      archiveFileName           [0] OCTET STRING,
082 *      totalArchiveSizeBytes     [1] INTEGER,
083 *      moreDataToReturn          [2] BOOLEAN,
084 *      fragmentData              [3] OCTET STRING,
085 *      ... }
086 * </PRE>
087 *
088 * @see  CollectSupportDataExtendedRequest
089 * @see  CollectSupportDataExtendedResult
090 * @see  CollectSupportDataOutputIntermediateResponse
091 */
092@NotMutable()
093@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
094public final class CollectSupportDataArchiveFragmentIntermediateResponse
095       extends IntermediateResponse
096{
097  /**
098   * The OID (1.3.6.1.4.1.30221.2.6.66) for the collect support data archive
099   * fragment intermediate response.
100   */
101  @NotNull public static final String
102       COLLECT_SUPPORT_DATA_ARCHIVE_FRAGMENT_INTERMEDIATE_RESPONSE_OID =
103       "1.3.6.1.4.1.30221.2.6.66";
104
105
106
107  /**
108   * The BER type for the value element that holds the name (without any path
109   * information) that the server used for the support data archive.
110   */
111  private static final byte TYPE_ARCHIVE_FILE_NAME = (byte) 0x80;
112
113
114
115  /**
116   * The BER type for the value element that holds the total size of the
117   * support data archive, in bytes.
118   */
119  private static final byte TYPE_TOTAL_ARCHIVE_SIZE_BYTES = (byte) 0x81;
120
121
122
123  /**
124   * The BER type for the value element that indicates whether there is still
125   * more of the support data archive to be returned.
126   */
127  private static final byte TYPE_MORE_DATA_TO_RETURN = (byte) 0x82;
128
129
130
131  /**
132   * The BER type for the value element that holds the data for this fragment of
133   * the support data archive.
134   */
135  private static final byte TYPE_FRAGMENT_DATA = (byte) 0x83;
136
137
138  /**
139   * The serial version UID for this serializable class.
140   */
141  private static final long serialVersionUID = 6989352662115346422L;
142
143
144
145  // Indicates whether there is still more of the support data archive to be
146  // returned to the client.
147  private final boolean moreDataToReturn;
148
149  // The data that comprises this fragment of the support data archive.
150  @NotNull private final byte[] fragmentData;
151
152  // The total size of the support data archive, in bytes.
153  private final long totalArchiveSizeBytes;
154
155  // The name (without any path information) that the server used for the
156  // support data archive file.
157  @NotNull private final String archiveFileName;
158
159
160
161  /**
162   * Creates a new collect support data archive fragment intermediate response
163   * object with the provided information.
164   *
165   * @param  archiveFileName        The name (without any path information) that
166   *                                the server used for the support data archive
167   *                                file.  It must not be {@code null}.
168   * @param  totalArchiveSizeBytes  The size, in bytes, of the complete
169   *                                support data archive.
170   * @param  moreDataToReturn       Indicates whether there are more fragments
171   *                                to be returned to as part of the complete
172   *                                support data archive.
173   * @param  fragmentData           The data contained in this fragment of the
174   *                                support data archive.  It must not be
175   *                                {@code null}.
176   * @param  controls               The set of controls to include in this
177   *                                intermediate response.  It may be
178   *                                {@code null} or empty if no controls should
179   *                                be included.
180   */
181  public CollectSupportDataArchiveFragmentIntermediateResponse(
182              @NotNull final String archiveFileName,
183              final long totalArchiveSizeBytes,
184              final boolean moreDataToReturn,
185              @NotNull final byte[] fragmentData,
186              @Nullable final Control... controls)
187  {
188    super(COLLECT_SUPPORT_DATA_ARCHIVE_FRAGMENT_INTERMEDIATE_RESPONSE_OID,
189         encodeValue(archiveFileName, totalArchiveSizeBytes, moreDataToReturn,
190              fragmentData),
191         controls);
192
193    this.archiveFileName = archiveFileName;
194    this.totalArchiveSizeBytes = totalArchiveSizeBytes;
195    this.moreDataToReturn = moreDataToReturn;
196    this.fragmentData = fragmentData;
197  }
198
199
200
201  /**
202   * Constructs an ASN.1 octet string suitable for use as the value of this
203   * collect support data archive fragment intermediate response.
204   *
205   * @param  archiveFileName        The name (without any path information) that
206   *                                the server used for the support data archive
207   *                                file.  It must not be {@code null}.
208   * @param  totalArchiveSizeBytes  The size, in bytes, of the complete
209   *                                support data archive.
210   * @param  moreDataToReturn       Indicates whether there are more fragments
211   *                                to be returned to as part of the complete
212   *                                support data archive.
213   * @param  fragmentData           The data contained in this fragment of the
214   *                                support data archive.  It must not be
215   *                                {@code null}.
216   *
217   * @return  The ASN.1 octet string containing the encoded value.
218   */
219  @NotNull()
220  private static ASN1OctetString encodeValue(
221               @NotNull final String archiveFileName,
222               final long totalArchiveSizeBytes,
223               final boolean moreDataToReturn,
224               @NotNull final byte[] fragmentData)
225  {
226    final ASN1Sequence valueSequence = new ASN1Sequence(
227         new ASN1OctetString(TYPE_ARCHIVE_FILE_NAME, archiveFileName),
228         new ASN1Long(TYPE_TOTAL_ARCHIVE_SIZE_BYTES, totalArchiveSizeBytes),
229         new ASN1Boolean(TYPE_MORE_DATA_TO_RETURN, moreDataToReturn),
230         new ASN1OctetString(TYPE_FRAGMENT_DATA, fragmentData));
231    return new ASN1OctetString(valueSequence.encode());
232  }
233
234
235
236  /**
237   * Creates a new collect support data archive fragment intermediate response
238   * that is decoded from the provided generic intermediate response.
239   *
240   * @param  intermediateResponse  The generic intermediate response to be
241   *                               decoded as a collect support data archive
242   *                               fragment intermediate response.  It must not
243   *                               be {@code null}.
244   *
245   * @throws  LDAPException  If the provided intermediate response object cannot
246   *                         be decoded as a collect support data archive
247   *                         fragment intermediate response.
248   */
249  public CollectSupportDataArchiveFragmentIntermediateResponse(
250              @NotNull final IntermediateResponse intermediateResponse)
251         throws LDAPException
252  {
253    super(intermediateResponse);
254
255    final ASN1OctetString value = intermediateResponse.getValue();
256    if (value == null)
257    {
258      throw new LDAPException(ResultCode.DECODING_ERROR,
259           ERR_CSD_FRAGMENT_IR_DECODE_NO_VALUE.get());
260    }
261
262    try
263    {
264      final ASN1Sequence valueSequence =
265           ASN1Sequence.decodeAsSequence(value.getValue());
266      final ASN1Element[] elements = valueSequence.elements();
267      archiveFileName =
268           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
269      totalArchiveSizeBytes = ASN1Long.decodeAsLong(elements[1]).longValue();
270      moreDataToReturn =
271           ASN1Boolean.decodeAsBoolean(elements[2]).booleanValue();
272      fragmentData =
273           ASN1OctetString.decodeAsOctetString(elements[3]).getValue();
274    }
275    catch (final Exception e)
276    {
277      Debug.debugException(e);
278      throw new LDAPException(ResultCode.DECODING_ERROR,
279           ERR_CSD_FRAGMENT_IR_DECODE_ERROR.get(
280                StaticUtils.getExceptionMessage(e)),
281           e);
282    }
283  }
284
285
286
287  /**
288   * Retrieves the name (without any path information) that the server used for
289   * the support data archive file.
290   *
291   * @return  The name (without any path information) that the server used for
292   *          the support data archive file.
293   */
294  @NotNull()
295  public String getArchiveFileName()
296  {
297    return archiveFileName;
298  }
299
300
301
302  /**
303   * Retrieves the total number of bytes contained in the complete support data
304   * archive.
305   *
306   * @return  The total number of bytes contained in the complete support data
307   *          archive.
308   */
309  public long getTotalArchiveSizeBytes()
310  {
311    return totalArchiveSizeBytes;
312  }
313
314
315
316  /**
317   * Indicates whether there are one or more fragments still to be returned
318   * in the complete support data archive.
319   *
320   * @return  {@code true} if there are still more fragments to be returned, or
321   *          {@code false} if not.
322   */
323  public boolean moreDataToReturn()
324  {
325    return moreDataToReturn;
326  }
327
328
329
330  /**
331   * Retrieves the data included in this fragment.
332   *
333   * @return  The data included in this fragment.
334   */
335  @NotNull()
336  public byte[] getFragmentData()
337  {
338    return fragmentData;
339  }
340
341
342
343  /**
344   * {@inheritDoc}
345   */
346  @Override()
347  @NotNull()
348  public String getIntermediateResponseName()
349  {
350    return INFO_COLLECT_SUPPORT_DATA_FRAGMENT_IR_NAME.get();
351  }
352
353
354
355  /**
356   * {@inheritDoc}
357   */
358  @Override()
359  @NotNull()
360  public String valueToString()
361  {
362    final StringBuilder buffer = new StringBuilder();
363
364    buffer.append("archiveFileName='");
365    buffer.append(archiveFileName);
366    buffer.append("' totalArchiveSizeBytes=");
367    buffer.append(totalArchiveSizeBytes);
368    buffer.append(" moreDataToReturn=");
369    buffer.append(moreDataToReturn);
370    buffer.append(" fragmentSizeBytes=");
371    buffer.append(fragmentData.length);
372
373    return buffer.toString();
374  }
375
376
377
378  /**
379   * {@inheritDoc}
380   */
381  @Override()
382  public void toString(@NotNull final StringBuilder buffer)
383  {
384    buffer.append(
385         "CollectSupportDataArchiveFragmentIntermediateResponse(oid='");
386    buffer.append(getOID());
387    buffer.append("', archiveFileName='");
388    buffer.append(archiveFileName);
389    buffer.append("', totalArchiveSizeBytes=");
390    buffer.append(totalArchiveSizeBytes);
391    buffer.append(", moreDataToReturn=");
392    buffer.append(moreDataToReturn);
393    buffer.append(", fragmentSizeBytes=");
394    buffer.append(fragmentData.length);
395
396    final Control[] controls = getControls();
397    if (controls.length > 0)
398    {
399      buffer.append(", controls={");
400      for (int i=0; i < controls.length; i++)
401      {
402        if (i > 0)
403        {
404          buffer.append(", ");
405        }
406
407        buffer.append(controls[i]);
408      }
409      buffer.append('}');
410    }
411
412    buffer.append(')');
413  }
414}