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