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