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