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