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