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