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