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