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