001/*
002 * Copyright 2013-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2013-2020 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) 2015-2020 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.Collection;
042import java.util.Collections;
043import java.util.Iterator;
044import java.util.List;
045import java.util.TreeSet;
046
047import com.unboundid.asn1.ASN1Element;
048import com.unboundid.asn1.ASN1OctetString;
049import com.unboundid.asn1.ASN1Sequence;
050import com.unboundid.ldap.sdk.Control;
051import com.unboundid.ldap.sdk.ExtendedResult;
052import com.unboundid.ldap.sdk.LDAPException;
053import com.unboundid.ldap.sdk.ResultCode;
054import com.unboundid.util.Debug;
055import com.unboundid.util.StaticUtils;
056import com.unboundid.util.ThreadSafety;
057import com.unboundid.util.ThreadSafetyLevel;
058import com.unboundid.util.Validator;
059
060import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
061
062
063
064/**
065 * This class provides an implementation of an extended result that can be used
066 * to retrieve a list of all available versions of the configuration within a
067 * server.  This may include not only the currently-active configuration, but
068 * also former configurations that have been archived, and the baseline
069 * configuration for the current server version.
070 * <BR>
071 * <BLOCKQUOTE>
072 *   <B>NOTE:</B>  This class, and other classes within the
073 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
074 *   supported for use against Ping Identity, UnboundID, and
075 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
076 *   for proprietary functionality or for external specifications that are not
077 *   considered stable or mature enough to be guaranteed to work in an
078 *   interoperable way with other types of LDAP servers.
079 * </BLOCKQUOTE>
080 * <BR>
081 * The OID for this extended result is 1.3.6.1.4.1.30221.2.6.27.  If the request
082 * was processed successfully, then the response will have a value with the
083 * following encoding:
084 * <PRE>
085 *   ListConfigurationsResult ::= SEQUENCE {
086 *        activeConfigFileName        [0] OCTET STRING,
087 *        baselineConfigFileNames     [1] OCTET STRING OPTIONAL,
088 *        archivedConfigFileNames     [2] SEQUENCE OF OCTET STRING OPTIONAL,
089 *        ... }
090 * </PRE>
091 */
092@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
093public final class ListConfigurationsExtendedResult
094       extends ExtendedResult
095{
096  /**
097   * The OID (1.3.6.1.4.1.30221.2.6.27) for the list configurations extended
098   * result.
099   */
100  public static final String LIST_CONFIGS_RESULT_OID =
101       "1.3.6.1.4.1.30221.2.6.27";
102
103
104
105  /**
106   * The BER type for the element holding the filename used for the active
107   * configuration.
108   */
109  private static final byte TYPE_ACTIVE_CONFIG_FILE_NAME = (byte) 0x80;
110
111
112
113  /**
114   * The BER type for the element holding the filename used for the baseline
115   * configuration.
116   */
117  private static final byte TYPE_BASELINE_CONFIG_FILE_NAMES = (byte) 0xA1;
118
119
120
121  /**
122   * The BER type for the element holding the filenames used for the archived
123   * configurations.
124   */
125  private static final byte TYPE_ARCHIVED_CONFIG_FILE_NAMES = (byte) 0xA2;
126
127
128
129  /**
130   * The serial version UID for this serializable class.
131   */
132  private static final long serialVersionUID = -466738484294922561L;
133
134
135
136  // The names of the archived configuration files.
137  private final List<String> archivedFileNames;
138
139  // The name of the baseline configuration file.
140  private final List<String> baselineFileNames;
141
142  // The name of the active configuration file.
143  private final String activeFileName;
144
145
146
147  /**
148   * Creates a new list configurations extended result from the provided generic
149   * extended result.
150   *
151   * @param  result  The generic extended result to be decoded as a list
152   *                 configurations extended result.
153   *
154   * @throws LDAPException  If the provided extended result cannot be parsed as
155   *                         a valid list configurations extended result.
156   */
157  public ListConfigurationsExtendedResult(final ExtendedResult result)
158       throws LDAPException
159  {
160    super(result);
161
162    final ASN1OctetString value = result.getValue();
163    if (value == null)
164    {
165      activeFileName = null;
166      baselineFileNames = Collections.emptyList();
167      archivedFileNames = Collections.emptyList();
168      return;
169    }
170
171    try
172    {
173      String activeName = null;
174      List<String> archivedNames = Collections.emptyList();
175      List<String> baselineNames = null;
176      final ASN1Element[] elements =
177           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
178      for (final ASN1Element e : elements)
179      {
180        switch (e.getType())
181        {
182          case TYPE_ACTIVE_CONFIG_FILE_NAME:
183            activeName = ASN1OctetString.decodeAsOctetString(e).stringValue();
184            break;
185          case TYPE_BASELINE_CONFIG_FILE_NAMES:
186            final ASN1Element[] baselineNameElements =
187                 ASN1Sequence.decodeAsSequence(e).elements();
188            baselineNames = new ArrayList<>(baselineNameElements.length);
189            for (final ASN1Element el : baselineNameElements)
190            {
191              baselineNames.add(
192                   ASN1OctetString.decodeAsOctetString(el).stringValue());
193            }
194            archivedNames = Collections.unmodifiableList(baselineNames);
195            break;
196          case TYPE_ARCHIVED_CONFIG_FILE_NAMES:
197            final ASN1Element[] archivedNameElements =
198                 ASN1Sequence.decodeAsSequence(e).elements();
199            archivedNames = new ArrayList<>(archivedNameElements.length);
200            for (final ASN1Element el : archivedNameElements)
201            {
202              archivedNames.add(
203                   ASN1OctetString.decodeAsOctetString(el).stringValue());
204            }
205            archivedNames = Collections.unmodifiableList(archivedNames);
206            break;
207          default:
208            throw new LDAPException(ResultCode.DECODING_ERROR,
209                 ERR_LIST_CONFIGS_RESULT_UNEXPECTED_ELEMENT_TYPE.get(
210                      StaticUtils.toHex(e.getType())));
211        }
212      }
213
214      activeFileName    = activeName;
215      archivedFileNames = archivedNames;
216      baselineFileNames = baselineNames;
217
218      if (activeFileName == null)
219      {
220        throw new LDAPException(ResultCode.DECODING_ERROR,
221             ERR_LIST_CONFIGS_RESULT_NO_ACTIVE_CONFIG.get());
222      }
223    }
224    catch (final LDAPException le)
225    {
226      Debug.debugException(le);
227      throw le;
228    }
229    catch (final Exception e)
230    {
231      Debug.debugException(e);
232      throw new LDAPException(ResultCode.DECODING_ERROR,
233           ERR_LIST_CONFIGS_RESULT_ERROR_PARSING_VALUE.get(
234                StaticUtils.getExceptionMessage(e)),
235           e);
236    }
237  }
238
239
240
241  /**
242   * Creates a new list configurations extended result with the provided
243   * information.
244   *
245   * @param  messageID          The message ID for the LDAP message that is
246   *                            associated with this LDAP result.
247   * @param  resultCode         The result code from the response.
248   * @param  diagnosticMessage  The diagnostic message from the response, if
249   *                            available.
250   * @param  matchedDN          The matched DN from the response, if available.
251   * @param  referralURLs       The set of referral URLs from the response, if
252   *                            available.
253   * @param  activeFileName     The name of the active configuration file, if
254   *                            available.
255   * @param  baselineFileNames  The names of the baseline configuration files
256   *                            for current and former server versions, if
257   *                            available.  It must be {@code null} or empty if
258   *                            the active file name is {@code null}.
259   * @param  archivedFileNames  The names of the archived configuration files,
260   *                            if available.  It must be {@code null} or empty
261   *                            if the active file name is {@code null}.
262   * @param  responseControls   The set of controls from the response, if
263   *                            available.
264   */
265  public ListConfigurationsExtendedResult(final int messageID,
266              final ResultCode resultCode, final String diagnosticMessage,
267              final String matchedDN, final String[] referralURLs,
268              final String activeFileName,
269              final Collection<String> baselineFileNames,
270              final Collection<String> archivedFileNames,
271              final Control... responseControls)
272  {
273    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
274         ((activeFileName == null) ? null : LIST_CONFIGS_RESULT_OID),
275         encodeValue(activeFileName, baselineFileNames, archivedFileNames),
276         responseControls);
277
278    this.activeFileName   = activeFileName;
279
280    if (baselineFileNames == null)
281    {
282      this.baselineFileNames = Collections.emptyList();
283    }
284    else
285    {
286      this.baselineFileNames =
287           Collections.unmodifiableList(new ArrayList<>(baselineFileNames));
288    }
289
290    if (archivedFileNames == null)
291    {
292      this.archivedFileNames = Collections.emptyList();
293    }
294    else
295    {
296      this.archivedFileNames =
297           Collections.unmodifiableList(new ArrayList<>(archivedFileNames));
298    }
299  }
300
301
302
303  /**
304   * Creates an ASN.1 octet string containing an encoded representation of the
305   * value for a list configurations extended result with the provided
306   * information.
307   *
308   * @param  activeFileName     The name of the active configuration file, if
309   *                            available.
310   * @param  baselineFileNames  The names of the baseline configuration files
311   *                            for current and former server versions, if
312   *                            available.  It must be {@code null} or empty if
313   *                            the active file name is {@code null}.
314   * @param  archivedFileNames  The names of the archived configuration files,
315   *                            if available.  It must be {@code null} or empty
316   *                            if the active file name is {@code null}.
317   *
318   * @return  An ASN.1 octet string containing an encoded representation of the
319   *          value for a list configurations extended result, or {@code null}
320   *          if a result with the provided information should not have a value.
321   */
322  public static ASN1OctetString encodeValue(final String activeFileName,
323                     final Collection<String> baselineFileNames,
324                     final Collection<String> archivedFileNames)
325  {
326    if (activeFileName == null)
327    {
328      Validator.ensureTrue(
329           ((baselineFileNames == null) || baselineFileNames.isEmpty()),
330           "The baseline filename must be null if the active filename is null");
331      Validator.ensureTrue(
332           ((archivedFileNames == null) || archivedFileNames.isEmpty()),
333           "The archived filenames must be null or empty if the active " +
334                "filename is null");
335      return null;
336    }
337
338    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
339    elements.add(
340         new ASN1OctetString(TYPE_ACTIVE_CONFIG_FILE_NAME, activeFileName));
341
342    if ((baselineFileNames != null) && (! baselineFileNames.isEmpty()))
343    {
344      final TreeSet<String> sortedBaselineNames =
345           new TreeSet<>(baselineFileNames);
346      final ArrayList<ASN1Element> baselineNameElements =
347           new ArrayList<>(sortedBaselineNames.size());
348      for (final String s : sortedBaselineNames)
349      {
350        baselineNameElements.add(new ASN1OctetString(s));
351      }
352      elements.add(new ASN1Sequence(TYPE_BASELINE_CONFIG_FILE_NAMES,
353           baselineNameElements));
354    }
355
356    if ((archivedFileNames != null) && (! archivedFileNames.isEmpty()))
357    {
358      final TreeSet<String> sortedArchivedNames =
359           new TreeSet<>(archivedFileNames);
360      final ArrayList<ASN1Element> archivedNameElements =
361           new ArrayList<>(sortedArchivedNames.size());
362      for (final String s : sortedArchivedNames)
363      {
364        archivedNameElements.add(new ASN1OctetString(s));
365      }
366      elements.add(new ASN1Sequence(TYPE_ARCHIVED_CONFIG_FILE_NAMES,
367           archivedNameElements));
368    }
369
370    return new ASN1OctetString(new ASN1Sequence(elements).encode());
371  }
372
373
374
375  /**
376   * Retrieves the name of the active configuration file the server is
377   * currently using, if available.
378   *
379   * @return  The name of the active configuration file the server is
380   *          currently using, or {@code null} this is not available.
381   */
382  public String getActiveFileName()
383  {
384    return activeFileName;
385  }
386
387
388
389  /**
390   * Retrieves a list containing the names of the baseline configuration files
391   * (i.e., the files containing the initial "out-of-the-box" configuration for
392   * various server versions), if available.
393   *
394   * @return  A list containing the names of the baseline configuration files,
395   *          or an empty list if this is not available.
396   */
397  public List<String> getBaselineFileNames()
398  {
399    return baselineFileNames;
400  }
401
402
403
404  /**
405   * Retrieves a list containing the names of the archived configuration files,
406   * if available.
407   *
408   * @return  A list containing the names of the archived configuration files,
409   *          or an empty list if this is not available.
410   */
411  public List<String> getArchivedFileNames()
412  {
413    return archivedFileNames;
414  }
415
416
417
418  /**
419   * {@inheritDoc}
420   */
421  @Override()
422  public String getExtendedResultName()
423  {
424    return INFO_EXTENDED_RESULT_NAME_LIST_CONFIGS.get();
425  }
426
427
428
429  /**
430   * {@inheritDoc}
431   */
432  @Override()
433  public void toString(final StringBuilder buffer)
434  {
435    buffer.append("ListConfigurationsExtendedResult(resultCode=");
436    buffer.append(getResultCode());
437
438    final int messageID = getMessageID();
439    if (messageID >= 0)
440    {
441      buffer.append(", messageID=");
442      buffer.append(messageID);
443    }
444
445    if (activeFileName != null)
446    {
447      buffer.append(", activeFileName='");
448      buffer.append(activeFileName);
449      buffer.append('\'');
450    }
451
452    if (! baselineFileNames.isEmpty())
453    {
454      buffer.append(", baselineFileNames={");
455
456      final Iterator<String> iterator = baselineFileNames.iterator();
457      while (iterator.hasNext())
458      {
459        buffer.append('\'');
460        buffer.append(iterator.next());
461        buffer.append('\'');
462        if (iterator.hasNext())
463        {
464          buffer.append(',');
465        }
466      }
467
468      buffer.append('}');
469    }
470
471    if (! archivedFileNames.isEmpty())
472    {
473      buffer.append(", archivedFileNames={");
474
475      final Iterator<String> iterator = archivedFileNames.iterator();
476      while (iterator.hasNext())
477      {
478        buffer.append('\'');
479        buffer.append(iterator.next());
480        buffer.append('\'');
481        if (iterator.hasNext())
482        {
483          buffer.append(',');
484        }
485      }
486
487      buffer.append('}');
488    }
489
490    final String diagnosticMessage = getDiagnosticMessage();
491    if (diagnosticMessage != null)
492    {
493      buffer.append(", diagnosticMessage='");
494      buffer.append(diagnosticMessage);
495      buffer.append('\'');
496    }
497
498    final String matchedDN = getMatchedDN();
499    if (matchedDN != null)
500    {
501      buffer.append(", matchedDN='");
502      buffer.append(matchedDN);
503      buffer.append('\'');
504    }
505
506    final String[] referralURLs = getReferralURLs();
507    if (referralURLs.length > 0)
508    {
509      buffer.append(", referralURLs={");
510      for (int i=0; i < referralURLs.length; i++)
511      {
512        if (i > 0)
513        {
514          buffer.append(", ");
515        }
516
517        buffer.append('\'');
518        buffer.append(referralURLs[i]);
519        buffer.append('\'');
520      }
521      buffer.append('}');
522    }
523
524    final Control[] responseControls = getResponseControls();
525    if (responseControls.length > 0)
526    {
527      buffer.append(", responseControls={");
528      for (int i=0; i < responseControls.length; i++)
529      {
530        if (i > 0)
531        {
532          buffer.append(", ");
533        }
534
535        buffer.append(responseControls[i]);
536      }
537      buffer.append('}');
538    }
539
540    buffer.append(')');
541  }
542}