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