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