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 an integer configuration attribute, which can hold zero or
054     * more integer values.  For scalability, the actual values will be stored as
055     * <CODE>long</CODE> elements, although it will be possible to interact with
056     * them as integers in cases where that scalability is not required.
057     */
058    @org.opends.server.types.PublicAPI(
059         stability=org.opends.server.types.StabilityLevel.VOLATILE,
060         mayInstantiate=true,
061         mayExtend=false,
062         mayInvoke=true)
063    public final class IntegerConfigAttribute
064           extends ConfigAttribute
065    {
066      /**
067       * The tracer object for the debug logger.
068       */
069      private static final DebugTracer TRACER = getTracer();
070    
071    
072    
073    
074      // The set of active values for this attribute.
075      private List<Long> activeValues;
076    
077      // The set of pending values for this attribute.
078      private List<Long> pendingValues;
079    
080      // Indicates whether this attribute will impose a lower bound for its values.
081      private boolean hasLowerBound;
082    
083      // Indicates whether this attribute will impose an upper bound for its values.
084      private boolean hasUpperBound;
085    
086      // The lower bound for values of this attribute.
087      private long lowerBound;
088    
089      // The upper bound for values of this attribute.
090      private long upperBound;
091    
092    
093    
094      /**
095       * Creates a new integer configuration attribute stub with the provided
096       * information but no values.  The values will be set using the
097       * <CODE>setInitialValue</CODE> method.
098       *
099       * @param  name                 The name for this configuration attribute.
100       * @param  description          The description for this configuration
101       *                              attribute.
102       * @param  isRequired           Indicates whether this configuration attribute
103       *                              is required to have at least one value.
104       * @param  isMultiValued        Indicates whether this configuration attribute
105       *                              may have multiple values.
106       * @param  requiresAdminAction  Indicates whether changes to this
107       *                              configuration attribute require administrative
108       *                              action before they will take effect.
109       * @param  hasLowerBound        Indicates whether a lower bound will be
110       *                              enforced for values of this attribute.
111       * @param  lowerBound           The lower bound that will be enforced for
112       *                              values of this attribute.
113       * @param  hasUpperBound        Indicates whether an upper bound will be
114       *                              enforced for values of this attribute.
115       * @param  upperBound           The upper bound that will be enforced for
116       *                              values of this attribute.
117       */
118      public IntegerConfigAttribute(String name, Message description,
119                                    boolean isRequired, boolean isMultiValued,
120                                    boolean requiresAdminAction,
121                                    boolean hasLowerBound, long lowerBound,
122                                    boolean hasUpperBound, long upperBound)
123      {
124        super(name, description, isRequired, isMultiValued, requiresAdminAction);
125    
126    
127        this.hasLowerBound = hasLowerBound;
128        this.lowerBound    = lowerBound;
129        this.hasUpperBound = hasUpperBound;
130        this.upperBound    = upperBound;
131    
132        activeValues  = new ArrayList<Long>();
133        pendingValues = activeValues;
134      }
135    
136    
137    
138      /**
139       * Creates a new integer configuration attribute with the provided
140       * information.  No validation will be performed on the provided value.
141       *
142       * @param  name                 The name for this configuration attribute.
143       * @param  description          The description for this configuration
144       *                              attribute.
145       * @param  isRequired           Indicates whether this configuration attribute
146       *                              is required to have at least one value.
147       * @param  isMultiValued        Indicates whether this configuration attribute
148       *                              may have multiple values.
149       * @param  requiresAdminAction  Indicates whether changes to this
150       *                              configuration attribute require administrative
151       *                              action before they will take effect.
152       * @param  hasLowerBound        Indicates whether a lower bound will be
153       *                              enforced for values of this attribute.
154       * @param  lowerBound           The lower bound that will be enforced for
155       *                              values of this attribute.
156       * @param  hasUpperBound        Indicates whether an upper bound will be
157       *                              enforced for values of this attribute.
158       * @param  upperBound           The upper bound that will be enforced for
159       *                              values of this attribute.
160       * @param  value                The value for this integer configuration
161       *                              attribute.
162       */
163      public IntegerConfigAttribute(String name, Message description,
164                                    boolean isRequired, boolean isMultiValued,
165                                    boolean requiresAdminAction,
166                                    boolean hasLowerBound, long lowerBound,
167                                    boolean hasUpperBound, long upperBound,
168                                    long value)
169      {
170        super(name, description, isRequired, isMultiValued, requiresAdminAction,
171              getValueSet(value));
172    
173    
174        this.hasLowerBound = hasLowerBound;
175        this.lowerBound    = lowerBound;
176        this.hasUpperBound = hasUpperBound;
177        this.upperBound    = upperBound;
178    
179        activeValues = new ArrayList<Long>(1);
180        activeValues.add(value);
181    
182        pendingValues = activeValues;
183      }
184    
185    
186    
187      /**
188       * Creates a new integer configuration attribute with the provided
189       * information.  No validation will be performed on the provided values.
190       *
191       * @param  name                 The name for this configuration attribute.
192       * @param  description          The description for this configuration
193       *                              attribute.
194       * @param  isRequired           Indicates whether this configuration attribute
195       *                              is required to have at least one value.
196       * @param  isMultiValued        Indicates whether this configuration attribute
197       *                              may have multiple values.
198       * @param  requiresAdminAction  Indicates whether changes to this
199       *                              configuration attribute require administrative
200       *                              action before they will take effect.
201       * @param  hasLowerBound        Indicates whether a lower bound will be
202       *                              enforced for values of this attribute.
203       * @param  lowerBound           The lower bound that will be enforced for
204       *                              values of this attribute.
205       * @param  hasUpperBound        Indicates whether an upper bound will be
206       *                              enforced for values of this attribute.
207       * @param  upperBound           The upper bound that will be enforced for
208       *                              values of this attribute.
209       * @param  values               The set of values for this configuration
210       *                              attribute.
211       */
212      public IntegerConfigAttribute(String name, Message description,
213                                    boolean isRequired, boolean isMultiValued,
214                                    boolean requiresAdminAction,
215                                    boolean hasLowerBound, long lowerBound,
216                                    boolean hasUpperBound, long upperBound,
217                                    List<Long> values)
218      {
219        super(name, description, isRequired, isMultiValued, requiresAdminAction,
220              getValueSet(values));
221    
222    
223        this.hasLowerBound = hasLowerBound;
224        this.lowerBound    = lowerBound;
225        this.hasUpperBound = hasUpperBound;
226        this.upperBound    = upperBound;
227    
228        if (values == null)
229        {
230          activeValues  = new ArrayList<Long>();
231          pendingValues = activeValues;
232        }
233        else
234        {
235          activeValues  = values;
236          pendingValues = activeValues;
237        }
238      }
239    
240    
241    
242      /**
243       * Creates a new integer configuration attribute with the provided
244       * information.  No validation will be performed on the provided values.
245       *
246       * @param  name                 The name for this configuration attribute.
247       * @param  description          The description for this configuration
248       *                              attribute.
249       * @param  isRequired           Indicates whether this configuration attribute
250       *                              is required to have at least one value.
251       * @param  isMultiValued        Indicates whether this configuration attribute
252       *                              may have multiple values.
253       * @param  requiresAdminAction  Indicates whether changes to this
254       *                              configuration attribute require administrative
255       *                              action before they will take effect.
256       * @param  hasLowerBound        Indicates whether a lower bound will be
257       *                              enforced for values of this attribute.
258       * @param  lowerBound           The lower bound that will be enforced for
259       *                              values of this attribute.
260       * @param  hasUpperBound        Indicates whether an upper bound will be
261       *                              enforced for values of this attribute.
262       * @param  upperBound           The upper bound that will be enforced for
263       *                              values of this attribute.
264       * @param  activeValues         The set of active values for this
265       *                              configuration attribute.
266       * @param  pendingValues        The set of pending values for this
267       *                              configuration attribute.
268       */
269      public IntegerConfigAttribute(String name, Message description,
270                                    boolean isRequired, boolean isMultiValued,
271                                    boolean requiresAdminAction,
272                                    boolean hasLowerBound, long lowerBound,
273                                    boolean hasUpperBound, long upperBound,
274                                    List<Long> activeValues,
275                                    List<Long> pendingValues)
276      {
277        super(name, description, isRequired, isMultiValued, requiresAdminAction,
278              getValueSet(activeValues), (pendingValues != null),
279              getValueSet(pendingValues));
280    
281    
282        this.hasLowerBound = hasLowerBound;
283        this.lowerBound    = lowerBound;
284        this.hasUpperBound = hasUpperBound;
285        this.upperBound    = upperBound;
286    
287        if (activeValues == null)
288        {
289          this.activeValues = new ArrayList<Long>();
290        }
291        else
292        {
293          this.activeValues = activeValues;
294        }
295    
296        if (pendingValues == null)
297        {
298          this.pendingValues = this.activeValues;
299        }
300        else
301        {
302          this.pendingValues = pendingValues;
303        }
304      }
305    
306    
307    
308      /**
309       * Retrieves the name of the data type for this configuration attribute.  This
310       * is for informational purposes (e.g., inclusion in method signatures and
311       * other kinds of descriptions) and does not necessarily need to map to an
312       * actual Java type.
313       *
314       * @return  The name of the data type for this configuration attribute.
315       */
316      public String getDataType()
317      {
318        return "Integer";
319      }
320    
321    
322    
323      /**
324       * Retrieves the attribute syntax for this configuration attribute.
325       *
326       * @return  The attribute syntax for this configuration attribute.
327       */
328      public AttributeSyntax getSyntax()
329      {
330        return DirectoryServer.getDefaultIntegerSyntax();
331      }
332    
333    
334    
335      /**
336       * Retrieves the active value for this configuration attribute as a long.
337       * This is only valid for single-valued attributes that have a value.
338       *
339       * @return  The active value for this configuration attribute as a long.
340       *
341       * @throws  ConfigException  If this attribute does not have exactly one
342       *                           active value.
343       */
344      public long activeValue()
345             throws ConfigException
346      {
347        if ((activeValues == null) || activeValues.isEmpty())
348        {
349          Message message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName());
350          throw new ConfigException(message);
351        }
352    
353        if (activeValues.size() > 1)
354        {
355          Message message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName());
356          throw new ConfigException(message);
357        }
358    
359        return activeValues.get(0);
360      }
361    
362    
363    
364      /**
365       * Retrieves the active value for this configuration attribute as an integer.
366       * This is only valid for single-valued attributes that have a value within
367       * the integer range.
368       *
369       * @return  The active value for this configuration attribute as an integer.
370       *
371       * @throws  ConfigException  If the active value of this attribute cannot be
372       *                           retrieved as an integer, including if there are
373       *                           no values, if there are multiple values, or if
374       *                           the value is not in the range of an integer.
375       */
376      public int activeIntValue()
377             throws ConfigException
378      {
379        if ((activeValues == null) || activeValues.isEmpty())
380        {
381          Message message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName());
382          throw new ConfigException(message);
383        }
384    
385        if (activeValues.size() > 1)
386        {
387          Message message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName());
388          throw new ConfigException(message);
389        }
390    
391        long longValue = activeValues.get(0);
392        int  intValue  = (int) longValue;
393        if (intValue == longValue)
394        {
395          return intValue;
396        }
397        else
398        {
399          Message message = ERR_CONFIG_ATTR_VALUE_OUT_OF_INT_RANGE.get(getName());
400          throw new ConfigException(message);
401        }
402      }
403    
404    
405    
406      /**
407       * Retrieves the set of active values for this configuration attribute.
408       *
409       * @return  The set of active values for this configuration attribute.
410       */
411      public List<Long> activeValues()
412      {
413        return activeValues;
414      }
415    
416    
417    
418      /**
419       * Retrieves the pending value for this configuration attribute as a long.
420       * This is only valid for single-valued attributes that have a value.  If this
421       * attribute does not have any pending values, then the active value will be
422       * returned.
423       *
424       * @return  The pending value for this configuration attribute as a long.
425       *
426       * @throws  ConfigException  If this attribute does not have exactly one
427       *                           pending value.
428       */
429      public long pendingValue()
430             throws ConfigException
431      {
432        if (! hasPendingValues())
433        {
434          return activeValue();
435        }
436    
437        if ((pendingValues == null) || pendingValues.isEmpty())
438        {
439          Message message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName());
440          throw new ConfigException(message);
441        }
442    
443        if (pendingValues.size() > 1)
444        {
445          Message message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName());
446          throw new ConfigException(message);
447        }
448    
449        return pendingValues.get(0);
450      }
451    
452    
453    
454      /**
455       * Retrieves the pending value for this configuration attribute as an integer.
456       * This is only valid for single-valued attributes that have a value within
457       * the integer range.  If this attribute does not have any pending values,
458       * then t he active value will be returned.
459       *
460       * @return  The pending value for this configuration attribute as an integer.
461       *
462       * @throws  ConfigException  If the pending value of this attribute cannot be
463       *                           retrieved as an integer, including if there are
464       *                           no values, if there are multiple values, or if
465       *                           the value is not in the range of an integer.
466       */
467      public int pendingIntValue()
468             throws ConfigException
469      {
470        if (! hasPendingValues())
471        {
472          return activeIntValue();
473        }
474    
475        if ((pendingValues == null) || pendingValues.isEmpty())
476        {
477          Message message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName());
478          throw new ConfigException(message);
479        }
480    
481        if (pendingValues.size() > 1)
482        {
483          Message message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName());
484          throw new ConfigException(message);
485        }
486    
487        long longValue = pendingValues.get(0);
488        int  intValue  = (int) longValue;
489        if (intValue == longValue)
490        {
491          return intValue;
492        }
493        else
494        {
495          Message message = ERR_CONFIG_ATTR_VALUE_OUT_OF_INT_RANGE.get(getName());
496          throw new ConfigException(message);
497        }
498      }
499    
500    
501    
502      /**
503       * Retrieves the set of pending values for this configuration attribute.  If
504       * there are no pending values, then the set of active values will be
505       * returned.
506       *
507       * @return  The set of pending values for this configuration attribute.
508       */
509      public List<Long> pendingValues()
510      {
511        if (! hasPendingValues())
512        {
513          return activeValues;
514        }
515    
516        return pendingValues;
517      }
518    
519    
520    
521      /**
522       * Indicates whether a lower bound will be enforced for the value of this
523       * configuration attribute.
524       *
525       * @return  <CODE>true</CODE> if a lower bound will be enforced for the
526       *          value of this configuration attribute, or <CODE>false</CODE> if
527       *          not.
528       */
529      public boolean hasLowerBound()
530      {
531        return hasLowerBound;
532      }
533    
534    
535    
536      /**
537       * Retrieves the lower bound for the value of this configuration attribute.
538       *
539       * @return  The lower bound for the value of this configuration attribute.
540       */
541      public long getLowerBound()
542      {
543        return lowerBound;
544      }
545    
546    
547    
548      /**
549       * Indicates whether an upper bound will be enforced for the calculated value
550       * of this configuration attribute.
551       *
552       * @return  <CODE>true</CODE> if an upper bound will be enforced for the
553       *          calculated value of this configuration attribute, or
554       *          <CODE>false</CODE> if not.
555       */
556      public boolean hasUpperBound()
557      {
558        return hasUpperBound;
559      }
560    
561    
562    
563      /**
564       * Retrieves the upper bound for the calculated value of this configuration
565       * attribute.
566       *
567       * @return  The upper bound for the calculated value of this configuration
568       *          attribute.
569       */
570      public long getUpperBound()
571      {
572        return upperBound;
573      }
574    
575    
576    
577      /**
578       * Sets the value for this integer configuration attribute.
579       *
580       * @param  value  The value for this integer configuration attribute.
581       *
582       * @throws  ConfigException  If the provided value is not acceptable.
583       */
584      public void setValue(long value)
585             throws ConfigException
586      {
587        if (hasLowerBound && (value < lowerBound))
588        {
589          Message message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
590              getName(), value, lowerBound);
591          throw new ConfigException(message);
592        }
593    
594        if (hasUpperBound && (value > upperBound))
595        {
596          Message message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
597              getName(), value, upperBound);
598          throw new ConfigException(message);
599        }
600    
601        if (requiresAdminAction())
602        {
603          pendingValues = new ArrayList<Long>(1);
604          pendingValues.add(value);
605          setPendingValues(getValueSet(value));
606        }
607        else
608        {
609          activeValues.clear();
610          activeValues.add(value);
611          pendingValues = activeValues;
612          setActiveValues(getValueSet(value));
613        }
614      }
615    
616    
617    
618      /**
619       * Sets the values for this integer configuration attribute.
620       *
621       * @param  values  The set of values for this integer configuration attribute.
622       *
623       * @throws  ConfigException  If the provided value set or any of the
624       *                           individual values are not acceptable.
625       */
626      public void setValues(List<Long> values)
627             throws ConfigException
628      {
629        // First check if the set is empty and if that is allowed.
630        if ((values == null) || (values.isEmpty()))
631        {
632          if (isRequired())
633          {
634            Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
635            throw new ConfigException(message);
636          }
637          else
638          {
639            if (requiresAdminAction())
640            {
641              setPendingValues(new LinkedHashSet<AttributeValue>(0));
642              pendingValues = new ArrayList<Long>();
643            }
644            else
645            {
646              setActiveValues(new LinkedHashSet<AttributeValue>(0));
647              activeValues.clear();
648            }
649          }
650        }
651    
652    
653        // Next check if the set contains multiple values and if that is allowed.
654        int numValues = values.size();
655        if ((! isMultiValued()) && (numValues > 1))
656        {
657          Message message =
658              ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName());
659          throw new ConfigException(message);
660        }
661    
662    
663        // Iterate through all the provided values, make sure that they are
664        // acceptable, and build the value set.
665        LinkedHashSet<AttributeValue> valueSet =
666             new LinkedHashSet<AttributeValue>(numValues);
667        for (long value : values)
668        {
669          if (hasLowerBound && (value < lowerBound))
670          {
671            Message message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
672                getName(), value, lowerBound);
673            throw new ConfigException(message);
674          }
675    
676          if (hasUpperBound && (value > upperBound))
677          {
678            Message message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
679                getName(), value, upperBound);
680            throw new ConfigException(message);
681          }
682    
683          String valueString = String.valueOf(value);
684          AttributeValue attrValue =
685               new AttributeValue(new ASN1OctetString(valueString),
686                                  new ASN1OctetString(valueString));
687    
688          if (valueSet.contains(attrValue))
689          {
690            Message message = ERR_CONFIG_ATTR_ADD_VALUES_ALREADY_EXISTS.get(
691                getName(), valueString);
692            throw new ConfigException(message);
693          }
694    
695          valueSet.add(attrValue);
696        }
697    
698    
699        // Apply this value set to the new active or pending value set.
700        if (requiresAdminAction())
701        {
702          pendingValues = values;
703          setPendingValues(valueSet);
704        }
705        else
706        {
707          activeValues  = values;
708          pendingValues = activeValues;
709          setActiveValues(valueSet);
710        }
711      }
712    
713    
714    
715      /**
716       * Creates the appropriate value set with the provided value.
717       *
718       * @param  value  The value to use to create the value set.
719       *
720       * @return  The constructed value set.
721       */
722      private static LinkedHashSet<AttributeValue> getValueSet(long value)
723      {
724        LinkedHashSet<AttributeValue> valueSet =
725             new LinkedHashSet<AttributeValue>(1);
726    
727        String valueString = String.valueOf(value);
728        valueSet.add(new AttributeValue(new ASN1OctetString(valueString),
729                                        new ASN1OctetString(valueString)));
730    
731        return valueSet;
732      }
733    
734    
735    
736      /**
737       * Creates the appropriate value set with the provided values.
738       *
739       * @param  values  The values to use to create the value set.
740       *
741       * @return  The constructed value set.
742       */
743      private static LinkedHashSet<AttributeValue> getValueSet(List<Long> values)
744      {
745        if (values == null)
746        {
747          return null;
748        }
749    
750        LinkedHashSet<AttributeValue> valueSet =
751             new LinkedHashSet<AttributeValue>(values.size());
752    
753        for (long value : values)
754        {
755          String valueString = String.valueOf(value);
756          valueSet.add(new AttributeValue(new ASN1OctetString(valueString),
757                                          new ASN1OctetString(valueString)));
758        }
759    
760        return valueSet;
761      }
762    
763    
764    
765      /**
766       * Applies the set of pending values, making them the active values for this
767       * configuration attribute.  This will not take any action if there are no
768       * pending values.
769       */
770      public void applyPendingValues()
771      {
772        if (! hasPendingValues())
773        {
774          return;
775        }
776    
777        super.applyPendingValues();
778        activeValues = pendingValues;
779      }
780    
781    
782    
783      /**
784       * Indicates whether the provided value is acceptable for use in this
785       * attribute.  If it is not acceptable, then the reason should be written into
786       * the provided buffer.
787       *
788       * @param  value         The value for which to make the determination.
789       * @param  rejectReason  A buffer into which a human-readable reason for the
790       *                       reject may be written.
791       *
792       * @return  <CODE>true</CODE> if the provided value is acceptable for use in
793       *          this attribute, or <CODE>false</CODE> if not.
794       */
795      public boolean valueIsAcceptable(AttributeValue value,
796                                       StringBuilder rejectReason)
797      {
798        // First, make sure we can represent it as a long.
799        String stringValue = value.getStringValue();
800        long longValue;
801        try
802        {
803          longValue = Long.parseLong(stringValue);
804        }
805        catch (Exception e)
806        {
807          if (debugEnabled())
808          {
809            TRACER.debugCaught(DebugLogLevel.ERROR, e);
810          }
811    
812          rejectReason.append(ERR_CONFIG_ATTR_INVALID_INT_VALUE.get(
813                  getName(), stringValue, String.valueOf(e)));
814          return false;
815        }
816    
817    
818        // Perform any necessary bounds checking.
819        if (hasLowerBound && (longValue < lowerBound))
820        {
821          rejectReason.append(ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
822                  getName(), longValue, lowerBound));
823          return false;
824        }
825    
826        if (hasUpperBound && (longValue > upperBound))
827        {
828          rejectReason.append(ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
829                  getName(), longValue, upperBound));
830          return false;
831        }
832    
833    
834        // If we've gotten here, then the value must be acceptable.
835        return true;
836      }
837    
838    
839    
840      /**
841       * Converts the provided set of strings to a corresponding set of attribute
842       * values.
843       *
844       * @param  valueStrings   The set of strings to be converted into attribute
845       *                        values.
846       * @param  allowFailures  Indicates whether the decoding process should allow
847       *                        any failures in which one or more values could be
848       *                        decoded but at least one could not.  If this is
849       *                        <CODE>true</CODE> and such a condition is acceptable
850       *                        for the underlying attribute type, then the returned
851       *                        set of values should simply not include those
852       *                        undecodable values.
853       *
854       * @return  The set of attribute values converted from the provided strings.
855       *
856       * @throws  ConfigException  If an unrecoverable problem occurs while
857       *                           performing the conversion.
858       */
859      public LinkedHashSet<AttributeValue>
860                  stringsToValues(List<String> valueStrings,
861                                  boolean allowFailures)
862             throws ConfigException
863      {
864        if ((valueStrings == null) || valueStrings.isEmpty())
865        {
866          if (isRequired())
867          {
868            Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
869            throw new ConfigException(message);
870          }
871          else
872          {
873            return new LinkedHashSet<AttributeValue>();
874          }
875        }
876    
877    
878        int numValues = valueStrings.size();
879        if ((! isMultiValued()) && (numValues > 1))
880        {
881          Message message =
882              ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName());
883          throw new ConfigException(message);
884        }
885    
886    
887        LinkedHashSet<AttributeValue> valueSet =
888             new LinkedHashSet<AttributeValue>(numValues);
889        for (String valueString : valueStrings)
890        {
891          long longValue;
892          try
893          {
894            longValue = Long.parseLong(valueString);
895          }
896          catch (Exception e)
897          {
898            if (debugEnabled())
899            {
900              TRACER.debugCaught(DebugLogLevel.ERROR, e);
901            }
902    
903            Message message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
904                    valueString, getName(),
905                    String.valueOf(e));
906    
907            if (allowFailures)
908            {
909              ErrorLogger.logError(message);
910              continue;
911            }
912            else
913            {
914              throw new ConfigException(message);
915            }
916          }
917    
918    
919          if (hasLowerBound && (longValue < lowerBound))
920          {
921    
922            Message message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
923                    getName(), longValue, lowerBound);
924            if (allowFailures)
925            {
926              ErrorLogger.logError(message);
927              continue;
928            }
929            else
930            {
931              throw new ConfigException(message);
932            }
933          }
934    
935    
936          if (hasUpperBound && (longValue > upperBound))
937          {
938            Message message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
939                    getName(), longValue, upperBound);
940    
941            if (allowFailures)
942            {
943              ErrorLogger.logError(message);
944              continue;
945            }
946            else
947            {
948              throw new ConfigException(message);
949            }
950          }
951    
952    
953          valueSet.add(new AttributeValue(new ASN1OctetString(valueString),
954                                          new ASN1OctetString(valueString)));
955        }
956    
957    
958        // If this method was configured to continue on error, then it is possible
959        // that we ended up with an empty list.  Check to see if this is a required
960        // attribute and if so deal with it accordingly.
961        if ((isRequired()) && valueSet.isEmpty())
962        {
963          Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
964          throw new ConfigException(message);
965        }
966    
967    
968        return valueSet;
969      }
970    
971    
972    
973      /**
974       * Converts the set of active values for this configuration attribute into a
975       * set of strings that may be stored in the configuration or represented over
976       * protocol.  The string representation used by this method should be
977       * compatible with the decoding used by the <CODE>stringsToValues</CODE>
978       * method.
979       *
980       * @return  The string representations of the set of active values for this
981       *          configuration attribute.
982       */
983      public List<String> activeValuesToStrings()
984      {
985        ArrayList<String> valueStrings =
986             new ArrayList<String>(activeValues.size());
987        for (long l : activeValues)
988        {
989          valueStrings.add(String.valueOf(l));
990        }
991    
992        return valueStrings;
993      }
994    
995    
996    
997      /**
998       * Converts the set of pending values for this configuration attribute into a
999       * set of strings that may be stored in the configuration or represented over
1000       * protocol.  The string representation used by this method should be
1001       * compatible with the decoding used by the <CODE>stringsToValues</CODE>
1002       * method.
1003       *
1004       * @return  The string representations of the set of pending values for this
1005       *          configuration attribute, or <CODE>null</CODE> if there are no
1006       *          pending values.
1007       */
1008      public List<String> pendingValuesToStrings()
1009      {
1010        if (hasPendingValues())
1011        {
1012          ArrayList<String> valueStrings =
1013               new ArrayList<String>(pendingValues.size());
1014          for (long l : pendingValues)
1015          {
1016            valueStrings.add(String.valueOf(l));
1017          }
1018    
1019          return valueStrings;
1020        }
1021        else
1022        {
1023          return null;
1024        }
1025      }
1026    
1027    
1028    
1029      /**
1030       * Retrieves a new configuration attribute of this type that will contain the
1031       * values from the provided attribute.
1032       *
1033       * @param  attributeList  The list of attributes to use to create the config
1034       *                        attribute.  The list must contain either one or two
1035       *                        elements, with both attributes having the same base
1036       *                        name and the only option allowed is ";pending" and
1037       *                        only if this attribute is one that requires admin
1038       *                        action before a change may take effect.
1039       *
1040       * @return  The generated configuration attribute.
1041       *
1042       * @throws  ConfigException  If the provided attribute cannot be treated as a
1043       *                           configuration attribute of this type (e.g., if
1044       *                           one or more of the values of the provided
1045       *                           attribute are not suitable for an attribute of
1046       *                           this type, or if this configuration attribute is
1047       *                           single-valued and the provided attribute has
1048       *                           multiple values).
1049       */
1050      public ConfigAttribute getConfigAttribute(List<Attribute> attributeList)
1051             throws ConfigException
1052      {
1053        ArrayList<Long> activeValues  = null;
1054        ArrayList<Long> pendingValues = null;
1055    
1056        for (Attribute a : attributeList)
1057        {
1058          if (a.hasOptions())
1059          {
1060            // This must be the pending value.
1061            if (a.hasOption(OPTION_PENDING_VALUES))
1062            {
1063              if (pendingValues != null)
1064              {
1065                // We cannot have multiple pending value sets.
1066                Message message =
1067                    ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName());
1068                throw new ConfigException(message);
1069              }
1070    
1071    
1072              LinkedHashSet<AttributeValue> values = a.getValues();
1073              if (values.isEmpty())
1074              {
1075                if (isRequired())
1076                {
1077                  // This is illegal -- it must have a value.
1078                  Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
1079                  throw new ConfigException(message);
1080                }
1081                else
1082                {
1083                  // This is fine.  The pending value set can be empty.
1084                  pendingValues = new ArrayList<Long>(0);
1085                }
1086              }
1087              else
1088              {
1089                int numValues = values.size();
1090                if ((numValues > 1) && (! isMultiValued()))
1091                {
1092                  // This is illegal -- the attribute is single-valued.
1093                  Message message =
1094                      ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
1095                  throw new ConfigException(message);
1096                }
1097    
1098                pendingValues = new ArrayList<Long>(numValues);
1099                for (AttributeValue v : values)
1100                {
1101                  long longValue;
1102                  try
1103                  {
1104                    longValue = Long.parseLong(v.getStringValue());
1105                  }
1106                  catch (Exception e)
1107                  {
1108                    Message message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
1109                        v.getStringValue(), a.getName(), String.valueOf(e));
1110                    throw new ConfigException(message, e);
1111                  }
1112    
1113    
1114                  // Check the bounds set for this attribute.
1115                  if (hasLowerBound && (longValue < lowerBound))
1116                  {
1117                    Message message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
1118                        a.getName(), longValue, lowerBound);
1119                    throw new ConfigException(message);
1120                  }
1121    
1122                  if (hasUpperBound && (longValue > upperBound))
1123                  {
1124                    Message message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
1125                        a.getName(), longValue, upperBound);
1126                    throw new ConfigException(message);
1127                  }
1128    
1129                  pendingValues.add(longValue);
1130                }
1131              }
1132            }
1133            else
1134            {
1135              // This is illegal -- only the pending option is allowed for
1136              // configuration attributes.
1137              Message message =
1138                  ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get(
1139                          a.getName());
1140              throw new ConfigException(message);
1141            }
1142          }
1143          else
1144          {
1145            // This must be the active value.
1146            if (activeValues!= null)
1147            {
1148              // We cannot have multiple active value sets.
1149              Message message =
1150                  ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName());
1151              throw new ConfigException(message);
1152            }
1153    
1154    
1155            LinkedHashSet<AttributeValue> values = a.getValues();
1156            if (values.isEmpty())
1157            {
1158              if (isRequired())
1159              {
1160                // This is illegal -- it must have a value.
1161                Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
1162                throw new ConfigException(message);
1163              }
1164              else
1165              {
1166                // This is fine.  The active value set can be empty.
1167                activeValues = new ArrayList<Long>(0);
1168              }
1169            }
1170            else
1171            {
1172              int numValues = values.size();
1173              if ((numValues > 1) && (! isMultiValued()))
1174              {
1175                // This is illegal -- the attribute is single-valued.
1176                Message message =
1177                    ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
1178                throw new ConfigException(message);
1179              }
1180    
1181              activeValues = new ArrayList<Long>(numValues);
1182              for (AttributeValue v : values)
1183              {
1184                long longValue;
1185                try
1186                {
1187                  longValue = Long.parseLong(v.getStringValue());
1188                }
1189                catch (Exception e)
1190                {
1191                  Message message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
1192                      v.getStringValue(), a.getName(), String.valueOf(e));
1193                  throw new ConfigException(message, e);
1194                }
1195    
1196    
1197                // Check the bounds set for this attribute.
1198                if (hasLowerBound && (longValue < lowerBound))
1199                {
1200                  Message message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(
1201                      a.getName(), longValue, lowerBound);
1202                  throw new ConfigException(message);
1203                }
1204    
1205                if (hasUpperBound && (longValue > upperBound))
1206                {
1207                  Message message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(
1208                      a.getName(), longValue, upperBound);
1209                  throw new ConfigException(message);
1210                }
1211    
1212                activeValues.add(longValue);
1213              }
1214            }
1215          }
1216        }
1217    
1218        if (activeValues == null)
1219        {
1220          // This is not OK.  The value set must contain an active value.
1221          Message message = ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName());
1222          throw new ConfigException(message);
1223        }
1224    
1225        if (pendingValues == null)
1226        {
1227          // This is OK.  We'll just use the active value set.
1228          pendingValues = activeValues;
1229        }
1230    
1231    
1232        return new IntegerConfigAttribute(getName(), getDescription(), isRequired(),
1233                                          isMultiValued(), requiresAdminAction(),
1234                                          hasLowerBound, lowerBound, hasUpperBound,
1235                                          upperBound, activeValues, pendingValues);
1236      }
1237    
1238    
1239    
1240      /**
1241       * Retrieves a JMX attribute containing the value set for this
1242       * configuration attribute (active or pending).
1243       *
1244       * @param pending indicates if pending or active  values are required.
1245       *
1246       * @return  A JMX attribute containing the active value set for this
1247       *          configuration attribute, or <CODE>null</CODE> if it does not have
1248       *          any active values.
1249       */
1250      private javax.management.Attribute _toJMXAttribute(boolean pending)
1251      {
1252        List<Long> requestedValues ;
1253        String name ;
1254        if (pending)
1255        {
1256            requestedValues = pendingValues ;
1257            name = getName() + ";" + OPTION_PENDING_VALUES ;
1258        }
1259        else
1260        {
1261            requestedValues = activeValues ;
1262            name = getName() ;
1263        }
1264    
1265        if (isMultiValued())
1266        {
1267          long[] values = new long[requestedValues.size()];
1268          for (int i=0; i < values.length; i++)
1269          {
1270            values[i] = requestedValues.get(i);
1271          }
1272    
1273          return new javax.management.Attribute(name, values);
1274        }
1275        else
1276        {
1277          if (requestedValues.isEmpty())
1278          {
1279            return null;
1280          }
1281          else
1282          {
1283            return new javax.management.Attribute(name, requestedValues.get(0));
1284          }
1285        }
1286      }
1287    
1288    
1289      /**
1290       * Retrieves a JMX attribute containing the active value set for this
1291       * configuration attribute.
1292       *
1293       * @return  A JMX attribute containing the active value set for this
1294       *          configuration attribute, or <CODE>null</CODE> if it does not have
1295       *          any active values.
1296       */
1297      public javax.management.Attribute toJMXAttribute()
1298        {
1299    
1300            return _toJMXAttribute(false);
1301        }
1302    
1303      /**
1304       * Retrieves a JMX attribute containing the pending value set for this
1305       * configuration attribute.
1306       *
1307       * @return  A JMX attribute containing the pending value set for this
1308       *          configuration attribute.
1309       */
1310      public  javax.management.Attribute toJMXAttributePending()
1311      {
1312          return _toJMXAttribute(true);
1313      }
1314    
1315    
1316    
1317      /**
1318       * Adds information about this configuration attribute to the provided JMX
1319       * attribute list.  If this configuration attribute requires administrative
1320       * action before changes take effect and it has a set of pending values, then
1321       * two attributes should be added to the list -- one for the active value
1322       * and one for the pending value.  The pending value should be named with
1323       * the pending option.
1324       *
1325       * @param  attributeList  The attribute list to which the JMX attribute(s)
1326       *                        should be added.
1327       */
1328      public void toJMXAttribute(AttributeList attributeList)
1329      {
1330        if (activeValues.size() > 0)
1331        {
1332          if (isMultiValued())
1333          {
1334            long[] values = new long[activeValues.size()];
1335            for (int i=0; i < values.length; i++)
1336            {
1337              values[i] = activeValues.get(i);
1338            }
1339    
1340            attributeList.add(new javax.management.Attribute(getName(), values));
1341          }
1342          else
1343          {
1344            attributeList.add(new javax.management.Attribute(getName(),
1345                                                             activeValues.get(0)));
1346          }
1347        }
1348        else
1349        {
1350          if (isMultiValued())
1351          {
1352            attributeList.add(new javax.management.Attribute(getName(),
1353                                                             new String[0]));
1354          }
1355          else
1356          {
1357            attributeList.add(new javax.management.Attribute(getName(), null));
1358          }
1359        }
1360    
1361    
1362        if (requiresAdminAction() && (pendingValues != null) &&
1363            (pendingValues != activeValues))
1364        {
1365          String name = getName() + ";" + OPTION_PENDING_VALUES;
1366    
1367          if (isMultiValued())
1368          {
1369            long[] values = new long[pendingValues.size()];
1370            for (int i=0; i < values.length; i++)
1371            {
1372              values[i] = pendingValues.get(i);
1373            }
1374    
1375            attributeList.add(new javax.management.Attribute(name, values));
1376          }
1377          else if (! pendingValues.isEmpty())
1378          {
1379            attributeList.add(new javax.management.Attribute(name,
1380                                                             pendingValues.get(0)));
1381          }
1382        }
1383      }
1384    
1385    
1386    
1387      /**
1388       * Adds information about this configuration attribute to the provided list in
1389       * the form of a JMX <CODE>MBeanAttributeInfo</CODE> object.  If this
1390       * configuration attribute requires administrative action before changes take
1391       * effect and it has a set of pending values, then two attribute info objects
1392       * should be added to the list -- one for the active value (which should be
1393       * read-write) and one for the pending value (which should be read-only).  The
1394       * pending value should be named with the pending option.
1395       *
1396       * @param  attributeInfoList  The list to which the attribute information
1397       *                            should be added.
1398       */
1399      public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList)
1400      {
1401        if (isMultiValued())
1402        {
1403          attributeInfoList.add(new MBeanAttributeInfo(getName(),
1404                                                       JMX_TYPE_LONG_ARRAY,
1405                                                       String.valueOf(
1406                                                               getDescription()),
1407                                                       true, true, false));
1408        }
1409        else
1410        {
1411          attributeInfoList.add(new MBeanAttributeInfo(getName(),
1412                                                       Long.class.getName(),
1413                                                       String.valueOf(
1414                                                               getDescription()),
1415                                                       true, true, false));
1416        }
1417    
1418    
1419        if (requiresAdminAction())
1420        {
1421          String name = getName() + ";" + OPTION_PENDING_VALUES;
1422    
1423          if (isMultiValued())
1424          {
1425            attributeInfoList.add(new MBeanAttributeInfo(name, JMX_TYPE_LONG_ARRAY,
1426                                                         String.valueOf(
1427                                                                 getDescription()),
1428                                                         true, false, false));
1429          }
1430          else
1431          {
1432            attributeInfoList.add(new MBeanAttributeInfo(name, Long.class.getName(),
1433                                                         String.valueOf(
1434                                                                 getDescription()),
1435                                                         true, false, false));
1436          }
1437        }
1438      }
1439    
1440    
1441    
1442      /**
1443       * Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this
1444       * configuration attribute.
1445       *
1446       * @return  A JMX <CODE>MBeanParameterInfo</CODE> object that describes this
1447       *          configuration attribute.
1448       */
1449      public MBeanParameterInfo toJMXParameterInfo()
1450      {
1451        if (isMultiValued())
1452        {
1453          return new MBeanParameterInfo(getName(), JMX_TYPE_LONG_ARRAY,
1454                                        String.valueOf(getDescription()));
1455        }
1456        else
1457        {
1458          return new MBeanParameterInfo(getName(), Long.TYPE.getName(),
1459                                        String.valueOf(getDescription()));
1460        }
1461      }
1462    
1463    
1464    
1465      /**
1466       * Attempts to set the value of this configuration attribute based on the
1467       * information in the provided JMX attribute.
1468       *
1469       * @param  jmxAttribute  The JMX attribute to use to attempt to set the value
1470       *                       of this configuration attribute.
1471       *
1472       * @throws  ConfigException  If the provided JMX attribute does not have an
1473       *                           acceptable value for this configuration
1474       *                           attribute.
1475       */
1476      public void setValue(javax.management.Attribute jmxAttribute)
1477             throws ConfigException
1478      {
1479        Object value = jmxAttribute.getValue();
1480        if (value instanceof Long)
1481        {
1482          setValue(((Long) value).longValue());
1483        }
1484        else if (value instanceof Integer)
1485        {
1486          setValue(((Integer) value).intValue());
1487        }
1488        else if (value instanceof String)
1489        {
1490          try
1491          {
1492            setValue(Long.parseLong((String) value));
1493          }
1494          catch (Exception e)
1495          {
1496            if (debugEnabled())
1497            {
1498              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1499            }
1500    
1501            Message message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
1502                String.valueOf(value), getName(), String.valueOf(e));
1503            throw new ConfigException(message, e);
1504          }
1505        }
1506        else if (value.getClass().isArray())
1507        {
1508          String componentType = value.getClass().getComponentType().getName();
1509          int length = Array.getLength(value);
1510    
1511          try
1512          {
1513            if (componentType.equals(Long.class.getName()))
1514            {
1515              ArrayList<Long> values = new ArrayList<Long>();
1516    
1517              for (int i=0; i < length; i++)
1518              {
1519                values.add(Array.getLong(value, i));
1520              }
1521    
1522              setValues(values);
1523            }
1524            else if (componentType.equals(Integer.class.getName()))
1525            {
1526              ArrayList<Long> values = new ArrayList<Long>();
1527    
1528              for (int i=0; i < length; i++)
1529              {
1530                values.add((long) Array.getInt(value, i));
1531              }
1532    
1533              setValues(values);
1534            }
1535            else if (componentType.equals(String.class.getName()))
1536            {
1537              ArrayList<Long> values = new ArrayList<Long>();
1538    
1539              for (int i=0; i < length; i++)
1540              {
1541                String s = (String) Array.get(value, i);
1542                values.add(Long.parseLong(s));
1543              }
1544    
1545              setValues(values);
1546            }
1547            else
1548            {
1549              Message message =
1550                  ERR_CONFIG_ATTR_INT_INVALID_ARRAY_TYPE.get(
1551                          jmxAttribute.getName(), componentType);
1552              throw new ConfigException(message);
1553            }
1554          }
1555          catch (ConfigException ce)
1556          {
1557            if (debugEnabled())
1558            {
1559              TRACER.debugCaught(DebugLogLevel.ERROR, ce);
1560            }
1561    
1562            throw ce;
1563          }
1564          catch (Exception e)
1565          {
1566            if (debugEnabled())
1567            {
1568              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1569            }
1570    
1571            Message message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(
1572                componentType + "[" + length + "]", getName(), String.valueOf(e));
1573            throw new ConfigException(message, e);
1574          }
1575        }
1576        else
1577        {
1578          Message message = ERR_CONFIG_ATTR_INT_INVALID_TYPE.get(
1579              String.valueOf(value), getName(), value.getClass().getName());
1580          throw new ConfigException(message);
1581        }
1582      }
1583    
1584    
1585    
1586      /**
1587       * Creates a duplicate of this configuration attribute.
1588       *
1589       * @return  A duplicate of this configuration attribute.
1590       */
1591      public ConfigAttribute duplicate()
1592      {
1593        return new IntegerConfigAttribute(getName(), getDescription(), isRequired(),
1594                                          isMultiValued(), requiresAdminAction(),
1595                                          hasLowerBound, lowerBound, hasUpperBound,
1596                                          upperBound, activeValues, pendingValues);
1597      }
1598    }
1599