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