001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.config;
028
029import static org.opends.messages.ConfigMessages.*;
030import static org.opends.server.config.ConfigConstants.*;
031import static org.opends.server.util.CollectionUtils.*;
032
033import java.lang.reflect.Array;
034import java.util.ArrayList;
035import java.util.LinkedHashSet;
036import java.util.List;
037
038import javax.management.AttributeList;
039import javax.management.MBeanAttributeInfo;
040import javax.management.MBeanParameterInfo;
041
042import org.forgerock.i18n.LocalizableMessage;
043import org.forgerock.i18n.slf4j.LocalizedLogger;
044import org.forgerock.opendj.ldap.ByteString;
045import org.forgerock.opendj.ldap.schema.Syntax;
046import org.opends.server.core.DirectoryServer;
047import org.opends.server.types.Attribute;
048
049/**
050 * This class defines a string configuration attribute, which can hold zero or
051 * more string values.
052 */
053@org.opends.server.types.PublicAPI(
054     stability=org.opends.server.types.StabilityLevel.VOLATILE,
055     mayInstantiate=true,
056     mayExtend=false,
057     mayInvoke=true)
058public final class StringConfigAttribute
059       extends ConfigAttribute
060{
061  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
062
063  /** The set of active values for this attribute. */
064  private List<String> activeValues;
065
066  /** The set of pending values for this attribute. */
067  private List<String> pendingValues;
068
069
070
071  /**
072   * Creates a new string configuration attribute stub with the provided
073   * information but no values.  The values will be set using the
074   * <CODE>setInitialValue</CODE> method.
075   *
076   * @param  name                 The name for this configuration attribute.
077   * @param  description          The description for this configuration
078   *                              attribute.
079   * @param  isRequired           Indicates whether this configuration attribute
080   *                              is required to have at least one value.
081   * @param  isMultiValued        Indicates whether this configuration attribute
082   *                              may have multiple values.
083   * @param  requiresAdminAction  Indicates whether changes to this
084   *                              configuration attribute require administrative
085   *                              action before they will take effect.
086   */
087  public StringConfigAttribute(String name, LocalizableMessage description,
088                               boolean isRequired, boolean isMultiValued,
089                               boolean requiresAdminAction)
090  {
091    super(name, description, isRequired, isMultiValued, requiresAdminAction);
092
093
094    activeValues  = new ArrayList<>();
095    pendingValues = activeValues;
096  }
097
098
099
100  /**
101   * Creates a new string configuration attribute with the provided information.
102   * No validation will be performed on the provided value.
103   *
104   * @param  name                 The name for this configuration attribute.
105   * @param  description          The description for this configuration
106   *                              attribute.
107   * @param  isRequired           Indicates whether this configuration attribute
108   *                              is required to have at least one value.
109   * @param  isMultiValued        Indicates whether this configuration attribute
110   *                              may have multiple values.
111   * @param  requiresAdminAction  Indicates whether changes to this
112   *                              configuration attribute require administrative
113   *                              action before they will take effect.
114   * @param  value                The value for this string configuration
115   *                              attribute.
116   */
117  public StringConfigAttribute(String name, LocalizableMessage description,
118                               boolean isRequired, boolean isMultiValued,
119                               boolean requiresAdminAction, String value)
120  {
121    super(name, description, isRequired, isMultiValued, requiresAdminAction,
122          getValueSet(value));
123
124
125    if (value == null)
126    {
127      activeValues = new ArrayList<>();
128    }
129    else
130    {
131      activeValues = newArrayList(value);
132    }
133
134    pendingValues = activeValues;
135  }
136
137
138
139  /**
140   * Creates a new string configuration attribute with the provided information.
141   * No validation will be performed on the provided values.
142   *
143   * @param  name                 The name for this configuration attribute.
144   * @param  description          The description for this configuration
145   *                              attribute.
146   * @param  isRequired           Indicates whether this configuration attribute
147   *                              is required to have at least one value.
148   * @param  isMultiValued        Indicates whether this configuration attribute
149   *                              may have multiple values.
150   * @param  requiresAdminAction  Indicates whether changes to this
151   *                              configuration attribute require administrative
152   *                              action before they will take effect.
153   * @param  values               The set of values for this configuration
154   *                              attribute.
155   */
156  public StringConfigAttribute(String name, LocalizableMessage description,
157                               boolean isRequired, boolean isMultiValued,
158                               boolean requiresAdminAction, List<String> values)
159  {
160    super(name, description, isRequired, isMultiValued, requiresAdminAction,
161          getValueSet(values));
162
163
164    activeValues  = values != null ? values : new ArrayList<String>();
165    pendingValues = activeValues;
166  }
167
168
169
170  /**
171   * Creates a new string configuration attribute with the provided information.
172   * No validation will be performed on the provided values.
173   *
174   * @param  name                 The name for this configuration attribute.
175   * @param  description          The description for this configuration
176   *                              attribute.
177   * @param  isRequired           Indicates whether this configuration attribute
178   *                              is required to have at least one value.
179   * @param  isMultiValued        Indicates whether this configuration attribute
180   *                              may have multiple values.
181   * @param  requiresAdminAction  Indicates whether changes to this
182   *                              configuration attribute require administrative
183   *                              action before they will take effect.
184   * @param  activeValues         The set of active values for this
185   *                              configuration attribute.
186   * @param  pendingValues        The set of pending values for this
187   *                              configuration attribute.
188   */
189  public StringConfigAttribute(String name, LocalizableMessage description,
190                               boolean isRequired, boolean isMultiValued,
191                               boolean requiresAdminAction,
192                               List<String> activeValues,
193                               List<String> pendingValues)
194  {
195    super(name, description, isRequired, isMultiValued, requiresAdminAction,
196          getValueSet(activeValues), (pendingValues != null),
197          getValueSet(pendingValues));
198
199
200    if (activeValues == null)
201    {
202      this.activeValues = new ArrayList<>();
203    }
204    else
205    {
206      this.activeValues = activeValues;
207    }
208
209    if (pendingValues == null)
210    {
211      this.pendingValues = this.activeValues;
212    }
213    else
214    {
215      this.pendingValues = pendingValues;
216    }
217  }
218
219
220
221  /**
222   * Retrieves the name of the data type for this configuration attribute.  This
223   * is for informational purposes (e.g., inclusion in method signatures and
224   * other kinds of descriptions) and does not necessarily need to map to an
225   * actual Java type.
226   *
227   * @return  The name of the data type for this configuration attribute.
228   */
229  public String getDataType()
230  {
231    return "String";
232  }
233
234
235
236  /**
237   * Retrieves the attribute syntax for this configuration attribute.
238   *
239   * @return  The attribute syntax for this configuration attribute.
240   */
241  public Syntax getSyntax()
242  {
243    return DirectoryServer.getDefaultStringSyntax();
244  }
245
246
247
248  /**
249   * Retrieves the active value for this configuration attribute as a string.
250   * This is only valid for single-valued attributes that have a value.
251   *
252   * @return The active value for this configuration attribute as a string.
253   * @throws org.forgerock.opendj.config.server.ConfigException
254   *           If this attribute does not have exactly one active value.
255   */
256  public String activeValue()
257         throws org.forgerock.opendj.config.server.ConfigException
258  {
259    if (activeValues == null || activeValues.isEmpty())
260    {
261      LocalizableMessage message = ERR_CONFIG_ATTR_NO_STRING_VALUE.get(getName());
262      throw new org.forgerock.opendj.config.server.ConfigException(message);
263    }
264
265    if (activeValues.size() > 1)
266    {
267      LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_STRING_VALUES.get(getName());
268      throw new org.forgerock.opendj.config.server.ConfigException(message);
269    }
270
271    return activeValues.get(0);
272  }
273
274
275
276  /**
277   * Retrieves the set of active values for this configuration attribute.
278   *
279   * @return  The set of active values for this configuration attribute.
280   */
281  public List<String> activeValues()
282  {
283    return activeValues;
284  }
285
286
287
288  /**
289   * Retrieves the pending value for this configuration attribute as a string.
290   * This is only valid for single-valued attributes that have a value. If this
291   * attribute does not have any pending values, then the active value will be
292   * returned.
293   *
294   * @return The pending value for this configuration attribute as a string.
295   * @throws org.forgerock.opendj.config.server.ConfigException
296   *           If this attribute does not have exactly one pending value.
297   */
298  public String pendingValue()
299         throws org.forgerock.opendj.config.server.ConfigException
300  {
301    if (! hasPendingValues())
302    {
303      return activeValue();
304    }
305
306    if (pendingValues == null || pendingValues.isEmpty())
307    {
308      LocalizableMessage message = ERR_CONFIG_ATTR_NO_STRING_VALUE.get(getName());
309      throw new org.forgerock.opendj.config.server.ConfigException(message);
310    }
311
312    if (pendingValues.size() > 1)
313    {
314      LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_STRING_VALUES.get(getName());
315      throw new org.forgerock.opendj.config.server.ConfigException(message);
316    }
317
318    return pendingValues.get(0);
319  }
320
321
322
323  /**
324   * Retrieves the set of pending values for this configuration attribute.  If
325   * there are no pending values, then the set of active values will be
326   * returned.
327   *
328   * @return  The set of pending values for this configuration attribute.
329   */
330  public List<String> pendingValues()
331  {
332    if (!hasPendingValues())
333    {
334      return activeValues;
335    }
336    return pendingValues;
337  }
338
339
340
341  /**
342   * Sets the value for this string configuration attribute.
343   *
344   * @param  value  The value for this string configuration attribute.
345   *
346   * @throws  ConfigException  If the provided value is not acceptable.
347   */
348  public void setValue(String value)
349         throws ConfigException
350  {
351    if (value == null || value.length() == 0)
352    {
353      throw new ConfigException(ERR_CONFIG_ATTR_EMPTY_STRING_VALUE.get(getName()));
354    }
355
356    if (requiresAdminAction())
357    {
358      pendingValues = newArrayList(value);
359      setPendingValues(getValueSet(value));
360    }
361    else
362    {
363      activeValues.clear();
364      activeValues.add(value);
365      pendingValues = activeValues;
366      setActiveValues(getValueSet(value));
367    }
368  }
369
370
371
372  /**
373   * Sets the values for this string configuration attribute.
374   *
375   * @param  values  The set of values for this string configuration attribute.
376   *
377   * @throws  ConfigException  If the provided value set or any of the
378   *                           individual values are not acceptable.
379   */
380  public void setValues(List<String> values)
381         throws ConfigException
382  {
383    // First check if the set is empty and if that is allowed.
384    if (values == null || values.isEmpty())
385    {
386      if (isRequired())
387      {
388        throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName()));
389      }
390
391      if (requiresAdminAction())
392      {
393        setPendingValues(new LinkedHashSet<ByteString>(0));
394        pendingValues = new ArrayList<>();
395      }
396      else
397      {
398        setActiveValues(new LinkedHashSet<ByteString>(0));
399        activeValues.clear();
400      }
401    }
402
403
404    // Next check if the set contains multiple values and if that is allowed.
405    int numValues = values.size();
406    if (!isMultiValued() && numValues > 1)
407    {
408      throw new ConfigException(ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName()));
409    }
410
411
412    // Iterate through all the provided values, make sure that they are
413    // acceptable, and build the value set.
414    LinkedHashSet<ByteString> valueSet = new LinkedHashSet<>(numValues);
415    for (String value : values)
416    {
417      if (value == null || value.length() == 0)
418      {
419        throw new ConfigException(ERR_CONFIG_ATTR_EMPTY_STRING_VALUE.get(getName()));
420      }
421
422      ByteString attrValue = ByteString.valueOfUtf8(value);
423      if (valueSet.contains(attrValue))
424      {
425        throw new ConfigException(ERR_CONFIG_ATTR_ADD_VALUES_ALREADY_EXISTS.get(getName(), value));
426      }
427
428      valueSet.add(attrValue);
429    }
430
431
432    // Apply this value set to the new active or pending value set.
433    if (requiresAdminAction())
434    {
435      pendingValues = values;
436      setPendingValues(valueSet);
437    }
438    else
439    {
440      activeValues  = values;
441      pendingValues = activeValues;
442      setActiveValues(valueSet);
443    }
444  }
445
446
447
448  /**
449   * Applies the set of pending values, making them the active values for this
450   * configuration attribute.  This will not take any action if there are no
451   * pending values.
452   */
453  public void applyPendingValues()
454  {
455    if (! hasPendingValues())
456    {
457      return;
458    }
459
460    super.applyPendingValues();
461    activeValues = pendingValues;
462  }
463
464
465
466  /**
467   * Indicates whether the provided value is acceptable for use in this
468   * attribute.  If it is not acceptable, then the reason should be written into
469   * the provided buffer.
470   *
471   * @param  value         The value for which to make the determination.
472   * @param  rejectReason  A buffer into which a human-readable reason for the
473   *                       reject may be written.
474   *
475   * @return  <CODE>true</CODE> if the provided value is acceptable for use in
476   *          this attribute, or <CODE>false</CODE> if not.
477   */
478  public boolean valueIsAcceptable(ByteString value,
479                                   StringBuilder rejectReason)
480  {
481    // The only requirement is that the value is not null or empty.
482    if (value == null || value.toString().length() == 0)
483    {
484      rejectReason.append(ERR_CONFIG_ATTR_EMPTY_STRING_VALUE.get(getName()));
485      return false;
486    }
487    return true;
488  }
489
490
491
492  /**
493   * Converts the provided set of strings to a corresponding set of attribute
494   * values.
495   *
496   * @param  valueStrings   The set of strings to be converted into attribute
497   *                        values.
498   * @param  allowFailures  Indicates whether the decoding process should allow
499   *                        any failures in which one or more values could be
500   *                        decoded but at least one could not.  If this is
501   *                        <CODE>true</CODE> and such a condition is acceptable
502   *                        for the underlying attribute type, then the returned
503   *                        set of values should simply not include those
504   *                        undecodable values.
505   *
506   * @return  The set of attribute values converted from the provided strings.
507   *
508   * @throws  ConfigException  If an unrecoverable problem occurs while
509   *                           performing the conversion.
510   */
511  public LinkedHashSet<ByteString> stringsToValues(List<String> valueStrings, boolean allowFailures)
512      throws ConfigException
513  {
514    if (valueStrings == null || valueStrings.isEmpty())
515    {
516      if (isRequired())
517      {
518        throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName()));
519      }
520      return new LinkedHashSet<>();
521    }
522
523    int numValues = valueStrings.size();
524    if (!isMultiValued() && numValues > 1)
525    {
526      throw new ConfigException(ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName()));
527    }
528
529    LinkedHashSet<ByteString> valueSet = new LinkedHashSet<>(numValues);
530    for (String valueString : valueStrings)
531    {
532      if (valueString == null || valueString.length() == 0)
533      {
534        reportError(allowFailures, ERR_CONFIG_ATTR_EMPTY_STRING_VALUE.get(getName()));
535        continue;
536      }
537
538      valueSet.add(ByteString.valueOfUtf8(valueString));
539    }
540
541    // If this method was configured to continue on error, then it is possible
542    // that we ended up with an empty list.  Check to see if this is a required
543    // attribute and if so deal with it accordingly.
544    if (isRequired() && valueSet.isEmpty())
545    {
546      throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName()));
547    }
548
549    return valueSet;
550  }
551
552  private void reportError(boolean allowFailures, LocalizableMessage message) throws ConfigException
553  {
554    if (!allowFailures)
555    {
556      throw new ConfigException(message);
557    }
558    logger.error(message);
559  }
560
561  /**
562   * Converts the set of active values for this configuration attribute into a
563   * set of strings that may be stored in the configuration or represented over
564   * protocol.  The string representation used by this method should be
565   * compatible with the decoding used by the <CODE>stringsToValues</CODE>
566   * method.
567   *
568   * @return  The string representations of the set of active values for this
569   *          configuration attribute.
570   */
571  public List<String> activeValuesToStrings()
572  {
573    return activeValues;
574  }
575
576
577
578  /**
579   * Converts the set of pending values for this configuration attribute into a
580   * set of strings that may be stored in the configuration or represented over
581   * protocol.  The string representation used by this method should be
582   * compatible with the decoding used by the <CODE>stringsToValues</CODE>
583   * method.
584   *
585   * @return  The string representations of the set of pending values for this
586   *          configuration attribute, or <CODE>null</CODE> if there are no
587   *          pending values.
588   */
589  public List<String> pendingValuesToStrings()
590  {
591    if (hasPendingValues())
592    {
593      return pendingValues;
594    }
595    return null;
596  }
597
598
599
600  /**
601   * Retrieves a new configuration attribute of this type that will contain the
602   * values from the provided attribute.
603   *
604   * @param  attributeList  The list of attributes to use to create the config
605   *                        attribute.  The list must contain either one or two
606   *                        elements, with both attributes having the same base
607   *                        name and the only option allowed is ";pending" and
608   *                        only if this attribute is one that requires admin
609   *                        action before a change may take effect.
610   *
611   * @return  The generated configuration attribute.
612   *
613   * @throws  ConfigException  If the provided attribute cannot be treated as a
614   *                           configuration attribute of this type (e.g., if
615   *                           one or more of the values of the provided
616   *                           attribute are not suitable for an attribute of
617   *                           this type, or if this configuration attribute is
618   *                           single-valued and the provided attribute has
619   *                           multiple values).
620   */
621  public ConfigAttribute getConfigAttribute(List<Attribute> attributeList)
622         throws ConfigException
623  {
624    ArrayList<String> activeValues  = null;
625    ArrayList<String> pendingValues = null;
626
627    for (Attribute a : attributeList)
628    {
629      if (a.hasOptions())
630      {
631        // This must be the pending value.
632        if (a.hasOption(OPTION_PENDING_VALUES))
633        {
634          if (pendingValues != null)
635          {
636            // We cannot have multiple pending value sets.
637            throw new ConfigException(ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName()));
638          }
639
640
641          if (a.isEmpty())
642          {
643            if (isRequired())
644            {
645              // This is illegal -- it must have a value.
646              throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName()));
647            }
648            // This is fine. The pending value set can be empty.
649            pendingValues = new ArrayList<>(0);
650          }
651          else
652          {
653            int numValues = a.size();
654            if (numValues > 1 && !isMultiValued())
655            {
656              // This is illegal -- the attribute is single-valued.
657              LocalizableMessage message =
658                  ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
659              throw new ConfigException(message);
660            }
661
662            pendingValues = new ArrayList<>(numValues);
663            for (ByteString v : a)
664            {
665              pendingValues.add(v.toString());
666            }
667          }
668        }
669        else
670        {
671          // This is illegal -- only the pending option is allowed for
672          // configuration attributes.
673          throw new ConfigException(ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get(a.getName()));
674        }
675      }
676      else
677      {
678        // This must be the active value.
679        if (activeValues!= null)
680        {
681          // We cannot have multiple active value sets.
682          throw new ConfigException(ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName()));
683        }
684
685
686        if (a.isEmpty())
687        {
688          if (isRequired())
689          {
690            // This is illegal -- it must have a value.
691            throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName()));
692          }
693          // This is fine. The active value set can be empty.
694          activeValues = new ArrayList<>(0);
695        }
696        else
697        {
698          int numValues = a.size();
699          if (numValues > 1 && !isMultiValued())
700          {
701            // This is illegal -- the attribute is single-valued.
702            LocalizableMessage message =
703                ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
704            throw new ConfigException(message);
705          }
706
707          activeValues = new ArrayList<>(numValues);
708          for (ByteString v : a)
709          {
710            activeValues.add(v.toString());
711          }
712        }
713      }
714    }
715
716    if (activeValues == null)
717    {
718      // This is not OK.  The value set must contain an active value.
719      LocalizableMessage message = ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName());
720      throw new ConfigException(message);
721    }
722
723    if (pendingValues == null)
724    {
725      // This is OK.  We'll just use the active value set.
726      pendingValues = activeValues;
727    }
728
729    return new StringConfigAttribute(getName(), getDescription(), isRequired(),
730                                     isMultiValued(), requiresAdminAction(),
731                                     activeValues, pendingValues);
732  }
733
734
735
736  /**
737   * Retrieves a JMX attribute containing the active value set for this
738   * configuration attribute.
739   *
740   * @param pending indicates if pending or active  values are required.
741   *
742   * @return  A JMX attribute containing the active value set for this
743   *          configuration attribute, or <CODE>null</CODE> if it does not have
744   *          any active values.
745   */
746  private javax.management.Attribute _toJMXAttribute(boolean pending)
747  {
748    List<String> requestedValues;
749    String name;
750    if (pending)
751    {
752        requestedValues = pendingValues ;
753        name = getName() + ";" + OPTION_PENDING_VALUES ;
754    }
755    else
756    {
757        requestedValues = activeValues ;
758        name = getName() ;
759    }
760
761    if (isMultiValued())
762    {
763      String[] values = requestedValues.toArray(new String[requestedValues.size()]);
764      return new javax.management.Attribute(name, values);
765    }
766    else if (!requestedValues.isEmpty())
767    {
768      return new javax.management.Attribute(name, requestedValues.get(0));
769    }
770    else
771    {
772      return null;
773    }
774  }
775
776  /**
777   * Retrieves a JMX attribute containing the active value set for this
778   * configuration attribute.
779   *
780   * @return  A JMX attribute containing the active value set for this
781   *          configuration attribute, or <CODE>null</CODE> if it does not have
782   *          any active values.
783   */
784  public javax.management.Attribute toJMXAttribute()
785  {
786    return _toJMXAttribute(false) ;
787  }
788
789  /**
790   * Retrieves a JMX attribute containing the pending value set for this
791   * configuration attribute.
792   *
793   * @return  A JMX attribute containing the pending value set for this
794   *          configuration attribute, or <CODE>null</CODE> if it does not have
795   *          any active values.
796   */
797  public javax.management.Attribute toJMXAttributePending()
798  {
799    return _toJMXAttribute(true) ;
800  }
801
802
803
804  /**
805   * Adds information about this configuration attribute to the provided JMX
806   * attribute list.  If this configuration attribute requires administrative
807   * action before changes take effect and it has a set of pending values, then
808   * two attributes should be added to the list -- one for the active value
809   * and one for the pending value.  The pending value should be named with
810   * the pending option.
811   *
812   * @param  attributeList  The attribute list to which the JMX attribute(s)
813   *                        should be added.
814   */
815  public void toJMXAttribute(AttributeList attributeList)
816  {
817    if (!activeValues.isEmpty())
818    {
819      if (isMultiValued())
820      {
821        String[] values = new String[activeValues.size()];
822        activeValues.toArray(values);
823
824        attributeList.add(new javax.management.Attribute(getName(), values));
825      }
826      else
827      {
828        attributeList.add(new javax.management.Attribute(getName(),
829                                                         activeValues.get(0)));
830      }
831    }
832    else
833    {
834      if (isMultiValued())
835      {
836        attributeList.add(new javax.management.Attribute(getName(),
837                                                         new String[0]));
838      }
839      else
840      {
841        attributeList.add(new javax.management.Attribute(getName(), null));
842      }
843    }
844
845
846    if (requiresAdminAction() && pendingValues != null && pendingValues != activeValues)
847    {
848      String name = getName() + ";" + OPTION_PENDING_VALUES;
849
850      if (isMultiValued())
851      {
852        String[] values = new String[pendingValues.size()];
853        pendingValues.toArray(values);
854
855        attributeList.add(new javax.management.Attribute(name, values));
856      }
857      else if (! pendingValues.isEmpty())
858      {
859        attributeList.add(new javax.management.Attribute(name, pendingValues.get(0)));
860      }
861    }
862  }
863
864
865
866  /**
867   * Adds information about this configuration attribute to the provided list in
868   * the form of a JMX <CODE>MBeanAttributeInfo</CODE> object.  If this
869   * configuration attribute requires administrative action before changes take
870   * effect and it has a set of pending values, then two attribute info objects
871   * should be added to the list -- one for the active value (which should be
872   * read-write) and one for the pending value (which should be read-only).  The
873   * pending value should be named with the pending option.
874   *
875   * @param  attributeInfoList  The list to which the attribute information
876   *                            should be added.
877   */
878  public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList)
879  {
880    attributeInfoList.add(new MBeanAttributeInfo(getName(), getType(),
881        String.valueOf(getDescription()), true, true, false));
882
883    if (requiresAdminAction())
884    {
885      String name = getName() + ";" + OPTION_PENDING_VALUES;
886      attributeInfoList.add(new MBeanAttributeInfo(name, getType(),
887          String.valueOf(getDescription()), true, false, false));
888    }
889  }
890
891
892
893  /**
894   * Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this
895   * configuration attribute.
896   *
897   * @return  A JMX <CODE>MBeanParameterInfo</CODE> object that describes this
898   *          configuration attribute.
899   */
900  public MBeanParameterInfo toJMXParameterInfo()
901  {
902    return new MBeanParameterInfo(getName(), getType(), String.valueOf(getDescription()));
903  }
904
905  private String getType()
906  {
907    return isMultiValued() ? JMX_TYPE_STRING_ARRAY : String.class.getName();
908  }
909
910  /**
911   * Attempts to set the value of this configuration attribute based on the
912   * information in the provided JMX attribute.
913   *
914   * @param  jmxAttribute  The JMX attribute to use to attempt to set the value
915   *                       of this configuration attribute.
916   *
917   * @throws  ConfigException  If the provided JMX attribute does not have an
918   *                           acceptable value for this configuration
919   *                           attribute.
920   */
921  public void setValue(javax.management.Attribute jmxAttribute)
922         throws ConfigException
923  {
924    Object value = jmxAttribute.getValue();
925    if (value instanceof String)
926    {
927      setValue((String) value);
928    }
929    else if (value.getClass().isArray())
930    {
931      String componentType = value.getClass().getComponentType().getName();
932      int length = Array.getLength(value);
933
934      if (componentType.equals(String.class.getName()))
935      {
936        try
937        {
938          ArrayList<String> values = new ArrayList<>(length);
939
940          for (int i=0; i < length; i++)
941          {
942            values.add((String) Array.get(value, i));
943          }
944
945          setValues(values);
946        }
947        catch (ConfigException ce)
948        {
949          logger.traceException(ce);
950
951          throw ce;
952        }
953        catch (Exception e)
954        {
955          logger.traceException(e);
956
957          throw new ConfigException(ERR_CONFIG_ATTR_INVALID_STRING_VALUE.get(getName(), value, e), e);
958        }
959      }
960      else
961      {
962        LocalizableMessage message =
963            ERR_CONFIG_ATTR_STRING_INVALID_ARRAY_TYPE.get(jmxAttribute, componentType);
964        throw new ConfigException(message);
965      }
966    }
967    else
968    {
969      throw new ConfigException(ERR_CONFIG_ATTR_STRING_INVALID_TYPE.get(
970          value, getName(), value.getClass().getName()));
971    }
972  }
973
974
975
976  /**
977   * Creates a duplicate of this configuration attribute.
978   *
979   * @return  A duplicate of this configuration attribute.
980   */
981  public ConfigAttribute duplicate()
982  {
983    return new StringConfigAttribute(getName(), getDescription(), isRequired(),
984                                     isMultiValued(), requiresAdminAction(),
985                                     activeValues, pendingValues);
986  }
987}