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-2015 ForgeRock AS. 016 */ 017package org.opends.server.config; 018 019import java.util.Iterator; 020import java.util.LinkedHashSet; 021import java.util.List; 022 023import javax.management.AttributeList; 024import javax.management.MBeanAttributeInfo; 025import javax.management.MBeanParameterInfo; 026 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.opendj.ldap.ByteString; 029import org.forgerock.opendj.ldap.schema.Syntax; 030import org.opends.server.core.DirectoryServer; 031import org.opends.server.types.Attribute; 032 033import static org.opends.messages.ConfigMessages.*; 034import static org.opends.server.config.ConfigConstants.*; 035import static org.opends.server.util.CollectionUtils.*; 036import static org.opends.server.util.ServerConstants.*; 037 038/** 039 * This class defines a Boolean configuration attribute, which can hold a single 040 * Boolean value of <CODE>true</CODE> or <CODE>false</CODE>. Boolean 041 * configuration attributes will always be required and will never be 042 * multivalued. 043 */ 044@org.opends.server.types.PublicAPI( 045 stability=org.opends.server.types.StabilityLevel.VOLATILE, 046 mayInstantiate=true, 047 mayExtend=false, 048 mayInvoke=true) 049public final class BooleanConfigAttribute 050 extends ConfigAttribute 051{ 052 /** The active value for this attribute. */ 053 private boolean activeValue; 054 055 /** The pending value for this attribute. */ 056 private boolean pendingValue; 057 058 059 060 /** 061 * Creates a new Boolean configuration attribute stub with the provided 062 * information but no values. The values will be set using the 063 * <CODE>setInitialValue</CODE> method. 064 * 065 * @param name The name for this configuration attribute. 066 * @param description The description for this configuration 067 * attribute. 068 * @param requiresAdminAction Indicates whether changes to this 069 * configuration attribute require administrative 070 * action before they will take effect. 071 */ 072 public BooleanConfigAttribute(String name, LocalizableMessage description, 073 boolean requiresAdminAction) 074 { 075 super(name, description, true, false, requiresAdminAction); 076 077 } 078 079 080 081 /** 082 * Creates a new Boolean configuration attribute with the provided 083 * information. 084 * 085 * @param name The name for this configuration attribute. 086 * @param description The description for this configuration 087 * attribute. 088 * @param requiresAdminAction Indicates whether changes to this 089 * configuration attribute require administrative 090 * action before they will take effect. 091 * @param value The value for this Boolean configuration 092 * attribute. 093 */ 094 public BooleanConfigAttribute(String name, LocalizableMessage description, 095 boolean requiresAdminAction, 096 boolean value) 097 { 098 super(name, description, true, false, requiresAdminAction, 099 getValueSet(value)); 100 101 activeValue = value; 102 pendingValue = value; 103 } 104 105 106 107 /** 108 * Creates a new Boolean configuration attribute with the provided 109 * information. 110 * 111 * @param name The name for this configuration attribute. 112 * @param description The description for this configuration 113 * attribute. 114 * @param requiresAdminAction Indicates whether changes to this 115 * configuration attribute require administrative 116 * action before they will take effect. 117 * @param activeValue The active value for this Boolean 118 * configuration attribute. 119 * @param pendingValue The pending value for this Boolean 120 * configuration attribute. 121 */ 122 public BooleanConfigAttribute(String name, LocalizableMessage description, 123 boolean requiresAdminAction, 124 boolean activeValue, boolean pendingValue) 125 { 126 super(name, description, true, false, requiresAdminAction, 127 getValueSet(activeValue), true, getValueSet(pendingValue)); 128 129 130 this.activeValue = activeValue; 131 this.pendingValue = pendingValue; 132 } 133 134 135 136 /** 137 * Retrieves the name of the data type for this configuration attribute. This 138 * is for informational purposes (e.g., inclusion in method signatures and 139 * other kinds of descriptions) and does not necessarily need to map to an 140 * actual Java type. 141 * 142 * @return The name of the data type for this configuration attribute. 143 */ 144 public String getDataType() 145 { 146 return "Boolean"; 147 } 148 149 150 151 /** 152 * Retrieves the attribute syntax for this configuration attribute. 153 * 154 * @return The attribute syntax for this configuration attribute. 155 */ 156 public Syntax getSyntax() 157 { 158 return DirectoryServer.getDefaultBooleanSyntax(); 159 } 160 161 162 163 /** 164 * Retrieves the active boolean value for this configuration attribute. 165 * 166 * @return The active boolean value for this configuration attribute. 167 */ 168 public boolean activeValue() 169 { 170 return activeValue; 171 } 172 173 174 175 /** 176 * Retrieves the pending boolean value for this configuration attribute. If 177 * there is no pending value, then the active value will be returned. 178 * 179 * @return The pending boolean value for this configuration attribute. 180 */ 181 public boolean pendingValue() 182 { 183 if (hasPendingValues()) 184 { 185 return pendingValue; 186 } 187 return activeValue; 188 } 189 190 191 192 /** 193 * Specifies the boolean value for this configuration attribute. 194 * 195 * @param booleanValue The boolean value for this configuration attribute. 196 */ 197 public void setValue(boolean booleanValue) 198 { 199 if (requiresAdminAction()) 200 { 201 pendingValue = booleanValue; 202 setPendingValues(getValueSet(booleanValue)); 203 } 204 else 205 { 206 activeValue = booleanValue; 207 setActiveValues(getValueSet(booleanValue)); 208 } 209 } 210 211 212 213 /** 214 * Creates the appropriate value set with the provided value. 215 * 216 * @param booleanValue The boolean value to use to create the value set. 217 * 218 * @return The value set constructed from the provided value. 219 */ 220 private static LinkedHashSet<ByteString> getValueSet(boolean booleanValue) 221 { 222 return getValueSet(booleanValue ? CONFIG_VALUE_TRUE : CONFIG_VALUE_FALSE); 223 } 224 225 226 227 /** 228 * Applies the set of pending values, making them the active values for this 229 * configuration attribute. This will not take any action if there are no 230 * pending values. 231 */ 232 public void applyPendingValues() 233 { 234 if (! hasPendingValues()) 235 { 236 return; 237 } 238 239 super.applyPendingValues(); 240 activeValue = pendingValue; 241 } 242 243 244 245 /** 246 * Indicates whether the provided value is acceptable for use in this 247 * attribute. If it is not acceptable, then the reason should be written into 248 * the provided buffer. 249 * 250 * @param value The value for which to make the determination. 251 * @param rejectReason A buffer into which a human-readable reason for the 252 * reject may be written. 253 * 254 * @return <CODE>true</CODE> if the provided value is acceptable for use in 255 * this attribute, or <CODE>false</CODE> if not. 256 */ 257 public boolean valueIsAcceptable(ByteString value, 258 StringBuilder rejectReason) 259 { 260 String stringValue = value.toString(); 261 if (stringValue.equalsIgnoreCase(CONFIG_VALUE_TRUE) || 262 stringValue.equalsIgnoreCase(CONFIG_VALUE_FALSE)) 263 { 264 return true; 265 } 266 267 rejectReason.append(ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get( 268 getName(), stringValue)); 269 return false; 270 } 271 272 273 274 /** 275 * Converts the provided set of strings to a corresponding set of attribute 276 * values. 277 * 278 * @param valueStrings The set of strings to be converted into attribute 279 * values. 280 * @param allowFailures Indicates whether the decoding process should allow 281 * any failures in which one or more values could be 282 * decoded but at least one could not. If this is 283 * <CODE>true</CODE> and such a condition is acceptable 284 * for the underlying attribute type, then the returned 285 * set of values should simply not include those 286 * undecodable values. 287 * 288 * @return The set of attribute values converted from the provided strings. 289 * 290 * @throws ConfigException If an unrecoverable problem occurs while 291 * performing the conversion. 292 */ 293 public LinkedHashSet<ByteString> stringsToValues(List<String> valueStrings, 294 boolean allowFailures) throws ConfigException 295 { 296 if (valueStrings == null || valueStrings.isEmpty()) 297 { 298 LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName()); 299 throw new ConfigException(message); 300 } 301 302 303 Iterator<String> iterator = valueStrings.iterator(); 304 String valueString = iterator.next().toLowerCase(); 305 if (iterator.hasNext()) 306 { 307 LocalizableMessage message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName()); 308 throw new ConfigException(message); 309 } 310 311 if (valueString.equals("true") || valueString.equals("yes") || 312 valueString.equals("on") || valueString.equals("1")) 313 { 314 return getValueSet(true); 315 } 316 else if (valueString.equals("false") || valueString.equals("no") || 317 valueString.equals("off") || valueString.equals("0")) 318 { 319 return getValueSet(false); 320 } 321 else 322 { 323 LocalizableMessage message = 324 ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get(getName(), valueString); 325 throw new ConfigException(message); 326 } 327 } 328 329 330 331 /** 332 * Converts the set of active values for this configuration attribute into a 333 * set of strings that may be stored in the configuration or represented over 334 * protocol. The string representation used by this method should be 335 * compatible with the decoding used by the <CODE>stringsToValues</CODE> 336 * method. 337 * 338 * @return The string representations of the set of active values for this 339 * configuration attribute. 340 */ 341 public List<String> activeValuesToStrings() 342 { 343 return newArrayList(String.valueOf(activeValue)); 344 } 345 346 /** 347 * Converts the set of pending values for this configuration attribute into a 348 * set of strings that may be stored in the configuration or represented over 349 * protocol. The string representation used by this method should be 350 * compatible with the decoding used by the {@link #stringsToValues(List, boolean)} 351 * method. 352 * 353 * @return The string representations of the set of pending values for this 354 * configuration attribute, or {@code null} if there are no 355 * pending values. 356 */ 357 public List<String> pendingValuesToStrings() 358 { 359 if (hasPendingValues()) 360 { 361 return newArrayList(String.valueOf(pendingValue)); 362 } 363 return null; 364 } 365 366 /** 367 * Retrieves a new configuration attribute of this type that will contain the 368 * values from the provided attribute. 369 * 370 * @param attributeList The list of attributes to use to create the config 371 * attribute. The list must contain either one or two 372 * elements, with both attributes having the same base 373 * name and the only option allowed is ";pending" and 374 * only if this attribute is one that requires admin 375 * action before a change may take effect. 376 * 377 * @return The generated configuration attribute. 378 * 379 * @throws ConfigException If the provided attribute cannot be treated as a 380 * configuration attribute of this type (e.g., if 381 * one or more of the values of the provided 382 * attribute are not suitable for an attribute of 383 * this type, or if this configuration attribute is 384 * single-valued and the provided attribute has 385 * multiple values). 386 */ 387 public ConfigAttribute getConfigAttribute(List<Attribute> attributeList) 388 throws ConfigException 389 { 390 boolean activeValue = false; 391 boolean pendingValue = false; 392 boolean activeValueSet = false; 393 boolean pendingValueSet = false; 394 395 for (Attribute a : attributeList) 396 { 397 if (a.hasOptions()) 398 { 399 // This must be the pending value. 400 if (a.hasOption(OPTION_PENDING_VALUES)) 401 { 402 if (pendingValueSet) 403 { 404 // We cannot have multiple pending values. 405 throw new ConfigException(ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName())); 406 } 407 if (a.isEmpty()) 408 { 409 // This is illegal -- it must have a value. 410 throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName())); 411 } 412 413 // Get the value and parse it as a Boolean. 414 Iterator<ByteString> iterator = a.iterator(); 415 String valueString = iterator.next().toString().toLowerCase(); 416 417 if (valueString.equals("true") || valueString.equals("yes") || 418 valueString.equals("on") || valueString.equals("1")) 419 { 420 pendingValue = true; 421 pendingValueSet = true; 422 } 423 else if (valueString.equals("false") || valueString.equals("no") || 424 valueString.equals("off") || valueString.equals("0")) 425 { 426 pendingValue = false; 427 pendingValueSet = true; 428 } 429 else 430 { 431 // This is an illegal value. 432 throw new ConfigException(ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get(getName(), valueString)); 433 } 434 435 if (iterator.hasNext()) 436 { 437 // This is illegal -- it must be single-valued. 438 throw new ConfigException(ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName())); 439 } 440 } 441 else 442 { 443 // This is illegal -- only the pending option is allowed for 444 // configuration attributes. 445 throw new ConfigException(ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get(a.getName())); 446 } 447 } 448 else 449 { 450 // This must be the active value. 451 if (activeValueSet) 452 { 453 // We cannot have multiple active values. 454 throw new ConfigException(ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName())); 455 } 456 if (a.isEmpty()) 457 { 458 // This is illegal -- it must have a value. 459 throw new ConfigException(ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName())); 460 } 461 462 // Get the value and parse it as a Boolean. 463 Iterator<ByteString> iterator = a.iterator(); 464 String valueString = iterator.next().toString().toLowerCase(); 465 466 if (valueString.equals("true") || valueString.equals("yes") || 467 valueString.equals("on") || valueString.equals("1")) 468 { 469 activeValue = true; 470 activeValueSet = true; 471 } 472 else if (valueString.equals("false") || valueString.equals("no") || 473 valueString.equals("off") || valueString.equals("0")) 474 { 475 activeValue = false; 476 activeValueSet = true; 477 } 478 else 479 { 480 // This is an illegal value. 481 throw new ConfigException(ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get(getName(), valueString)); 482 } 483 484 if (iterator.hasNext()) 485 { 486 // This is illegal -- it must be single-valued. 487 throw new ConfigException(ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName())); 488 } 489 } 490 } 491 492 if (! activeValueSet) 493 { 494 // This is not OK. The value set must contain an active value. 495 throw new ConfigException(ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName())); 496 } 497 498 if (pendingValueSet) 499 { 500 return new BooleanConfigAttribute(getName(), getDescription(), 501 requiresAdminAction(), activeValue, 502 pendingValue); 503 } 504 else 505 { 506 return new BooleanConfigAttribute(getName(), getDescription(), 507 requiresAdminAction(), activeValue); 508 } 509 } 510 511 512 513 /** 514 * Retrieves a JMX attribute containing the active value set for this 515 * configuration attribute. 516 * 517 * @return A JMX attribute containing the active value set for this 518 * configuration attribute, or <CODE>null</CODE> if it does not have 519 * any active values. 520 */ 521 public javax.management.Attribute toJMXAttribute() 522 { 523 return new javax.management.Attribute(getName(), activeValue); 524 } 525 526 /** 527 * Retrieves a JMX attribute containing the pending value set for this 528 * configuration attribute. 529 * 530 * @return A JMX attribute containing the pending value set for this 531 * configuration attribute. 532 */ 533 public javax.management.Attribute toJMXAttributePending() 534 { 535 return new javax.management.Attribute(getName() + ";" 536 + OPTION_PENDING_VALUES, pendingValue); 537 } 538 539 540 541 /** 542 * Adds information about this configuration attribute to the provided JMX 543 * attribute list. If this configuration attribute requires administrative 544 * action before changes take effect and it has a set of pending values, then 545 * two attributes should be added to the list -- one for the active value 546 * and one for the pending value. The pending value should be named with 547 * the pending option. 548 * 549 * @param attributeList The attribute list to which the JMX attribute(s) 550 * should be added. 551 */ 552 public void toJMXAttribute(AttributeList attributeList) 553 { 554 attributeList.add(new javax.management.Attribute(getName(), activeValue)); 555 556 if (requiresAdminAction() && pendingValue != activeValue) 557 { 558 String name = getName() + ";" + OPTION_PENDING_VALUES; 559 attributeList.add(new javax.management.Attribute(name, pendingValue)); 560 } 561 } 562 563 564 565 /** 566 * Adds information about this configuration attribute to the provided list in 567 * the form of a JMX <CODE>MBeanAttributeInfo</CODE> object. If this 568 * configuration attribute requires administrative action before changes take 569 * effect and it has a set of pending values, then two attribute info objects 570 * should be added to the list -- one for the active value (which should be 571 * read-write) and one for the pending value (which should be read-only). The 572 * pending value should be named with the pending option. 573 * 574 * @param attributeInfoList The list to which the attribute information 575 * should be added. 576 */ 577 public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList) 578 { 579 attributeInfoList.add(new MBeanAttributeInfo(getName(), 580 Boolean.class.getName(), 581 String.valueOf( 582 getDescription()), 583 true, true, false)); 584 585 if (requiresAdminAction()) 586 { 587 String name = getName() + ";" + OPTION_PENDING_VALUES; 588 attributeInfoList.add(new MBeanAttributeInfo(name, 589 Boolean.class.getName(), 590 String.valueOf( 591 getDescription()), 592 true, false, false)); 593 } 594 } 595 596 597 598 /** 599 * Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this 600 * configuration attribute. 601 * 602 * @return A JMX <CODE>MBeanParameterInfo</CODE> object that describes this 603 * configuration attribute. 604 */ 605 public MBeanParameterInfo toJMXParameterInfo() 606 { 607 return new MBeanParameterInfo(getName(), Boolean.TYPE.getName(), 608 String.valueOf(getDescription())); 609 } 610 611 612 613 /** 614 * Attempts to set the value of this configuration attribute based on the 615 * information in the provided JMX attribute. 616 * 617 * @param jmxAttribute The JMX attribute to use to attempt to set the value 618 * of this configuration attribute. 619 * 620 * @throws ConfigException If the provided JMX attribute does not have an 621 * acceptable value for this configuration 622 * attribute. 623 */ 624 public void setValue(javax.management.Attribute jmxAttribute) 625 throws ConfigException 626 { 627 Object value = jmxAttribute.getValue(); 628 if (value instanceof Boolean) 629 { 630 setValue(((Boolean) value).booleanValue()); 631 } 632 else if (value instanceof String) 633 { 634 String stringValue = ((String) value).toLowerCase(); 635 if (stringValue.equals("true") || stringValue.equals("yes") || 636 stringValue.equals("on") || stringValue.equals("1")) 637 { 638 setValue(true); 639 } 640 else if (stringValue.equals("false") || stringValue.equals("no") || 641 stringValue.equals("off") || stringValue.equals("0")) 642 { 643 setValue(false); 644 } 645 else 646 { 647 LocalizableMessage message = 648 ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get(getName(), stringValue); 649 throw new ConfigException(message); 650 } 651 } 652 else 653 { 654 throw new ConfigException(ERR_CONFIG_ATTR_INVALID_BOOLEAN_VALUE.get( 655 getName(), value.getClass().getName() + ":" + value)); 656 } 657 } 658 659 660 661 /** 662 * Creates a duplicate of this configuration attribute. 663 * 664 * @return A duplicate of this configuration attribute. 665 */ 666 public ConfigAttribute duplicate() 667 { 668 return new BooleanConfigAttribute(getName(), getDescription(), 669 requiresAdminAction(), activeValue, 670 pendingValue); 671 } 672}