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