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