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