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