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}