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