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-2016 ForgeRock AS. 016 */ 017package org.opends.server.config; 018 019import java.lang.reflect.Array; 020import java.util.ArrayList; 021import java.util.LinkedHashSet; 022import java.util.List; 023 024import javax.management.AttributeList; 025import javax.management.MBeanAttributeInfo; 026import javax.management.MBeanParameterInfo; 027 028import org.forgerock.i18n.LocalizableMessage; 029import org.forgerock.i18n.slf4j.LocalizedLogger; 030import org.forgerock.opendj.ldap.ByteString; 031import org.forgerock.opendj.ldap.schema.Syntax; 032import org.opends.server.core.DirectoryServer; 033import org.opends.server.types.Attribute; 034import org.opends.server.util.CollectionUtils; 035 036import static org.opends.server.config.ConfigConstants.*; 037import static org.opends.messages.ConfigMessages.*; 038 039/** 040 * This class defines an integer configuration attribute, which can hold zero or 041 * more integer values. For scalability, the actual values will be stored as 042 * <CODE>long</CODE> elements, although it will be possible to interact with 043 * them as integers in cases where that scalability is not required. 044 */ 045@org.opends.server.types.PublicAPI( 046 stability=org.opends.server.types.StabilityLevel.VOLATILE, 047 mayInstantiate=true, 048 mayExtend=false, 049 mayInvoke=true) 050public final class IntegerConfigAttribute 051 extends ConfigAttribute 052{ 053 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 054 055 /** The set of active values for this attribute. */ 056 private List<Long> activeValues; 057 /** The set of pending values for this attribute. */ 058 private List<Long> pendingValues; 059 /** Indicates whether this attribute will impose a lower bound for its values. */ 060 private boolean hasLowerBound; 061 /** Indicates whether this attribute will impose an upper bound for its values. */ 062 private boolean hasUpperBound; 063 /** The lower bound for values of this attribute. */ 064 private long lowerBound; 065 /** The upper bound for values of this attribute. */ 066 private long upperBound; 067 068 /** 069 * Creates a new integer configuration attribute stub with the provided 070 * information but no values. The values will be set using the 071 * <CODE>setInitialValue</CODE> method. 072 * 073 * @param name The name for this configuration attribute. 074 * @param description The description for this configuration 075 * attribute. 076 * @param isRequired Indicates whether this configuration attribute 077 * is required to have at least one value. 078 * @param isMultiValued Indicates whether this configuration attribute 079 * may have multiple values. 080 * @param requiresAdminAction Indicates whether changes to this 081 * configuration attribute require administrative 082 * action before they will take effect. 083 * @param hasLowerBound Indicates whether a lower bound will be 084 * enforced for values of this attribute. 085 * @param lowerBound The lower bound that will be enforced for 086 * values of this attribute. 087 * @param hasUpperBound Indicates whether an upper bound will be 088 * enforced for values of this attribute. 089 * @param upperBound The upper bound that will be enforced for 090 * values of this attribute. 091 */ 092 public IntegerConfigAttribute(String name, LocalizableMessage description, 093 boolean isRequired, boolean isMultiValued, 094 boolean requiresAdminAction, 095 boolean hasLowerBound, long lowerBound, 096 boolean hasUpperBound, long upperBound) 097 { 098 super(name, description, isRequired, isMultiValued, requiresAdminAction); 099 100 this.hasLowerBound = hasLowerBound; 101 this.lowerBound = lowerBound; 102 this.hasUpperBound = hasUpperBound; 103 this.upperBound = upperBound; 104 105 activeValues = new ArrayList<>(); 106 pendingValues = activeValues; 107 } 108 109 /** 110 * Creates a new integer configuration attribute with the provided 111 * information. No validation will be performed on the provided value. 112 * 113 * @param name The name for this configuration attribute. 114 * @param description The description for this configuration 115 * attribute. 116 * @param isRequired Indicates whether this configuration attribute 117 * is required to have at least one value. 118 * @param isMultiValued Indicates whether this configuration attribute 119 * may have multiple values. 120 * @param requiresAdminAction Indicates whether changes to this 121 * configuration attribute require administrative 122 * action before they will take effect. 123 * @param hasLowerBound Indicates whether a lower bound will be 124 * enforced for values of this attribute. 125 * @param lowerBound The lower bound that will be enforced for 126 * values of this attribute. 127 * @param hasUpperBound Indicates whether an upper bound will be 128 * enforced for values of this attribute. 129 * @param upperBound The upper bound that will be enforced for 130 * values of this attribute. 131 * @param value The value for this integer configuration 132 * attribute. 133 */ 134 public IntegerConfigAttribute(String name, LocalizableMessage description, 135 boolean isRequired, boolean isMultiValued, 136 boolean requiresAdminAction, 137 boolean hasLowerBound, long lowerBound, 138 boolean hasUpperBound, long upperBound, 139 long value) 140 { 141 super(name, description, isRequired, isMultiValued, requiresAdminAction, 142 getLongValueSet(value)); 143 144 this.hasLowerBound = hasLowerBound; 145 this.lowerBound = lowerBound; 146 this.hasUpperBound = hasUpperBound; 147 this.upperBound = upperBound; 148 149 activeValues = CollectionUtils.newArrayList(value); 150 pendingValues = activeValues; 151 } 152 153 /** 154 * Creates a new integer configuration attribute with the provided 155 * information. No validation will be performed on the provided values. 156 * 157 * @param name The name for this configuration attribute. 158 * @param description The description for this configuration 159 * attribute. 160 * @param isRequired Indicates whether this configuration attribute 161 * is required to have at least one value. 162 * @param isMultiValued Indicates whether this configuration attribute 163 * may have multiple values. 164 * @param requiresAdminAction Indicates whether changes to this 165 * configuration attribute require administrative 166 * action before they will take effect. 167 * @param hasLowerBound Indicates whether a lower bound will be 168 * enforced for values of this attribute. 169 * @param lowerBound The lower bound that will be enforced for 170 * values of this attribute. 171 * @param hasUpperBound Indicates whether an upper bound will be 172 * enforced for values of this attribute. 173 * @param upperBound The upper bound that will be enforced for 174 * values of this attribute. 175 * @param values The set of values for this configuration 176 * attribute. 177 */ 178 public IntegerConfigAttribute(String name, LocalizableMessage description, 179 boolean isRequired, boolean isMultiValued, 180 boolean requiresAdminAction, 181 boolean hasLowerBound, long lowerBound, 182 boolean hasUpperBound, long upperBound, 183 List<Long> values) 184 { 185 super(name, description, isRequired, isMultiValued, requiresAdminAction, 186 getLongValueSet(values)); 187 188 this.hasLowerBound = hasLowerBound; 189 this.lowerBound = lowerBound; 190 this.hasUpperBound = hasUpperBound; 191 this.upperBound = upperBound; 192 193 activeValues = values != null ? values : new ArrayList<Long>(); 194 pendingValues = activeValues; 195 } 196 197 /** 198 * Creates a new integer configuration attribute with the provided 199 * information. No validation will be performed on the provided values. 200 * 201 * @param name The name for this configuration attribute. 202 * @param description The description for this configuration 203 * attribute. 204 * @param isRequired Indicates whether this configuration attribute 205 * is required to have at least one value. 206 * @param isMultiValued Indicates whether this configuration attribute 207 * may have multiple values. 208 * @param requiresAdminAction Indicates whether changes to this 209 * configuration attribute require administrative 210 * action before they will take effect. 211 * @param hasLowerBound Indicates whether a lower bound will be 212 * enforced for values of this attribute. 213 * @param lowerBound The lower bound that will be enforced for 214 * values of this attribute. 215 * @param hasUpperBound Indicates whether an upper bound will be 216 * enforced for values of this attribute. 217 * @param upperBound The upper bound that will be enforced for 218 * values of this attribute. 219 * @param activeValues The set of active values for this 220 * configuration attribute. 221 * @param pendingValues The set of pending values for this 222 * configuration attribute. 223 */ 224 public IntegerConfigAttribute(String name, LocalizableMessage description, 225 boolean isRequired, boolean isMultiValued, 226 boolean requiresAdminAction, 227 boolean hasLowerBound, long lowerBound, 228 boolean hasUpperBound, long upperBound, 229 List<Long> activeValues, 230 List<Long> pendingValues) 231 { 232 super(name, description, isRequired, isMultiValued, requiresAdminAction, 233 getLongValueSet(activeValues), (pendingValues != null), 234 getLongValueSet(pendingValues)); 235 236 this.hasLowerBound = hasLowerBound; 237 this.lowerBound = lowerBound; 238 this.hasUpperBound = hasUpperBound; 239 this.upperBound = upperBound; 240 241 if (activeValues == null) 242 { 243 this.activeValues = new ArrayList<>(); 244 } 245 else 246 { 247 this.activeValues = activeValues; 248 } 249 250 if (pendingValues == null) 251 { 252 this.pendingValues = this.activeValues; 253 } 254 else 255 { 256 this.pendingValues = pendingValues; 257 } 258 } 259 260 /** 261 * Retrieves the name of the data type for this configuration attribute. This 262 * is for informational purposes (e.g., inclusion in method signatures and 263 * other kinds of descriptions) and does not necessarily need to map to an 264 * actual Java type. 265 * 266 * @return The name of the data type for this configuration attribute. 267 */ 268 @Override 269 public String getDataType() 270 { 271 return "Integer"; 272 } 273 274 /** 275 * Retrieves the attribute syntax for this configuration attribute. 276 * 277 * @return The attribute syntax for this configuration attribute. 278 */ 279 @Override 280 public Syntax getSyntax() 281 { 282 return DirectoryServer.getDefaultIntegerSyntax(); 283 } 284 285 /** 286 * Retrieves the active value for this configuration attribute as a long. 287 * This is only valid for single-valued attributes that have a value. 288 * 289 * @return The active value for this configuration attribute as a long. 290 * 291 * @throws ConfigException If this attribute does not have exactly one 292 * active value. 293 */ 294 public long activeValue() 295 throws ConfigException 296 { 297 if (activeValues == null || activeValues.isEmpty()) 298 { 299 LocalizableMessage message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName()); 300 throw new ConfigException(message); 301 } 302 303 if (activeValues.size() > 1) 304 { 305 LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName()); 306 throw new ConfigException(message); 307 } 308 309 return activeValues.get(0); 310 } 311 312 /** 313 * Retrieves the active value for this configuration attribute as an integer. 314 * This is only valid for single-valued attributes that have a value within 315 * the integer range. 316 * 317 * @return The active value for this configuration attribute as an integer. 318 * 319 * @throws ConfigException If the active value of this attribute cannot be 320 * retrieved as an integer, including if there are 321 * no values, if there are multiple values, or if 322 * the value is not in the range of an integer. 323 */ 324 public int activeIntValue() 325 throws ConfigException 326 { 327 if (activeValues == null || activeValues.isEmpty()) 328 { 329 LocalizableMessage message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName()); 330 throw new ConfigException(message); 331 } 332 333 if (activeValues.size() > 1) 334 { 335 LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName()); 336 throw new ConfigException(message); 337 } 338 339 long longValue = activeValues.get(0); 340 int intValue = (int) longValue; 341 if (intValue == longValue) 342 { 343 return intValue; 344 } 345 else 346 { 347 LocalizableMessage message = ERR_CONFIG_ATTR_VALUE_OUT_OF_INT_RANGE.get(getName()); 348 throw new ConfigException(message); 349 } 350 } 351 352 /** 353 * Retrieves the set of active values for this configuration attribute. 354 * 355 * @return The set of active values for this configuration attribute. 356 */ 357 public List<Long> activeValues() 358 { 359 return activeValues; 360 } 361 362 /** 363 * Retrieves the pending value for this configuration attribute as a long. 364 * This is only valid for single-valued attributes that have a value. If this 365 * attribute does not have any pending values, then the active value will be 366 * returned. 367 * 368 * @return The pending value for this configuration attribute as a long. 369 * 370 * @throws ConfigException If this attribute does not have exactly one 371 * pending value. 372 */ 373 public long pendingValue() 374 throws ConfigException 375 { 376 if (! hasPendingValues()) 377 { 378 return activeValue(); 379 } 380 381 if (pendingValues == null || pendingValues.isEmpty()) 382 { 383 LocalizableMessage message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName()); 384 throw new ConfigException(message); 385 } 386 387 if (pendingValues.size() > 1) 388 { 389 LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName()); 390 throw new ConfigException(message); 391 } 392 393 return pendingValues.get(0); 394 } 395 396 /** 397 * Retrieves the pending value for this configuration attribute as an integer. 398 * This is only valid for single-valued attributes that have a value within 399 * the integer range. If this attribute does not have any pending values, 400 * then t he active value will be returned. 401 * 402 * @return The pending value for this configuration attribute as an integer. 403 * 404 * @throws ConfigException If the pending value of this attribute cannot be 405 * retrieved as an integer, including if there are 406 * no values, if there are multiple values, or if 407 * the value is not in the range of an integer. 408 */ 409 public int pendingIntValue() 410 throws ConfigException 411 { 412 if (! hasPendingValues()) 413 { 414 return activeIntValue(); 415 } 416 417 if (pendingValues == null || pendingValues.isEmpty()) 418 { 419 LocalizableMessage message = ERR_CONFIG_ATTR_NO_INT_VALUE.get(getName()); 420 throw new ConfigException(message); 421 } 422 423 if (pendingValues.size() > 1) 424 { 425 LocalizableMessage message = ERR_CONFIG_ATTR_MULTIPLE_INT_VALUES.get(getName()); 426 throw new ConfigException(message); 427 } 428 429 long longValue = pendingValues.get(0); 430 int intValue = (int) longValue; 431 if (intValue == longValue) 432 { 433 return intValue; 434 } 435 else 436 { 437 LocalizableMessage message = ERR_CONFIG_ATTR_VALUE_OUT_OF_INT_RANGE.get(getName()); 438 throw new ConfigException(message); 439 } 440 } 441 442 /** 443 * Retrieves the set of pending values for this configuration attribute. If 444 * there are no pending values, then the set of active values will be 445 * returned. 446 * 447 * @return The set of pending values for this configuration attribute. 448 */ 449 public List<Long> pendingValues() 450 { 451 if (! hasPendingValues()) 452 { 453 return activeValues; 454 } 455 456 return pendingValues; 457 } 458 459 /** 460 * Indicates whether a lower bound will be enforced for the value of this 461 * configuration attribute. 462 * 463 * @return <CODE>true</CODE> if a lower bound will be enforced for the 464 * value of this configuration attribute, or <CODE>false</CODE> if 465 * not. 466 */ 467 public boolean hasLowerBound() 468 { 469 return hasLowerBound; 470 } 471 472 /** 473 * Retrieves the lower bound for the value of this configuration attribute. 474 * 475 * @return The lower bound for the value of this configuration attribute. 476 */ 477 public long getLowerBound() 478 { 479 return lowerBound; 480 } 481 482 /** 483 * Indicates whether an upper bound will be enforced for the calculated value 484 * of this configuration attribute. 485 * 486 * @return <CODE>true</CODE> if an upper bound will be enforced for the 487 * calculated value of this configuration attribute, or 488 * <CODE>false</CODE> if not. 489 */ 490 public boolean hasUpperBound() 491 { 492 return hasUpperBound; 493 } 494 495 /** 496 * Retrieves the upper bound for the calculated value of this configuration 497 * attribute. 498 * 499 * @return The upper bound for the calculated value of this configuration 500 * attribute. 501 */ 502 public long getUpperBound() 503 { 504 return upperBound; 505 } 506 507 /** 508 * Sets the value for this integer configuration attribute. 509 * 510 * @param value The value for this integer configuration attribute. 511 * 512 * @throws ConfigException If the provided value is not acceptable. 513 */ 514 public void setValue(long value) 515 throws ConfigException 516 { 517 if (hasLowerBound && value < lowerBound) 518 { 519 LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get( 520 getName(), value, lowerBound); 521 throw new ConfigException(message); 522 } 523 524 if (hasUpperBound && value > upperBound) 525 { 526 LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get( 527 getName(), value, upperBound); 528 throw new ConfigException(message); 529 } 530 531 if (requiresAdminAction()) 532 { 533 pendingValues = CollectionUtils.newArrayList(value); 534 setPendingValues(getLongValueSet(value)); 535 } 536 else 537 { 538 activeValues.clear(); 539 activeValues.add(value); 540 pendingValues = activeValues; 541 setActiveValues(getLongValueSet(value)); 542 } 543 } 544 545 /** 546 * Sets the values for this integer configuration attribute. 547 * 548 * @param values The set of values for this integer configuration attribute. 549 * 550 * @throws ConfigException If the provided value set or any of the 551 * individual values are not acceptable. 552 */ 553 public void setValues(List<Long> values) 554 throws ConfigException 555 { 556 // First check if the set is empty and if that is allowed. 557 if (values == null || values.isEmpty()) 558 { 559 if (isRequired()) 560 { 561 throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName())); 562 } 563 564 if (requiresAdminAction()) 565 { 566 setPendingValues(new LinkedHashSet<ByteString>(0)); 567 pendingValues = new ArrayList<>(); 568 } 569 else 570 { 571 setActiveValues(new LinkedHashSet<ByteString>(0)); 572 activeValues.clear(); 573 } 574 } 575 576 // Next check if the set contains multiple values and if that is allowed. 577 int numValues = values.size(); 578 if (!isMultiValued() && numValues > 1) 579 { 580 LocalizableMessage message = 581 ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName()); 582 throw new ConfigException(message); 583 } 584 585 // Iterate through all the provided values, make sure that they are 586 // acceptable, and build the value set. 587 LinkedHashSet<ByteString> valueSet = new LinkedHashSet<>(numValues); 588 for (long value : values) 589 { 590 if (hasLowerBound && value < lowerBound) 591 { 592 LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get( 593 getName(), value, lowerBound); 594 throw new ConfigException(message); 595 } 596 597 if (hasUpperBound && value > upperBound) 598 { 599 LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get( 600 getName(), value, upperBound); 601 throw new ConfigException(message); 602 } 603 604 String valueString = String.valueOf(value); 605 ByteString attrValue = ByteString.valueOfUtf8(valueString); 606 if (valueSet.contains(attrValue)) 607 { 608 LocalizableMessage message = ERR_CONFIG_ATTR_ADD_VALUES_ALREADY_EXISTS.get( 609 getName(), valueString); 610 throw new ConfigException(message); 611 } 612 613 valueSet.add(attrValue); 614 } 615 616 // Apply this value set to the new active or pending value set. 617 if (requiresAdminAction()) 618 { 619 pendingValues = values; 620 setPendingValues(valueSet); 621 } 622 else 623 { 624 activeValues = values; 625 pendingValues = activeValues; 626 setActiveValues(valueSet); 627 } 628 } 629 630 /** 631 * Creates the appropriate value set with the provided value. 632 * 633 * @param value The value to use to create the value set. 634 * 635 * @return The constructed value set. 636 */ 637 private static LinkedHashSet<ByteString> getLongValueSet(long value) 638 { 639 return getValueSet(String.valueOf(value)); 640 } 641 642 /** 643 * Creates the appropriate value set with the provided values. 644 * 645 * @param values The values to use to create the value set. 646 * 647 * @return The constructed value set. 648 */ 649 private static LinkedHashSet<ByteString> getLongValueSet(List<Long> values) 650 { 651 if (values == null) 652 { 653 return null; 654 } 655 656 LinkedHashSet<ByteString> valueSet = new LinkedHashSet<>(values.size()); 657 for (long value : values) 658 { 659 valueSet.add(ByteString.valueOfUtf8(String.valueOf(value))); 660 } 661 return valueSet; 662 } 663 664 /** 665 * Applies the set of pending values, making them the active values for this 666 * configuration attribute. This will not take any action if there are no 667 * pending values. 668 */ 669 @Override 670 public void applyPendingValues() 671 { 672 if (! hasPendingValues()) 673 { 674 return; 675 } 676 677 super.applyPendingValues(); 678 activeValues = pendingValues; 679 } 680 681 /** 682 * Indicates whether the provided value is acceptable for use in this 683 * attribute. If it is not acceptable, then the reason should be written into 684 * the provided buffer. 685 * 686 * @param value The value for which to make the determination. 687 * @param rejectReason A buffer into which a human-readable reason for the 688 * reject may be written. 689 * 690 * @return <CODE>true</CODE> if the provided value is acceptable for use in 691 * this attribute, or <CODE>false</CODE> if not. 692 */ 693 @Override 694 public boolean valueIsAcceptable(ByteString value, StringBuilder rejectReason) 695 { 696 // First, make sure we can represent it as a long. 697 String stringValue = value.toString(); 698 long longValue; 699 try 700 { 701 longValue = Long.parseLong(stringValue); 702 } 703 catch (Exception e) 704 { 705 logger.traceException(e); 706 707 rejectReason.append(ERR_CONFIG_ATTR_INVALID_INT_VALUE.get( 708 getName(), stringValue, e)); 709 return false; 710 } 711 712 // Perform any necessary bounds checking. 713 if (hasLowerBound && longValue < lowerBound) 714 { 715 rejectReason.append(ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get( 716 getName(), longValue, lowerBound)); 717 return false; 718 } 719 720 if (hasUpperBound && longValue > upperBound) 721 { 722 rejectReason.append(ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get( 723 getName(), longValue, upperBound)); 724 return false; 725 } 726 727 // If we've gotten here, then the value must be acceptable. 728 return true; 729 } 730 731 /** 732 * Converts the provided set of strings to a corresponding set of attribute 733 * values. 734 * 735 * @param valueStrings The set of strings to be converted into attribute 736 * values. 737 * @param allowFailures Indicates whether the decoding process should allow 738 * any failures in which one or more values could be 739 * decoded but at least one could not. If this is 740 * <CODE>true</CODE> and such a condition is acceptable 741 * for the underlying attribute type, then the returned 742 * set of values should simply not include those 743 * undecodable values. 744 * 745 * @return The set of attribute values converted from the provided strings. 746 * 747 * @throws ConfigException If an unrecoverable problem occurs while 748 * performing the conversion. 749 */ 750 @Override 751 public LinkedHashSet<ByteString> 752 stringsToValues(List<String> valueStrings, boolean allowFailures) 753 throws ConfigException 754 { 755 if (valueStrings == null || valueStrings.isEmpty()) 756 { 757 if (isRequired()) 758 { 759 throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName())); 760 } 761 return new LinkedHashSet<>(); 762 } 763 764 int numValues = valueStrings.size(); 765 if (!isMultiValued() && numValues > 1) 766 { 767 throw new ConfigException(ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName())); 768 } 769 770 LinkedHashSet<ByteString> valueSet = new LinkedHashSet<>(numValues); 771 for (String valueString : valueStrings) 772 { 773 long longValue; 774 try 775 { 776 longValue = Long.parseLong(valueString); 777 } 778 catch (Exception e) 779 { 780 logger.traceException(e); 781 782 reportError(allowFailures, ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(valueString, getName(), e)); 783 continue; 784 } 785 786 if (hasLowerBound && longValue < lowerBound) 787 { 788 reportError(allowFailures, ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get(getName(), longValue, lowerBound)); 789 continue; 790 } 791 if (hasUpperBound && longValue > upperBound) 792 { 793 reportError(allowFailures, ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get(getName(), longValue, upperBound)); 794 continue; 795 } 796 797 valueSet.add(ByteString.valueOfUtf8(valueString)); 798 } 799 800 // If this method was configured to continue on error, then it is possible 801 // that we ended up with an empty list. Check to see if this is a required 802 // attribute and if so deal with it accordingly. 803 if (isRequired() && valueSet.isEmpty()) 804 { 805 throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(getName())); 806 } 807 808 return valueSet; 809 } 810 811 private void reportError(boolean allowFailures, LocalizableMessage message) throws ConfigException 812 { 813 if (!allowFailures) 814 { 815 throw new ConfigException(message); 816 } 817 logger.error(message); 818 } 819 820 /** 821 * Converts the set of active values for this configuration attribute into a 822 * set of strings that may be stored in the configuration or represented over 823 * protocol. The string representation used by this method should be 824 * compatible with the decoding used by the <CODE>stringsToValues</CODE> 825 * method. 826 * 827 * @return The string representations of the set of active values for this 828 * configuration attribute. 829 */ 830 @Override 831 public List<String> activeValuesToStrings() 832 { 833 return toListOfString(activeValues); 834 } 835 836 /** 837 * Converts the set of pending values for this configuration attribute into a 838 * set of strings that may be stored in the configuration or represented over 839 * protocol. The string representation used by this method should be 840 * compatible with the decoding used by the <CODE>stringsToValues</CODE> 841 * method. 842 * 843 * @return The string representations of the set of pending values for this 844 * configuration attribute, or <CODE>null</CODE> if there are no 845 * pending values. 846 */ 847 @Override 848 public List<String> pendingValuesToStrings() 849 { 850 if (hasPendingValues()) 851 { 852 return toListOfString(pendingValues); 853 } 854 return null; 855 } 856 857 private List<String> toListOfString(List<Long> values) 858 { 859 ArrayList<String> results = new ArrayList<>(values.size()); 860 for (long l : values) 861 { 862 results.add(String.valueOf(l)); 863 } 864 return results; 865 } 866 867 /** 868 * Retrieves a new configuration attribute of this type that will contain the 869 * values from the provided attribute. 870 * 871 * @param attributeList The list of attributes to use to create the config 872 * attribute. The list must contain either one or two 873 * elements, with both attributes having the same base 874 * name and the only option allowed is ";pending" and 875 * only if this attribute is one that requires admin 876 * action before a change may take effect. 877 * 878 * @return The generated configuration attribute. 879 * 880 * @throws ConfigException If the provided attribute cannot be treated as a 881 * configuration attribute of this type (e.g., if 882 * one or more of the values of the provided 883 * attribute are not suitable for an attribute of 884 * this type, or if this configuration attribute is 885 * single-valued and the provided attribute has 886 * multiple values). 887 */ 888 @Override 889 public ConfigAttribute getConfigAttribute(List<Attribute> attributeList) 890 throws ConfigException 891 { 892 ArrayList<Long> activeValues = null; 893 ArrayList<Long> pendingValues = null; 894 895 for (Attribute a : attributeList) 896 { 897 if (a.hasOptions()) 898 { 899 // This must be the pending value. 900 if (a.hasOption(OPTION_PENDING_VALUES)) 901 { 902 if (pendingValues != null) 903 { 904 // We cannot have multiple pending value sets. 905 LocalizableMessage message = 906 ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName()); 907 throw new ConfigException(message); 908 } 909 910 if (a.isEmpty()) 911 { 912 if (isRequired()) 913 { 914 // This is illegal -- it must have a value. 915 LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName()); 916 throw new ConfigException(message); 917 } 918 else 919 { 920 // This is fine. The pending value set can be empty. 921 pendingValues = new ArrayList<>(0); 922 } 923 } 924 else 925 { 926 int numValues = a.size(); 927 if (numValues > 1 && !isMultiValued()) 928 { 929 // This is illegal -- the attribute is single-valued. 930 LocalizableMessage message = 931 ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName()); 932 throw new ConfigException(message); 933 } 934 935 pendingValues = new ArrayList<>(numValues); 936 for (ByteString v : a) 937 { 938 long longValue; 939 try 940 { 941 longValue = Long.parseLong(v.toString()); 942 } 943 catch (Exception e) 944 { 945 LocalizableMessage message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get( 946 v, a.getName(), e); 947 throw new ConfigException(message, e); 948 } 949 950 // Check the bounds set for this attribute. 951 if (hasLowerBound && longValue < lowerBound) 952 { 953 LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get( 954 a.getName(), longValue, lowerBound); 955 throw new ConfigException(message); 956 } 957 958 if (hasUpperBound && longValue > upperBound) 959 { 960 LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get( 961 a.getName(), longValue, upperBound); 962 throw new ConfigException(message); 963 } 964 965 pendingValues.add(longValue); 966 } 967 } 968 } 969 else 970 { 971 // This is illegal -- only the pending option is allowed for 972 // configuration attributes. 973 LocalizableMessage message = 974 ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get( 975 a.getName()); 976 throw new ConfigException(message); 977 } 978 } 979 else 980 { 981 // This must be the active value. 982 if (activeValues!= null) 983 { 984 // We cannot have multiple active value sets. 985 LocalizableMessage message = 986 ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName()); 987 throw new ConfigException(message); 988 } 989 990 if (a.isEmpty()) 991 { 992 if (isRequired()) 993 { 994 // This is illegal -- it must have a value. 995 LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName()); 996 throw new ConfigException(message); 997 } 998 else 999 { 1000 // This is fine. The active value set can be empty. 1001 activeValues = new ArrayList<>(0); 1002 } 1003 } 1004 else 1005 { 1006 int numValues = a.size(); 1007 if (numValues > 1 && !isMultiValued()) 1008 { 1009 // This is illegal -- the attribute is single-valued. 1010 LocalizableMessage message = 1011 ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName()); 1012 throw new ConfigException(message); 1013 } 1014 1015 activeValues = new ArrayList<>(numValues); 1016 for (ByteString v : a) 1017 { 1018 long longValue; 1019 try 1020 { 1021 longValue = Long.parseLong(v.toString()); 1022 } 1023 catch (Exception e) 1024 { 1025 LocalizableMessage message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get( 1026 v, a.getName(), e); 1027 throw new ConfigException(message, e); 1028 } 1029 1030 // Check the bounds set for this attribute. 1031 if (hasLowerBound && longValue < lowerBound) 1032 { 1033 LocalizableMessage message = ERR_CONFIG_ATTR_INT_BELOW_LOWER_BOUND.get( 1034 a.getName(), longValue, lowerBound); 1035 throw new ConfigException(message); 1036 } 1037 1038 if (hasUpperBound && longValue > upperBound) 1039 { 1040 LocalizableMessage message = ERR_CONFIG_ATTR_INT_ABOVE_UPPER_BOUND.get( 1041 a.getName(), longValue, upperBound); 1042 throw new ConfigException(message); 1043 } 1044 1045 activeValues.add(longValue); 1046 } 1047 } 1048 } 1049 } 1050 1051 if (activeValues == null) 1052 { 1053 // This is not OK. The value set must contain an active value. 1054 LocalizableMessage message = ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName()); 1055 throw new ConfigException(message); 1056 } 1057 1058 if (pendingValues == null) 1059 { 1060 // This is OK. We'll just use the active value set. 1061 pendingValues = activeValues; 1062 } 1063 1064 return new IntegerConfigAttribute(getName(), getDescription(), isRequired(), 1065 isMultiValued(), requiresAdminAction(), 1066 hasLowerBound, lowerBound, hasUpperBound, 1067 upperBound, activeValues, pendingValues); 1068 } 1069 1070 /** 1071 * Retrieves a JMX attribute containing the value set for this 1072 * configuration attribute (active or pending). 1073 * 1074 * @param pending indicates if pending or active values are required. 1075 * 1076 * @return A JMX attribute containing the active value set for this 1077 * configuration attribute, or <CODE>null</CODE> if it does not have 1078 * any active values. 1079 */ 1080 private javax.management.Attribute _toJMXAttribute(boolean pending) 1081 { 1082 List<Long> requestedValues ; 1083 String name ; 1084 if (pending) 1085 { 1086 requestedValues = pendingValues ; 1087 name = getName() + ";" + OPTION_PENDING_VALUES ; 1088 } 1089 else 1090 { 1091 requestedValues = activeValues ; 1092 name = getName() ; 1093 } 1094 1095 if (isMultiValued()) 1096 { 1097 long[] values = new long[requestedValues.size()]; 1098 for (int i=0; i < values.length; i++) 1099 { 1100 values[i] = requestedValues.get(i); 1101 } 1102 1103 return new javax.management.Attribute(name, values); 1104 } 1105 else if (requestedValues.isEmpty()) 1106 { 1107 return null; 1108 } 1109 else 1110 { 1111 return new javax.management.Attribute(name, requestedValues.get(0)); 1112 } 1113 } 1114 1115 /** 1116 * Retrieves a JMX attribute containing the active value set for this 1117 * configuration attribute. 1118 * 1119 * @return A JMX attribute containing the active value set for this 1120 * configuration attribute, or <CODE>null</CODE> if it does not have 1121 * any active values. 1122 */ 1123 @Override 1124 public javax.management.Attribute toJMXAttribute() 1125 { 1126 return _toJMXAttribute(false); 1127 } 1128 1129 /** 1130 * Retrieves a JMX attribute containing the pending value set for this 1131 * configuration attribute. 1132 * 1133 * @return A JMX attribute containing the pending value set for this 1134 * configuration attribute. 1135 */ 1136 @Override 1137 public javax.management.Attribute toJMXAttributePending() 1138 { 1139 return _toJMXAttribute(true); 1140 } 1141 1142 /** 1143 * Adds information about this configuration attribute to the provided JMX 1144 * attribute list. If this configuration attribute requires administrative 1145 * action before changes take effect and it has a set of pending values, then 1146 * two attributes should be added to the list -- one for the active value 1147 * and one for the pending value. The pending value should be named with 1148 * the pending option. 1149 * 1150 * @param attributeList The attribute list to which the JMX attribute(s) 1151 * should be added. 1152 */ 1153 @Override 1154 public void toJMXAttribute(AttributeList attributeList) 1155 { 1156 if (!activeValues.isEmpty()) 1157 { 1158 if (isMultiValued()) 1159 { 1160 long[] values = new long[activeValues.size()]; 1161 for (int i=0; i < values.length; i++) 1162 { 1163 values[i] = activeValues.get(i); 1164 } 1165 1166 attributeList.add(new javax.management.Attribute(getName(), values)); 1167 } 1168 else 1169 { 1170 attributeList.add(new javax.management.Attribute(getName(), 1171 activeValues.get(0))); 1172 } 1173 } 1174 else 1175 { 1176 if (isMultiValued()) 1177 { 1178 attributeList.add(new javax.management.Attribute(getName(), 1179 new String[0])); 1180 } 1181 else 1182 { 1183 attributeList.add(new javax.management.Attribute(getName(), null)); 1184 } 1185 } 1186 1187 if (requiresAdminAction() 1188 && pendingValues != null 1189 && pendingValues != activeValues) 1190 { 1191 String name = getName() + ";" + OPTION_PENDING_VALUES; 1192 1193 if (isMultiValued()) 1194 { 1195 long[] values = new long[pendingValues.size()]; 1196 for (int i=0; i < values.length; i++) 1197 { 1198 values[i] = pendingValues.get(i); 1199 } 1200 1201 attributeList.add(new javax.management.Attribute(name, values)); 1202 } 1203 else if (! pendingValues.isEmpty()) 1204 { 1205 attributeList.add(new javax.management.Attribute(name, 1206 pendingValues.get(0))); 1207 } 1208 } 1209 } 1210 1211 /** 1212 * Adds information about this configuration attribute to the provided list in 1213 * the form of a JMX <CODE>MBeanAttributeInfo</CODE> object. If this 1214 * configuration attribute requires administrative action before changes take 1215 * effect and it has a set of pending values, then two attribute info objects 1216 * should be added to the list -- one for the active value (which should be 1217 * read-write) and one for the pending value (which should be read-only). The 1218 * pending value should be named with the pending option. 1219 * 1220 * @param attributeInfoList The list to which the attribute information 1221 * should be added. 1222 */ 1223 @Override 1224 public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList) 1225 { 1226 if (isMultiValued()) 1227 { 1228 attributeInfoList.add(new MBeanAttributeInfo(getName(), 1229 JMX_TYPE_LONG_ARRAY, 1230 String.valueOf( 1231 getDescription()), 1232 true, true, false)); 1233 } 1234 else 1235 { 1236 attributeInfoList.add(new MBeanAttributeInfo(getName(), 1237 Long.class.getName(), 1238 String.valueOf( 1239 getDescription()), 1240 true, true, false)); 1241 } 1242 1243 if (requiresAdminAction()) 1244 { 1245 String name = getName() + ";" + OPTION_PENDING_VALUES; 1246 1247 if (isMultiValued()) 1248 { 1249 attributeInfoList.add(new MBeanAttributeInfo(name, JMX_TYPE_LONG_ARRAY, 1250 String.valueOf( 1251 getDescription()), 1252 true, false, false)); 1253 } 1254 else 1255 { 1256 attributeInfoList.add(new MBeanAttributeInfo(name, Long.class.getName(), 1257 String.valueOf( 1258 getDescription()), 1259 true, false, false)); 1260 } 1261 } 1262 } 1263 1264 /** 1265 * Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this 1266 * configuration attribute. 1267 * 1268 * @return A JMX <CODE>MBeanParameterInfo</CODE> object that describes this 1269 * configuration attribute. 1270 */ 1271 @Override 1272 public MBeanParameterInfo toJMXParameterInfo() 1273 { 1274 if (isMultiValued()) 1275 { 1276 return new MBeanParameterInfo(getName(), JMX_TYPE_LONG_ARRAY, 1277 String.valueOf(getDescription())); 1278 } 1279 else 1280 { 1281 return new MBeanParameterInfo(getName(), Long.TYPE.getName(), 1282 String.valueOf(getDescription())); 1283 } 1284 } 1285 1286 /** 1287 * Attempts to set the value of this configuration attribute based on the 1288 * information in the provided JMX attribute. 1289 * 1290 * @param jmxAttribute The JMX attribute to use to attempt to set the value 1291 * of this configuration attribute. 1292 * 1293 * @throws ConfigException If the provided JMX attribute does not have an 1294 * acceptable value for this configuration 1295 * attribute. 1296 */ 1297 @Override 1298 public void setValue(javax.management.Attribute jmxAttribute) 1299 throws ConfigException 1300 { 1301 Object value = jmxAttribute.getValue(); 1302 if (value instanceof Long) 1303 { 1304 setValue(((Long) value).longValue()); 1305 } 1306 else if (value instanceof Integer) 1307 { 1308 setValue(((Integer) value).intValue()); 1309 } 1310 else if (value instanceof String) 1311 { 1312 try 1313 { 1314 setValue(Long.parseLong((String) value)); 1315 } 1316 catch (Exception e) 1317 { 1318 logger.traceException(e); 1319 1320 LocalizableMessage message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get(value, getName(), e); 1321 throw new ConfigException(message, e); 1322 } 1323 } 1324 else if (value.getClass().isArray()) 1325 { 1326 String componentType = value.getClass().getComponentType().getName(); 1327 int length = Array.getLength(value); 1328 1329 try 1330 { 1331 if (componentType.equals(Long.class.getName())) 1332 { 1333 ArrayList<Long> values = new ArrayList<>(); 1334 1335 for (int i=0; i < length; i++) 1336 { 1337 values.add(Array.getLong(value, i)); 1338 } 1339 1340 setValues(values); 1341 } 1342 else if (componentType.equals(Integer.class.getName())) 1343 { 1344 ArrayList<Long> values = new ArrayList<>(); 1345 1346 for (int i=0; i < length; i++) 1347 { 1348 values.add((long) Array.getInt(value, i)); 1349 } 1350 1351 setValues(values); 1352 } 1353 else if (componentType.equals(String.class.getName())) 1354 { 1355 ArrayList<Long> values = new ArrayList<>(); 1356 1357 for (int i=0; i < length; i++) 1358 { 1359 String s = (String) Array.get(value, i); 1360 values.add(Long.parseLong(s)); 1361 } 1362 1363 setValues(values); 1364 } 1365 else 1366 { 1367 LocalizableMessage message = 1368 ERR_CONFIG_ATTR_INT_INVALID_ARRAY_TYPE.get( 1369 jmxAttribute.getName(), componentType); 1370 throw new ConfigException(message); 1371 } 1372 } 1373 catch (ConfigException ce) 1374 { 1375 logger.traceException(ce); 1376 1377 throw ce; 1378 } 1379 catch (Exception e) 1380 { 1381 logger.traceException(e); 1382 1383 LocalizableMessage message = ERR_CONFIG_ATTR_INT_COULD_NOT_PARSE.get( 1384 componentType + "[" + length + "]", getName(), e); 1385 throw new ConfigException(message, e); 1386 } 1387 } 1388 else 1389 { 1390 throw new ConfigException(ERR_CONFIG_ATTR_INT_INVALID_TYPE.get( 1391 value, getName(), value.getClass().getName())); 1392 } 1393 } 1394 1395 /** 1396 * Creates a duplicate of this configuration attribute. 1397 * 1398 * @return A duplicate of this configuration attribute. 1399 */ 1400 @Override 1401 public ConfigAttribute duplicate() 1402 { 1403 return new IntegerConfigAttribute(getName(), getDescription(), isRequired(), 1404 isMultiValued(), requiresAdminAction(), 1405 hasLowerBound, lowerBound, hasUpperBound, 1406 upperBound, activeValues, pendingValues); 1407 } 1408}