001/*
002 * Copyright 2020-2026 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2020-2026 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-2026 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 java.util.ArrayList;
041import java.util.List;
042
043import com.unboundid.asn1.ASN1Boolean;
044import com.unboundid.asn1.ASN1Element;
045import com.unboundid.asn1.ASN1Enumerated;
046import com.unboundid.asn1.ASN1Integer;
047import com.unboundid.asn1.ASN1OctetString;
048import com.unboundid.asn1.ASN1Sequence;
049import com.unboundid.ldap.sdk.Control;
050import com.unboundid.ldap.sdk.ExtendedRequest;
051import com.unboundid.ldap.sdk.ExtendedResult;
052import com.unboundid.ldap.sdk.IntermediateResponse;
053import com.unboundid.ldap.sdk.IntermediateResponseListener;
054import com.unboundid.ldap.sdk.LDAPException;
055import com.unboundid.ldap.sdk.LDAPConnection;
056import com.unboundid.ldap.sdk.LDAPRuntimeException;
057import com.unboundid.ldap.sdk.ResultCode;
058import com.unboundid.ldap.sdk.unboundidds.tasks.CollectSupportDataSecurityLevel;
059import com.unboundid.util.Debug;
060import com.unboundid.util.NotMutable;
061import com.unboundid.util.NotNull;
062import com.unboundid.util.Nullable;
063import com.unboundid.util.StaticUtils;
064import com.unboundid.util.ThreadSafety;
065import com.unboundid.util.ThreadSafetyLevel;
066import com.unboundid.util.Validator;
067
068import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
069
070
071
072/**
073 * This class provides an implementation of an extended request that may be used
074 * to invoke the collect-support data tool in a Ping Identity Directory Server
075 * and stream the output (using
076 * {@link CollectSupportDataOutputIntermediateResponse} messages) and the
077 * resulting support data archive (using
078 * {@link CollectSupportDataArchiveFragmentIntermediateResponse} messages)
079 * back to the client before the final
080 * {@link CollectSupportDataExtendedResult} response.
081 * <BR>
082 * <BLOCKQUOTE>
083 *   <B>NOTE:</B>  This class, and other classes within the
084 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
085 *   supported for use against Ping Identity, UnboundID, and
086 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
087 *   for proprietary functionality or for external specifications that are not
088 *   considered stable or mature enough to be guaranteed to work in an
089 *   interoperable way with other types of LDAP servers.
090 * </BLOCKQUOTE>
091 * <BR>
092 * The collect support data extended request has an OID of
093 * 1.3.6.1.4.1.30221.2.6.64 and a value with the following encoding:
094 * <BR>
095 * <PRE>
096 *   CollectSupportDataRequest ::= SEQUENCE {
097 *      archiveFileName                 [0]  OCTET STRING OPTIONAL,
098 *      encryptionPassphrase            [1]  OCTET STRING OPTIONAL,
099 *      includeExpensiveData            [2]  BOOLEAN DEFAULT FALSE,
100 *      includeReplicationStateDump     [3]  BOOLEAN DEFAULT FALSE,
101 *      includeBinaryFiles              [4]  BOOLEAN DEFAULT FALSE,
102 *      includeExtensionSource          [5]  BOOLEAN DEFAULT FALSE,
103 *      useSequentialMode               [6]  BOOLEAN DEFAULT FALSE,
104 *      securityLevel                   [7]  ENUMERATED {
105 *           none                            (0),
106 *           obscureSecrets                  (1),
107 *           maximum                         (2),
108 *           ... } DEFAULT obscureSecrets,
109 *      jstackCount                     [8]  INTEGER (0..MAX) DEFAULT 10,
110 *      reportCount                     [9]  INTEGER (0..MAX) DEFAULT 10,
111 *      reportIntervalSeconds           [10] INTEGER (1..MAX) DEFAULT 1,
112 *      logCaptureWindow                [11] CHOICE {
113 *           toolDefault                     [0] NULL,
114 *           durationMillis                  [1] INTEGER (0..MAX),
115 *           timeWindow                      [2] SEQUENCE {
116 *                startTime                       OCTET STRING,
117 *                endTime                         OCTET STRING OPTIONAL },
118 *           headAndTailSize                 [3] SEQUENCE {
119 *                headSizeKB                      [0] INTEGER OPTIONAL,
120 *                tailSizeKB                      [1] INTEGER OPTIONAL },
121 *           ... } DEFAULT default,
122 *      comment                         [12] OCTET STRING OPTIONAL,
123 *      proxyToServer                   [13] SEQUENCE OF {
124 *           address                         OCTET STRING,
125 *           port                            INTEGER (1..65535),
126 *           ... } OPTIONAL,
127 *      maximumFragmentSizeBytes        [1] INTEGER DEFAULT 1048576,
128 *      noJFR                           [15] BOOLEAN DEFAULT FALSE,
129 *      ... }
130 * </PRE>
131 * <BR><BR>
132 * Because the tool output and the support data archive will be streamed back to
133 * the client using intermediate response messages, the request must be
134 * configured with an intermediate response listener to gain access to that
135 * information.
136 *
137 * @see  CollectSupportDataExtendedResult
138 * @see  CollectSupportDataOutputIntermediateResponse
139 * @see  CollectSupportDataArchiveFragmentIntermediateResponse
140 * @see  CollectSupportDataSecurityLevel
141 * @see  CollectSupportDataLogCaptureWindow
142 */
143@NotMutable()
144@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
145public final class CollectSupportDataExtendedRequest
146       extends ExtendedRequest
147       implements IntermediateResponseListener
148{
149  /**
150   * The OID (1.3.6.1.4.1.30221.2.6.64) for the collect support data extended
151   * request.
152   */
153  @NotNull public static final String COLLECT_SUPPORT_DATA_REQUEST_OID =
154       "1.3.6.1.4.1.30221.2.6.64";
155
156
157
158  /**
159   * The BER type for the request element that specifies the name to use for the
160   * archive file.
161   */
162  private static final byte TYPE_ARCHIVE_FILE_NAME = (byte) 0x80;
163
164
165
166  /**
167   * The BER type for the request element that specifies the passphrase to use
168   * to encrypt the contents of the support data archive.
169   */
170  static final byte TYPE_ENCRYPTION_PASSPHRASE = (byte) 0x81;
171
172
173
174  /**
175   * The BER type for the request element that indicates whether to include
176   * data that may be expensive to collect.
177   */
178  private static final byte TYPE_INCLUDE_EXPENSIVE_DATA = (byte) 0x82;
179
180
181
182  /**
183   * The BER type for the request element that indicates whether to include a
184   * replication state dump.
185   */
186  private static final byte TYPE_INCLUDE_REPLICATION_STATE_DUMP = (byte) 0x83;
187
188
189
190  /**
191   * The BER type for the request element that indicates whether to include
192   * binary files.
193   */
194  private static final byte TYPE_INCLUDE_BINARY_FILES = (byte) 0x84;
195
196
197
198  /**
199   * The BER type for the request element that indicates whether to include
200   * extension source code.
201   */
202  private static final byte TYPE_INCLUDE_EXTENSION_SOURCE = (byte) 0x85;
203
204
205
206  /**
207   * The BER type for the request element that indicates whether to collect
208   * information in sequential mode.
209   */
210  private static final byte TYPE_USE_SEQUENTIAL_MODE = (byte) 0x86;
211
212
213
214  /**
215   * The BER type for the request element that specifies the security level.
216   */
217  private static final byte TYPE_SECURITY_LEVEL = (byte) 0x87;
218
219
220
221  /**
222   * The BER type for the request element that specifies the number of jstack
223   * stack traces to include.
224   */
225  private static final byte TYPE_JSTACK_COUNT = (byte) 0x88;
226
227
228
229  /**
230   * The BER type for the request element that specifies the number intervals
231   * to collect from interval-based sampling tools.
232   */
233  private static final byte TYPE_REPORT_COUNT = (byte) 0x89;
234
235
236
237  /**
238   * The BER type for the request element that specifies the interval duration
239   * to use for interval-based sampling tools.
240   */
241  private static final byte TYPE_REPORT_INTERVAL_SECONDS = (byte) 0x8A;
242
243
244
245  /**
246   * The BER type for the request element that specifies the log capture window
247   * for the request.
248   */
249  private static final byte TYPE_LOG_CAPTURE_WINDOW = (byte) 0xAB;
250
251
252
253  /**
254   * The BER type for the request element that specifies a comment to include in
255   * the archive.
256   */
257  private static final byte TYPE_COMMENT = (byte) 0x8C;
258
259
260
261  /**
262   * The BER type for the request element that specifies the address and port
263   * to which the request should be forwarded.
264   */
265  private static final byte TYPE_PROXY_TO_SERVER = (byte) 0xAD;
266
267
268
269  /**
270   * The BER type for the request element that specifies the maximum archive
271   * fragment size.
272   */
273  private static final byte TYPE_MAXIMUM_FRAGMENT_SIZE_BYTES = (byte) 0x8E;
274
275
276
277  /**
278   * The BER type for the request element that indicates whether to skip the
279   * collection of Java Flight Recorder data.
280   */
281  private static final byte TYPE_NO_JFR = (byte) 0x8F;
282
283
284
285  /**
286   * The integer value for the {@link CollectSupportDataSecurityLevel#NONE}
287   * security level.
288   */
289  private static final int SECURITY_LEVEL_VALUE_NONE = 0;
290
291
292
293  /**
294   * The integer value for the
295   * {@link CollectSupportDataSecurityLevel#OBSCURE_SECRETS} security level.
296   */
297  private static final int SECURITY_LEVEL_VALUE_OBSCURE_SECRETS = 1;
298
299
300
301  /**
302   * The integer value for the {@link CollectSupportDataSecurityLevel#MAXIMUM}
303   * security level.
304   */
305  private static final int SECURITY_LEVEL_VALUE_MAXIMUM = 2;
306
307
308
309  /**
310   * The serial version UID for this serializable class.
311   */
312  private static final long serialVersionUID = -8884596371195896085L;
313
314
315
316  // The passphrase to use to encrypt the contents of the support data archive.
317  @Nullable private final ASN1OctetString encryptionPassphrase;
318
319  // Indicates whether to include binary files in the support data archive.
320  @Nullable private final Boolean includeBinaryFiles;
321
322  // Indicates whether to include expensive data in the support data archive.
323  @Nullable private final Boolean includeExpensiveData;
324
325  // Indicates whether to include third-party extension source code in the
326  // support data archive.
327  @Nullable private final Boolean includeExtensionSource;
328
329  // Indicates whether to include a replication state dump in the support data
330  // archive.
331  @Nullable private final Boolean includeReplicationStateDump;
332
333  // Indicates whether to skip the collection of Java Flight Recorder data.
334  @Nullable private final Boolean noJFR;
335
336  // Indicates whether to capture information sequentially rather than in
337  // parallel.
338  @Nullable private final Boolean useSequentialMode;
339
340  // The intermediate response listener that will be used for this operation.
341  @NotNull private final CollectSupportDataIntermediateResponseListener
342       intermediateResponseListener;
343
344  // The log capture window that indicates how much log content to include in
345  // the support data archive.
346  @Nullable private final CollectSupportDataLogCaptureWindow logCaptureWindow;
347
348  // The security level to use for data included in the support data archive.
349  @Nullable private final CollectSupportDataSecurityLevel securityLevel;
350
351  // The number of jstacks to include in the support data archive.
352  @Nullable private final Integer jstackCount;
353
354  // The maximum size, in bytes, of any support data archive fragment to include
355  // in a collect support data archive fragment intermediate response.
356  @Nullable private final Integer maximumFragmentSizeBytes;
357
358  // The port of a backend Directory Server instance to which the collect
359  // support data extended request should be forwarded.
360  @Nullable private final Integer proxyToServerPort;
361
362  // The report count to use for sampled metrics.
363  @Nullable private final Integer reportCount;
364
365  // The report interval in seconds to use for sampled metrics.
366  @Nullable private final Integer reportIntervalSeconds;
367
368  // The name (without any path information) the client intends to use for the
369  // support data archive file.
370  @Nullable private final String archiveFileName;
371
372  // A comment to include in the support data archive.
373  @Nullable private final String comment;
374
375  // The address of a backend Directory Server to which the collect support data
376  // extended request should be forwarded.
377  @Nullable private final String proxyToServerAddress;
378
379
380
381  /**
382   * Creates a new instance of this extended request with the provided
383   * information.
384   *
385   * @param  properties                    The properties that should be used
386   *                                       for the collect support data extended
387   *                                       request.  It must not be
388   *                                       {@code null}.
389   * @param  intermediateResponseListener  The listener that will be used to
390   *                                       handle any intermediate response
391   *                                       messages that are received in the
392   *                                       course of processing the collect
393   *                                       support data extended request.  It
394   *                                       must not be {@code null}.
395   * @param  controls                      The controls to include in the
396   *                                       collect support data extended
397   *                                       request.  It may be {@code null} or
398   *                                       empty if no controls are needed.
399   */
400  public CollectSupportDataExtendedRequest(
401       @NotNull final CollectSupportDataExtendedRequestProperties properties,
402       @NotNull final CollectSupportDataIntermediateResponseListener
403            intermediateResponseListener,
404       @Nullable final Control... controls)
405  {
406    super(COLLECT_SUPPORT_DATA_REQUEST_OID, encodeValue(properties), controls);
407
408    Validator.ensureNotNullWithMessage(intermediateResponseListener,
409         "CollectSupportDataExtendedRequest.intermediateResponseListener " +
410              "must not be null.");
411    this.intermediateResponseListener = intermediateResponseListener;
412
413    archiveFileName = properties.getArchiveFileName();
414    encryptionPassphrase = properties.getEncryptionPassphrase();
415    includeBinaryFiles = properties.getIncludeBinaryFiles();
416    includeExpensiveData = properties.getIncludeExpensiveData();
417    includeExtensionSource = properties.getIncludeExtensionSource();
418    includeReplicationStateDump = properties.getIncludeReplicationStateDump();
419    noJFR = properties.getNoJFR();
420    useSequentialMode = properties.getUseSequentialMode();
421    logCaptureWindow = properties.getLogCaptureWindow();
422    securityLevel = properties.getSecurityLevel();
423    jstackCount = properties.getJStackCount();
424    reportCount = properties.getReportCount();
425    reportIntervalSeconds = properties.getReportIntervalSeconds();
426    maximumFragmentSizeBytes = properties.getMaximumFragmentSizeBytes();
427    proxyToServerPort = properties.getProxyToServerPort();
428    comment = properties.getComment();
429    proxyToServerAddress = properties.getProxyToServerAddress();
430
431    setIntermediateResponseListener(this);
432  }
433
434
435
436  /**
437   * Constructs an ASN.1 octet string suitable for use as the value of this
438   * collect support data extended request from the given set of properties.
439   *
440   * @param  properties  The properties that should be used to construct the
441   *                     extended request value.  It must not be {@code null}.
442   *
443   * @return  the ASN.1 octet string that was created.
444   */
445  @NotNull()
446  private static ASN1OctetString encodeValue(
447       @NotNull final CollectSupportDataExtendedRequestProperties properties)
448  {
449    final List<ASN1Element> elements = new ArrayList<>(20);
450
451    final String archiveFileName = properties.getArchiveFileName();
452    if (archiveFileName != null)
453    {
454      elements.add(new ASN1OctetString(TYPE_ARCHIVE_FILE_NAME,
455           archiveFileName));
456    }
457
458    final ASN1OctetString encryptionPassphrase =
459         properties.getEncryptionPassphrase();
460    if (encryptionPassphrase != null)
461    {
462      elements.add(encryptionPassphrase);
463    }
464
465    final Boolean includeExpensiveData = properties.getIncludeExpensiveData();
466    if (includeExpensiveData != null)
467    {
468      elements.add(new ASN1Boolean(TYPE_INCLUDE_EXPENSIVE_DATA,
469           includeExpensiveData));
470    }
471
472    final Boolean includeReplicationStateDump =
473         properties.getIncludeReplicationStateDump();
474    if (includeReplicationStateDump != null)
475    {
476      elements.add(new ASN1Boolean(TYPE_INCLUDE_REPLICATION_STATE_DUMP,
477           includeReplicationStateDump));
478    }
479
480    final Boolean includeBinaryFiles = properties.getIncludeBinaryFiles();
481    if (includeBinaryFiles != null)
482    {
483      elements.add(new ASN1Boolean(TYPE_INCLUDE_BINARY_FILES,
484           includeBinaryFiles));
485    }
486
487    final Boolean includeExtensionSource =
488         properties.getIncludeExtensionSource();
489    if (includeExtensionSource != null)
490    {
491      elements.add(new ASN1Boolean(TYPE_INCLUDE_EXTENSION_SOURCE,
492           includeExtensionSource));
493    }
494
495    final Boolean useSequentialMode = properties.getUseSequentialMode();
496    if (useSequentialMode != null)
497    {
498      elements.add(new ASN1Boolean(TYPE_USE_SEQUENTIAL_MODE,
499           useSequentialMode));
500    }
501
502    final CollectSupportDataSecurityLevel securityLevel =
503         properties.getSecurityLevel();
504    if (securityLevel != null)
505    {
506      final int securityLevelIntValue;
507      switch (securityLevel)
508      {
509        case NONE:
510          securityLevelIntValue = SECURITY_LEVEL_VALUE_NONE;
511          break;
512        case OBSCURE_SECRETS:
513          securityLevelIntValue = SECURITY_LEVEL_VALUE_OBSCURE_SECRETS;
514          break;
515        case MAXIMUM:
516          securityLevelIntValue = SECURITY_LEVEL_VALUE_MAXIMUM;
517          break;
518        default:
519          throw new LDAPRuntimeException(new LDAPException(
520               ResultCode.LOCAL_ERROR,
521               ERR_CSD_REQUEST_UNSUPPORTED_SECURITY_LEVEL.get(
522                    securityLevel.getName())));
523      }
524
525      elements.add(new ASN1Enumerated(TYPE_SECURITY_LEVEL,
526           securityLevelIntValue));
527    }
528
529    final Integer jstackCount = properties.getJStackCount();
530    if (jstackCount != null)
531    {
532      elements.add(new ASN1Integer(TYPE_JSTACK_COUNT, jstackCount));
533    }
534
535    final Integer reportCount = properties.getReportCount();
536    if (reportCount != null)
537    {
538      elements.add(new ASN1Integer(TYPE_REPORT_COUNT, reportCount));
539    }
540
541    final Integer reportIntervalSeconds =
542         properties.getReportIntervalSeconds();
543    if (reportIntervalSeconds != null)
544    {
545      elements.add(new ASN1Integer(TYPE_REPORT_INTERVAL_SECONDS,
546           reportIntervalSeconds));
547    }
548
549    final CollectSupportDataLogCaptureWindow logCaptureWindow =
550         properties.getLogCaptureWindow();
551    if (logCaptureWindow != null)
552    {
553      elements.add(new ASN1Element(TYPE_LOG_CAPTURE_WINDOW,
554           logCaptureWindow.encode().encode()));
555    }
556
557    final String comment = properties.getComment();
558    if (comment != null)
559    {
560      elements.add(new ASN1OctetString(TYPE_COMMENT, comment));
561    }
562
563    final String proxyToServerAddress = properties.getProxyToServerAddress();
564    if (proxyToServerAddress != null)
565    {
566      elements.add(new ASN1Sequence(TYPE_PROXY_TO_SERVER,
567           new ASN1OctetString(proxyToServerAddress),
568           new ASN1Integer(properties.getProxyToServerPort())));
569    }
570
571    final Integer maximumFragmentSizeBytes =
572         properties.getMaximumFragmentSizeBytes();
573    if (maximumFragmentSizeBytes != null)
574    {
575      elements.add(new ASN1Integer(TYPE_MAXIMUM_FRAGMENT_SIZE_BYTES,
576           maximumFragmentSizeBytes));
577    }
578
579    final Boolean noJFR = properties.getNoJFR();
580    if (noJFR != null)
581    {
582      elements.add(new ASN1Boolean(TYPE_NO_JFR, noJFR));
583    }
584
585    return new ASN1OctetString(new ASN1Sequence(elements).encode());
586  }
587
588
589
590  /**
591   * Creates a new collect support data extended request that is decoded from
592   * the provided generic extended request.
593   *
594   * @param  request                       The generic extended request to be
595   *                                       decoded as a collect support data
596   *                                       extended request.  It must not be
597   *                                       {@code null}.
598   * @param  intermediateResponseListener  The listener that will be used to
599   *                                       handle any intermediate response
600   *                                       messages that are received in the
601   *                                       course of processing the collect
602   *                                       support data extended request.  It
603   *                                       must not be {@code null}.
604   *
605   * @throws  LDAPException  If the provided extended request cannot be decoded
606   *                         as a valid collect support data extended request.
607   */
608  public CollectSupportDataExtendedRequest(
609              @NotNull final ExtendedRequest request,
610              @NotNull final CollectSupportDataIntermediateResponseListener
611                   intermediateResponseListener)
612         throws LDAPException
613  {
614    super(request);
615
616    Validator.ensureNotNullWithMessage(intermediateResponseListener,
617         "CollectSupportDataExtendedRequest.intermediateResponseListener " +
618              "must not be null.");
619    this.intermediateResponseListener = intermediateResponseListener;
620
621    final ASN1OctetString value = request.getValue();
622    if (value == null)
623    {
624      throw new LDAPException(ResultCode.DECODING_ERROR,
625           ERR_CSD_REQUEST_DECODE_NO_VALUE.get());
626    }
627
628    try
629    {
630      ASN1OctetString encPassphrase = null;
631      Boolean includeExpensive = null;
632      Boolean includeReplication = null;
633      Boolean includeBinary = null;
634      Boolean includeSource = null;
635      Boolean sequentialMode = null;
636      Boolean skipJFR = null;
637      CollectSupportDataSecurityLevel secLevel = null;
638      Integer jCount = null;
639      Integer rCount = null;
640      Integer rInterval = null;
641      CollectSupportDataLogCaptureWindow lcw = null;
642      String archiveName = null;
643      String commentStr = null;
644      String proxyToAddress = null;
645      Integer proxyToPort = null;
646      Integer maxFragmentSize = null;
647
648      final ASN1Sequence valueSequence =
649           ASN1Sequence.decodeAsSequence(value.getValue());
650      final ASN1Element[] elements = valueSequence.elements();
651      for (final ASN1Element e : elements)
652      {
653        switch (e.getType())
654        {
655          case TYPE_ARCHIVE_FILE_NAME:
656            archiveName = ASN1OctetString.decodeAsOctetString(e).stringValue();
657            break;
658          case TYPE_ENCRYPTION_PASSPHRASE:
659            encPassphrase = ASN1OctetString.decodeAsOctetString(e);
660            break;
661          case TYPE_INCLUDE_EXPENSIVE_DATA:
662            includeExpensive = ASN1Boolean.decodeAsBoolean(e).booleanValue();
663            break;
664          case TYPE_INCLUDE_REPLICATION_STATE_DUMP:
665            includeReplication = ASN1Boolean.decodeAsBoolean(e).booleanValue();
666            break;
667          case TYPE_INCLUDE_BINARY_FILES:
668            includeBinary = ASN1Boolean.decodeAsBoolean(e).booleanValue();
669            break;
670          case TYPE_INCLUDE_EXTENSION_SOURCE:
671            includeSource  = ASN1Boolean.decodeAsBoolean(e).booleanValue();
672            break;
673          case TYPE_USE_SEQUENTIAL_MODE:
674            sequentialMode  = ASN1Boolean.decodeAsBoolean(e).booleanValue();
675            break;
676          case TYPE_SECURITY_LEVEL:
677            final int secLevelIntValue =
678                 ASN1Enumerated.decodeAsEnumerated(e).intValue();
679            switch (secLevelIntValue)
680            {
681              case SECURITY_LEVEL_VALUE_NONE:
682                secLevel = CollectSupportDataSecurityLevel.NONE;
683                break;
684              case SECURITY_LEVEL_VALUE_OBSCURE_SECRETS:
685                secLevel = CollectSupportDataSecurityLevel.OBSCURE_SECRETS;
686                break;
687              case SECURITY_LEVEL_VALUE_MAXIMUM:
688                secLevel = CollectSupportDataSecurityLevel.MAXIMUM;
689                break;
690              default:
691                throw new LDAPException(ResultCode.DECODING_ERROR,
692                     ERR_CSD_REQUEST_DECODE_UNSUPPORTED_SECURITY_LEVEL.get(
693                          secLevelIntValue));
694            }
695            break;
696          case TYPE_JSTACK_COUNT:
697            jCount = ASN1Integer.decodeAsInteger(e).intValue();
698            break;
699          case TYPE_REPORT_COUNT:
700            rCount = ASN1Integer.decodeAsInteger(e).intValue();
701            break;
702          case TYPE_REPORT_INTERVAL_SECONDS:
703            rInterval = ASN1Integer.decodeAsInteger(e).intValue();
704            break;
705          case TYPE_LOG_CAPTURE_WINDOW:
706            final ASN1Element lcwElement = ASN1Element.decode(e.getValue());
707            try
708            {
709              lcw = CollectSupportDataLogCaptureWindow.decode(lcwElement);
710            }
711            catch (final Exception ex)
712            {
713              Debug.debugException(ex);
714              throw new LDAPException(ResultCode.DECODING_ERROR,
715                   ERR_CSD_REQUEST_DECODE_LCW_FAILED.get(
716                        StaticUtils.getExceptionMessage(ex)),
717                   ex);
718            }
719            break;
720          case TYPE_COMMENT:
721            commentStr = ASN1OctetString.decodeAsOctetString(e).stringValue();
722            break;
723          case TYPE_PROXY_TO_SERVER:
724            final ASN1Element[] proxyToElements =
725                 ASN1Sequence.decodeAsSequence(e).elements();
726            proxyToAddress = ASN1OctetString.decodeAsOctetString(
727                 proxyToElements[0]).stringValue();
728            proxyToPort = ASN1Integer.decodeAsInteger(
729                 proxyToElements[1]).intValue();
730            break;
731          case TYPE_MAXIMUM_FRAGMENT_SIZE_BYTES:
732            maxFragmentSize = ASN1Integer.decodeAsInteger(e).intValue();
733            break;
734          case TYPE_NO_JFR:
735            skipJFR = ASN1Boolean.decodeAsBoolean(e).booleanValue();
736            break;
737        }
738      }
739
740      archiveFileName = archiveName;
741      encryptionPassphrase = encPassphrase;
742      includeExpensiveData = includeExpensive;
743      includeReplicationStateDump = includeReplication;
744      includeBinaryFiles = includeBinary;
745      includeExtensionSource = includeSource;
746      useSequentialMode = sequentialMode;
747      noJFR = skipJFR;
748      securityLevel = secLevel;
749      jstackCount = jCount;
750      reportCount = rCount;
751      reportIntervalSeconds = rInterval;
752      logCaptureWindow = lcw;
753      comment = commentStr;
754      proxyToServerAddress = proxyToAddress;
755      proxyToServerPort = proxyToPort;
756      maximumFragmentSizeBytes = maxFragmentSize;
757    }
758    catch (final LDAPException e)
759    {
760      Debug.debugException(e);
761      throw e;
762    }
763    catch (final Exception e)
764    {
765      Debug.debugException(e);
766      throw new LDAPException(ResultCode.DECODING_ERROR,
767           ERR_CSD_REQUEST_DECODE_ERROR.get(
768                StaticUtils.getExceptionMessage(e)),
769           e);
770    }
771  }
772
773
774
775  /**
776   * Retrieves the listener that will be notified when any output, archive
777   * fragment, or other types of intermediate response messages are received
778   * in response to this extended request.
779   *
780   * @return  The listener that will be notified when any output, archive
781   *          fragment, or other types of intermediate response messages are
782   *          in response to this extended request.
783   */
784  @NotNull()
785  public CollectSupportDataIntermediateResponseListener
786              getCollectSupportDataIntermediateResponseListener()
787  {
788    return intermediateResponseListener;
789  }
790
791
792
793  /**
794   * Retrieves the name (without any path information) that the client intends
795   * to use for the support data archive file.
796   *
797   * @return  The name (without any path information) that the client intends to
798   *          use for the support data archive file, or {@code null} if the
799   *          server should generate an archive file name.
800   */
801  @Nullable()
802  public String getArchiveFileName()
803  {
804    return archiveFileName;
805  }
806
807
808
809  /**
810   * Retrieves the passphrase that should be used to encrypt the contents of the
811   * support data archive.
812   *
813   * @return  The passphrase that should be used to encrypt the contents of the
814   *          support data archive, or {@code null} if the archive should not
815   *          be encrypted.
816   */
817  @Nullable()
818  public ASN1OctetString getEncryptionPassphrase()
819  {
820    return encryptionPassphrase;
821  }
822
823
824
825  /**
826   * Retrieves the value of a flag that indicates whether the support data
827   * archive may include data that is potentially expensive to collect and
828   * could affect the performance or responsiveness of the server.
829   *
830   * @return  The value of a flag that indicates whether the support data
831   *          archive may include data that is potentially expensive to collect,
832   *          or {@code null} if the property should not be specified when the
833   *          task is created (in which case the server will use a default
834   *          behavior of excluding expensive data).
835   */
836  @Nullable()
837  public Boolean getIncludeExpensiveData()
838  {
839    return includeExpensiveData;
840  }
841
842
843
844  /**
845   * Retrieves the value of a flag that indicates whether the support data
846   * archive may include a replication state dump, which may be several
847   * megabytes in size.
848   *
849   * @return  The value of a flag that indicates whether the support data
850   *          archive may include a replication state dump, or {@code null} if
851   *          the property should not be specified when the task is created (in
852   *          which case the server will use a default behavior of excluding the
853   *          state dump).
854   */
855  @Nullable()
856  public Boolean getIncludeReplicationStateDump()
857  {
858    return includeReplicationStateDump;
859  }
860
861
862
863  /**
864   * Retrieves the value of a flag that indicates whether the support data
865   * archive may include binary files.
866   *
867   * @return  The value of a flag that indicates whether the support data
868   *          archive may include binary files, or {@code null} if the property
869   *          should not be specified when the task is created (in which case
870   *          the server will use a default behavior of excluding binary files).
871   */
872  @Nullable()
873  public Boolean getIncludeBinaryFiles()
874  {
875    return includeBinaryFiles;
876  }
877
878
879
880  /**
881   * Retrieves the value of a flag that indicates whether the support data
882   * archive should include source code (if available) for any third-party
883   * extensions installed in the server.
884   *
885   * @return  The value of a flag that indicates whether the support data
886   *          archive should include source code (if available) for any
887   *          third-party extensions installed in the server, or {@code null} if
888   *          the property should not be specified when the task is created (in
889   *          which case the server will use a default behavior of excluding
890   *          extension source code).
891   */
892  @Nullable()
893  public Boolean getIncludeExtensionSource()
894  {
895    return includeExtensionSource;
896  }
897
898
899
900  /**
901   * Retrieves the value of a flag that indicates whether the server should
902   * collect items for the support data archive in sequential mode rather than
903   * in parallel.  Collecting data in sequential mode may reduce the amount of
904   * memory consumed during the collection process, but it will take longer to
905   * complete.
906   *
907   * @return  The value of a flag that indicates whether the server should
908   *          collect items for the support data archive in sequential mode
909   *          rather than in parallel, or {@code null} if the property should
910   *          not be specified when the task is created (in which case the
911   *          server will default to capturing data in parallel).
912   */
913  @Nullable()
914  public Boolean getUseSequentialMode()
915  {
916    return useSequentialMode;
917  }
918
919
920
921  /**
922   * Retrieves the value of a flag that indicates whether the server should skip
923   * the collection of Java Flight Recorder (JFR) data.
924   *
925   * @return  The value of a flag that indicates whether the server should skip
926   *          the collection of Java Flight Recorder data, or {@code null} if
927   *          the property should not be specified when the request is created
928   *          (in which case the server will use a default behavior of
929   *          collecting JFR data if a recording is available).
930   */
931  @Nullable()
932  public Boolean getNoJFR()
933  {
934    return noJFR;
935  }
936
937
938
939  /**
940   * Retrieves the security level that should be used to indicate which data
941   * should be obscured, redacted, or omitted from the support data archive.
942   *
943   * @return  The security level that should be used when creating the support
944   *          data archive, or {@code null} if the property should not be
945   *          specified when the task is created (in which case the server will
946   *          use a default security level).
947   */
948  @Nullable()
949  public CollectSupportDataSecurityLevel getSecurityLevel()
950  {
951    return securityLevel;
952  }
953
954
955
956  /**
957   * Retrieves the number of times that the jstack utility should be invoked to
958   * obtain stack traces from all threads in the server.
959   *
960   * @return  The number of times that the jstack utility should be invoked to
961   *          obtain stack traces from all threads in the server, or
962   *          {@code null} if the property should not be specified when the task
963   *          is created (in which case the server will use a default count).
964   */
965  @Nullable()
966  public Integer getJStackCount()
967  {
968    return jstackCount;
969  }
970
971
972
973  /**
974   * Retrieves the number of intervals that should be captured from tools that
975   * use interval-based sampling (e.g., vmstat, iostat, mpstat, etc.).
976   *
977   * @return  The number of intervals that should be captured from tools that
978   *          use interval-based sampling, or {@code null} if the property
979   *          should not be specified when the task is created (in which case
980   *          the server will use a default report count).
981   */
982  @Nullable()
983  public Integer getReportCount()
984  {
985    return reportCount;
986  }
987
988
989
990  /**
991   * Retrieves the interval duration in seconds that should be used for tools
992   * that use interval-based sampling (e.g., vmstat, iostat, mpstat, etc.).
993   *
994   * @return  The interval duration in seconds that should be used for tools
995   *          that use interval-based sampling, or {@code null} if the property
996   *          should not be specified when the task is created (in which case
997   *          the server will use a default report interval).
998   */
999  @Nullable()
1000  public Integer getReportIntervalSeconds()
1001  {
1002    return reportIntervalSeconds;
1003  }
1004
1005
1006
1007  /**
1008   * Retrieves the log capture window object that indicates how much log content
1009   * should be included in the support data archive.
1010   *
1011   * @return  The log capture window object that indicates how much log content
1012   *          should be included in the support data archive, or {@code null}
1013   *          if this should not be specified in the request and the server
1014   *          should choose an appropriate amount of log content.
1015   */
1016  @Nullable()
1017  public CollectSupportDataLogCaptureWindow getLogCaptureWindow()
1018  {
1019    return logCaptureWindow;
1020  }
1021
1022
1023
1024  /**
1025   * Retrieves an additional comment that should be included in the support data
1026   * archive.
1027   *
1028   * @return  An additional comment that should be included in the support data
1029   *          archive, or {@code null} if no comment should be included.
1030   */
1031  @Nullable()
1032  public String getComment()
1033  {
1034    return comment;
1035  }
1036
1037
1038
1039  /**
1040   * Retrieves the address of the backend Directory Server to which the collect
1041   * support data extended request should be forwarded.
1042   *
1043   * @return  The address of the backend Directory Server to which the collect
1044   *          support data extended request should be forwarded, or {@code null}
1045   *          if the request should be processed directly by the server that
1046   *          receives it.
1047   */
1048  @Nullable()
1049  public String getProxyToServerAddress()
1050  {
1051    return proxyToServerAddress;
1052  }
1053
1054
1055
1056  /**
1057   * Retrieves the port of the backend Directory Server to which the collect
1058   * support data extended request should be forwarded.
1059   *
1060   * @return  The port of the backend Directory Server to which the collect
1061   *          support data extended request should be forwarded, or {@code null}
1062   *          if the request should be processed directly by the server that
1063   *          receives it.
1064   */
1065  @Nullable()
1066  public Integer getProxyToServerPort()
1067  {
1068    return proxyToServerPort;
1069  }
1070
1071
1072
1073  /**
1074   * Retrieves the maximum size, in bytes, that may be used for a support data
1075   * archive fragment returned in any single
1076   * {@link CollectSupportDataArchiveFragmentIntermediateResponse} message.
1077   *
1078   * @return  The maximum size, in bytes, that may be used for a support data
1079   *          archive fragment in any single archive fragment intermediate
1080   *          response message, or {@code null} if the server should use a
1081   *          default maximum fragment size.
1082   */
1083  @Nullable()
1084  public Integer getMaximumFragmentSizeBytes()
1085  {
1086    return maximumFragmentSizeBytes;
1087  }
1088
1089
1090
1091  /**
1092   * {@inheritDoc}
1093   */
1094  @Override()
1095  @NotNull()
1096  public CollectSupportDataExtendedResult process(
1097              @NotNull final LDAPConnection connection, final int depth)
1098         throws LDAPException
1099  {
1100    final ExtendedResult extendedResponse = super.process(connection, depth);
1101    return new CollectSupportDataExtendedResult(extendedResponse);
1102  }
1103
1104
1105
1106  /**
1107   * {@inheritDoc}.
1108   */
1109  @Override()
1110  @NotNull()
1111  public CollectSupportDataExtendedRequest duplicate()
1112  {
1113    return duplicate(getControls());
1114  }
1115
1116
1117
1118  /**
1119   * {@inheritDoc}.
1120   */
1121  @Override()
1122  @NotNull()
1123  public CollectSupportDataExtendedRequest duplicate(
1124              @Nullable final Control[] controls)
1125  {
1126    final CollectSupportDataExtendedRequest r =
1127         new CollectSupportDataExtendedRequest(
1128              new CollectSupportDataExtendedRequestProperties(this),
1129              intermediateResponseListener, controls);
1130    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1131    r.setReferralDepth(getReferralDepth());
1132    r.setReferralConnector(getReferralConnectorInternal());
1133    return r;
1134  }
1135
1136
1137
1138  /**
1139   * {@inheritDoc}
1140   */
1141  @Override()
1142  @NotNull()
1143  public String getExtendedRequestName()
1144  {
1145    return INFO_COLLECT_SUPPORT_DATA_REQUEST_NAME.get();
1146  }
1147
1148
1149
1150
1151  /**
1152   * {@inheritDoc}
1153   */
1154  @Override()
1155  public void intermediateResponseReturned(
1156                   @NotNull final IntermediateResponse intermediateResponse)
1157  {
1158    final String oid = intermediateResponse.getOID();
1159    if (oid == null)
1160    {
1161      intermediateResponseListener.handleOtherIntermediateResponse(
1162           intermediateResponse);
1163      return;
1164    }
1165
1166    switch (oid)
1167    {
1168      case CollectSupportDataOutputIntermediateResponse.
1169           COLLECT_SUPPORT_DATA_OUTPUT_INTERMEDIATE_RESPONSE_OID:
1170        final CollectSupportDataOutputIntermediateResponse
1171             outputIntermediateResponse;
1172        try
1173        {
1174          outputIntermediateResponse =
1175               new CollectSupportDataOutputIntermediateResponse(
1176                    intermediateResponse);
1177        }
1178        catch (final Exception e)
1179        {
1180          Debug.debugException(e);
1181          intermediateResponseListener.handleOtherIntermediateResponse(
1182               intermediateResponse);
1183          return;
1184        }
1185
1186        intermediateResponseListener.handleOutputIntermediateResponse(
1187             outputIntermediateResponse);
1188        break;
1189
1190      case CollectSupportDataArchiveFragmentIntermediateResponse.
1191           COLLECT_SUPPORT_DATA_ARCHIVE_FRAGMENT_INTERMEDIATE_RESPONSE_OID:
1192        final CollectSupportDataArchiveFragmentIntermediateResponse
1193             fragmentIntermediateResponse;
1194        try
1195        {
1196          fragmentIntermediateResponse =
1197               new CollectSupportDataArchiveFragmentIntermediateResponse(
1198                    intermediateResponse);
1199        }
1200        catch (final Exception e)
1201        {
1202          Debug.debugException(e);
1203          intermediateResponseListener.handleOtherIntermediateResponse(
1204               intermediateResponse);
1205          return;
1206        }
1207
1208        intermediateResponseListener.handleArchiveFragmentIntermediateResponse(
1209             fragmentIntermediateResponse);
1210        break;
1211
1212      default:
1213        intermediateResponseListener.handleOtherIntermediateResponse(
1214             intermediateResponse);
1215    }
1216  }
1217
1218
1219
1220  /**
1221   * {@inheritDoc}
1222   */
1223  @Override()
1224  public void toString(@NotNull final StringBuilder buffer)
1225  {
1226    buffer.append("CollectSupportDataExtendedRequest(oid='");
1227    buffer.append(getOID());
1228    buffer.append('\'');
1229
1230    if (archiveFileName != null)
1231    {
1232      buffer.append(", archiveFileName='");
1233      buffer.append(archiveFileName);
1234      buffer.append('\'');
1235    }
1236
1237    if (encryptionPassphrase != null)
1238    {
1239      buffer.append(", encryptionPassphrase='*****REDACTED*****'");
1240    }
1241
1242    if (includeExpensiveData != null)
1243    {
1244      buffer.append(", includeExpensiveData=");
1245      buffer.append(includeExpensiveData);
1246    }
1247
1248    if (includeReplicationStateDump != null)
1249    {
1250      buffer.append(", includeReplicationStateDump=");
1251      buffer.append(includeReplicationStateDump);
1252    }
1253
1254    if (includeBinaryFiles != null)
1255    {
1256      buffer.append(", includeBinaryFiles=");
1257      buffer.append(includeBinaryFiles);
1258    }
1259
1260    if (includeExtensionSource != null)
1261    {
1262      buffer.append(", includeExtensionSource=");
1263      buffer.append(includeExtensionSource);
1264    }
1265
1266    if (useSequentialMode != null)
1267    {
1268      buffer.append(", useSequentialMode=");
1269      buffer.append(useSequentialMode);
1270    }
1271
1272    if (noJFR != null)
1273    {
1274      buffer.append(", noJFR=");
1275      buffer.append(noJFR);
1276    }
1277
1278    if (securityLevel != null)
1279    {
1280      buffer.append(", securityLevel='");
1281      buffer.append(securityLevel.getName());
1282      buffer.append('\'');
1283    }
1284
1285    if (jstackCount != null)
1286    {
1287      buffer.append(", jstackCount=");
1288      buffer.append(jstackCount);
1289    }
1290
1291    if (reportCount != null)
1292    {
1293      buffer.append(", reportCount=");
1294      buffer.append(reportCount);
1295    }
1296
1297    if (reportIntervalSeconds != null)
1298    {
1299      buffer.append(", reportIntervalSeconds=");
1300      buffer.append(reportIntervalSeconds);
1301    }
1302
1303    if (logCaptureWindow != null)
1304    {
1305      buffer.append(", logCaptureWindow=");
1306      logCaptureWindow.toString(buffer);
1307    }
1308
1309    if (comment != null)
1310    {
1311      buffer.append(", comment='");
1312      buffer.append(comment);
1313      buffer.append('\'');
1314    }
1315
1316    if (proxyToServerAddress != null)
1317    {
1318      buffer.append(", proxyToServerAddress='");
1319      buffer.append(proxyToServerAddress);
1320      buffer.append('\'');
1321    }
1322
1323    if (proxyToServerPort != null)
1324    {
1325      buffer.append(", proxyToServerPort=");
1326      buffer.append(proxyToServerPort);
1327    }
1328
1329    if (maximumFragmentSizeBytes != null)
1330    {
1331      buffer.append(", maximumFragmentSizeBytes=");
1332      buffer.append(maximumFragmentSizeBytes);
1333    }
1334
1335    final Control[] controls = getControls();
1336    if (controls.length > 0)
1337    {
1338      buffer.append(", controls={");
1339      for (int i=0; i < controls.length; i++)
1340      {
1341        if (i > 0)
1342        {
1343          buffer.append(", ");
1344        }
1345
1346        buffer.append(controls[i]);
1347      }
1348      buffer.append('}');
1349    }
1350
1351    buffer.append(')');
1352  }
1353}