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}