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