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