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.lang.reflect.Array;
033    import java.util.ArrayList;
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    import org.opends.server.types.DN;
046    import org.opends.server.types.DebugLogLevel;
047    
048    import static org.opends.server.config.ConfigConstants.*;
049    import static org.opends.server.loggers.debug.DebugLogger.*;
050    import org.opends.server.loggers.debug.DebugTracer;
051    import org.opends.server.loggers.ErrorLogger;
052    import static org.opends.messages.ConfigMessages.*;
053    /**
054     * This class defines a DN configuration attribute, which can hold zero or more
055     * DN values.
056     */
057    @org.opends.server.types.PublicAPI(
058         stability=org.opends.server.types.StabilityLevel.VOLATILE,
059         mayInstantiate=true,
060         mayExtend=false,
061         mayInvoke=true)
062    public final class DNConfigAttribute
063           extends ConfigAttribute
064    {
065      /**
066       * The tracer object for the debug logger.
067       */
068      private static final DebugTracer TRACER = getTracer();
069    
070    
071    
072    
073      // The set of active values for this attribute.
074      private List<DN> activeValues;
075    
076      // The set of pending values for this attribute.
077      private List<DN> pendingValues;
078    
079    
080    
081      /**
082       * Creates a new DN configuration attribute stub with the provided information
083       * but no values.  The values will be set using the
084       * <CODE>setInitialValue</CODE> method.
085       *
086       * @param  name                 The name for this configuration attribute.
087       * @param  description          The description for this configuration
088       *                              attribute.
089       * @param  isRequired           Indicates whether this configuration attribute
090       *                              is required to have at least one value.
091       * @param  isMultiValued        Indicates whether this configuration attribute
092       *                              may have multiple values.
093       * @param  requiresAdminAction  Indicates whether changes to this
094       *                              configuration attribute require administrative
095       *                              action before they will take effect.
096       */
097      public DNConfigAttribute(String name, Message description, boolean isRequired,
098                               boolean isMultiValued, boolean requiresAdminAction)
099      {
100        super(name, description, isRequired, isMultiValued, requiresAdminAction);
101    
102    
103        activeValues  = new ArrayList<DN>();
104        pendingValues = activeValues;
105      }
106    
107    
108    
109      /**
110       * Creates a new DN configuration attribute with the provided information.  No
111       * validation will be performed on the provided value.
112       *
113       * @param  name                 The name for this configuration attribute.
114       * @param  description          The description for this configuration
115       *                              attribute.
116       * @param  isRequired           Indicates whether this configuration attribute
117       *                              is required to have at least one value.
118       * @param  isMultiValued        Indicates whether this configuration attribute
119       *                              may have multiple values.
120       * @param  requiresAdminAction  Indicates whether changes to this
121       *                              configuration attribute require administrative
122       *                              action before they will take effect.
123       * @param  value                The value for this DN configuration attribute.
124       */
125      public DNConfigAttribute(String name, Message description, boolean isRequired,
126                               boolean isMultiValued, boolean requiresAdminAction,
127                               DN value)
128      {
129        super(name, description, isRequired, isMultiValued, requiresAdminAction,
130              getValueSet(value));
131    
132    
133        if (value == null)
134        {
135          activeValues = new ArrayList<DN>();
136        }
137        else
138        {
139          activeValues = new ArrayList<DN>(1);
140          activeValues.add(value);
141        }
142    
143        pendingValues = activeValues;
144      }
145    
146    
147    
148      /**
149       * Creates a new DN configuration attribute with the provided information.  No
150       * validation will be performed on the provided values.
151       *
152       * @param  name                 The name for this configuration attribute.
153       * @param  description          The description for this configuration
154       *                              attribute.
155       * @param  isRequired           Indicates whether this configuration attribute
156       *                              is required to have at least one value.
157       * @param  isMultiValued        Indicates whether this configuration attribute
158       *                              may have multiple values.
159       * @param  requiresAdminAction  Indicates whether changes to this
160       *                              configuration attribute require administrative
161       *                              action before they will take effect.
162       * @param  values               The set of values for this configuration
163       *                              attribute.
164       */
165      public DNConfigAttribute(String name, Message description, boolean isRequired,
166                               boolean isMultiValued, boolean requiresAdminAction,
167                               List<DN> values)
168      {
169        super(name, description, isRequired, isMultiValued, requiresAdminAction,
170              getValueSet(values));
171    
172    
173        if (values == null)
174        {
175          activeValues  = new ArrayList<DN>();
176          pendingValues = activeValues;
177        }
178        else
179        {
180          activeValues  = values;
181          pendingValues = activeValues;
182        }
183      }
184    
185    
186    
187      /**
188       * Creates a new DN configuration attribute with the provided information.  No
189       * validation will be performed on the provided values.
190       *
191       * @param  name                 The name for this configuration attribute.
192       * @param  description          The description for this configuration
193       *                              attribute.
194       * @param  isRequired           Indicates whether this configuration attribute
195       *                              is required to have at least one value.
196       * @param  isMultiValued        Indicates whether this configuration attribute
197       *                              may have multiple values.
198       * @param  requiresAdminAction  Indicates whether changes to this
199       *                              configuration attribute require administrative
200       *                              action before they will take effect.
201       * @param  activeValues         The set of active values for this
202       *                              configuration attribute.
203       * @param  pendingValues        The set of pending values for this
204       *                              configuration attribute.
205       */
206      public DNConfigAttribute(String name, Message description, boolean isRequired,
207                               boolean isMultiValued, boolean requiresAdminAction,
208                               List<DN> activeValues, List<DN> pendingValues)
209      {
210        super(name, description, isRequired, isMultiValued, requiresAdminAction,
211              getValueSet(activeValues), (pendingValues != null),
212              getValueSet(pendingValues));
213    
214    
215        if (activeValues == null)
216        {
217          this.activeValues = new ArrayList<DN>();
218        }
219        else
220        {
221          this.activeValues = activeValues;
222        }
223    
224        if (pendingValues == null)
225        {
226          this.pendingValues = this.activeValues;
227        }
228        else
229        {
230          this.pendingValues = pendingValues;
231        }
232      }
233    
234    
235    
236      /**
237       * Retrieves the name of the data type for this configuration attribute.  This
238       * is for informational purposes (e.g., inclusion in method signatures and
239       * other kinds of descriptions) and does not necessarily need to map to an
240       * actual Java type.
241       *
242       * @return  The name of the data type for this configuration attribute.
243       */
244      public String getDataType()
245      {
246        return "DN";
247      }
248    
249    
250    
251      /**
252       * Retrieves the attribute syntax for this configuration attribute.
253       *
254       * @return  The attribute syntax for this configuration attribute.
255       */
256      public AttributeSyntax getSyntax()
257      {
258        return DirectoryServer.getDefaultStringSyntax();
259      }
260    
261    
262    
263      /**
264       * Retrieves the active value for this configuration attribute as a DN.  This
265       * is only valid for single-valued attributes that have a value.
266       *
267       * @return  The active value for this configuration attribute as a DN.
268       *
269       * @throws  ConfigException  If this attribute does not have exactly one
270       *                           active value.
271       */
272      public DN activeValue()
273             throws ConfigException
274      {
275        if ((activeValues == null) || activeValues.isEmpty())
276        {
277          Message message = ERR_CONFIG_ATTR_NO_STRING_VALUE.get(getName());
278          throw new ConfigException(message);
279        }
280    
281        if (activeValues.size() > 1)
282        {
283          Message message = ERR_CONFIG_ATTR_MULTIPLE_STRING_VALUES.get(getName());
284          throw new ConfigException(message);
285        }
286    
287        return activeValues.get(0);
288      }
289    
290    
291    
292      /**
293       * Retrieves the set of active values for this configuration attribute.
294       *
295       * @return  The set of active values for this configuration attribute.
296       */
297      public List<DN> activeValues()
298      {
299        return activeValues;
300      }
301    
302    
303    
304      /**
305       * Retrieves the pending value for this configuration attribute as a DN.
306       * This is only valid for single-valued attributes that have a value.  If this
307       * attribute does not have any pending values, then the active value will be
308       * returned.
309       *
310       * @return  The pending value for this configuration attribute as a DN.
311       *
312       * @throws  ConfigException  If this attribute does not have exactly one
313       *                           pending value.
314       */
315      public DN pendingValue()
316             throws ConfigException
317      {
318        if (! hasPendingValues())
319        {
320          return activeValue();
321        }
322    
323        if ((pendingValues == null) || pendingValues.isEmpty())
324        {
325          Message message = ERR_CONFIG_ATTR_NO_STRING_VALUE.get(getName());
326          throw new ConfigException(message);
327        }
328    
329        if (pendingValues.size() > 1)
330        {
331          Message message = ERR_CONFIG_ATTR_MULTIPLE_STRING_VALUES.get(getName());
332          throw new ConfigException(message);
333        }
334    
335        return pendingValues.get(0);
336      }
337    
338    
339    
340      /**
341       * Retrieves the set of pending values for this configuration attribute.  If
342       * there are no pending values, then the set of active values will be
343       * returned.
344       *
345       * @return  The set of pending values for this configuration attribute.
346       */
347      public List<DN> pendingValues()
348      {
349        if (! hasPendingValues())
350        {
351          return activeValues;
352        }
353    
354        return pendingValues;
355      }
356    
357    
358    
359      /**
360       * Sets the value for this DN configuration attribute.
361       *
362       * @param  value  The value for this DN configuration attribute.
363       *
364       * @throws  ConfigException  If the provided value is not acceptable.
365       */
366      public void setValue(DN value)
367             throws ConfigException
368      {
369        if (value == null)
370        {
371          Message message = ERR_CONFIG_ATTR_DN_NULL.get(getName());
372          throw new ConfigException(message);
373        }
374    
375        if (requiresAdminAction())
376        {
377          pendingValues = new ArrayList<DN>(1);
378          pendingValues.add(value);
379          setPendingValues(getValueSet(value));
380        }
381        else
382        {
383          activeValues.clear();
384          activeValues.add(value);
385          pendingValues = activeValues;
386          setActiveValues(getValueSet(value));
387        }
388      }
389    
390    
391    
392      /**
393       * Sets the values for this DN configuration attribute.
394       *
395       * @param  values  The set of values for this DN configuration attribute.
396       *
397       * @throws  ConfigException  If the provided value set or any of the
398       *                           individual values are not acceptable.
399       */
400      public void setValues(List<DN> values)
401             throws ConfigException
402      {
403        // First check if the set is empty and if that is allowed.
404        if ((values == null) || (values.isEmpty()))
405        {
406          if (isRequired())
407          {
408            Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
409            throw new ConfigException(message);
410          }
411          else
412          {
413            if (requiresAdminAction())
414            {
415              setPendingValues(new LinkedHashSet<AttributeValue>(0));
416              pendingValues = new ArrayList<DN>();
417            }
418            else
419            {
420              setActiveValues(new LinkedHashSet<AttributeValue>(0));
421              activeValues.clear();
422            }
423          }
424        }
425    
426    
427        // Next check if the set contains multiple values and if that is allowed.
428        int numValues = values.size();
429        if ((! isMultiValued()) && (numValues > 1))
430        {
431          Message message =
432              ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName());
433          throw new ConfigException(message);
434        }
435    
436    
437        // Iterate through all the provided values, make sure that they are
438        // acceptable, and build the value set.
439        LinkedHashSet<AttributeValue> valueSet =
440             new LinkedHashSet<AttributeValue>(numValues);
441        for (DN value : values)
442        {
443          if (value == null)
444          {
445            Message message = ERR_CONFIG_ATTR_DN_NULL.get(getName());
446            throw new ConfigException(message);
447          }
448    
449          AttributeValue attrValue =
450               new AttributeValue(new ASN1OctetString(value.toString()),
451                                  new ASN1OctetString(value.toNormalizedString()));
452    
453          if (valueSet.contains(attrValue))
454          {
455            Message message =
456                ERR_CONFIG_ATTR_ADD_VALUES_ALREADY_EXISTS.get(
457                        getName(), String.valueOf(value));
458            throw new ConfigException(message);
459          }
460    
461          valueSet.add(attrValue);
462        }
463    
464    
465        // Apply this value set to the new active or pending value set.
466        if (requiresAdminAction())
467        {
468          pendingValues = values;
469          setPendingValues(valueSet);
470        }
471        else
472        {
473          activeValues  = values;
474          pendingValues = activeValues;
475          setActiveValues(valueSet);
476        }
477      }
478    
479    
480    
481      /**
482       * Creates the appropriate value set with the provided value.
483       *
484       * @param  value  The value to use to create the value set.
485       *
486       * @return  The constructed value set.
487       */
488      private static LinkedHashSet<AttributeValue> getValueSet(DN value)
489      {
490        LinkedHashSet<AttributeValue> valueSet;
491        if (value == null)
492        {
493          valueSet = new LinkedHashSet<AttributeValue>(0);
494        }
495        else
496        {
497          valueSet = new LinkedHashSet<AttributeValue>(1);
498          valueSet.add(new AttributeValue(new ASN1OctetString(value.toString()),
499                                new ASN1OctetString(value.toNormalizedString())));
500        }
501    
502        return valueSet;
503      }
504    
505    
506    
507      /**
508       * Creates the appropriate value set with the provided values.
509       *
510       * @param  values  The values to use to create the value set.
511       *
512       * @return  The constructed value set.
513       */
514      private static LinkedHashSet<AttributeValue> getValueSet(List<DN> values)
515      {
516        if (values == null)
517        {
518          return null;
519        }
520    
521        LinkedHashSet<AttributeValue> valueSet =
522             new LinkedHashSet<AttributeValue>(values.size());
523    
524        for (DN value : values)
525        {
526          valueSet.add(new AttributeValue(new ASN1OctetString(value.toString()),
527                                new ASN1OctetString(value.toNormalizedString())));
528        }
529    
530        return valueSet;
531      }
532    
533    
534    
535      /**
536       * Applies the set of pending values, making them the active values for this
537       * configuration attribute.  This will not take any action if there are no
538       * pending values.
539       */
540      public void applyPendingValues()
541      {
542        if (! hasPendingValues())
543        {
544          return;
545        }
546    
547        super.applyPendingValues();
548        activeValues = pendingValues;
549      }
550    
551    
552    
553      /**
554       * Indicates whether the provided value is acceptable for use in this
555       * attribute.  If it is not acceptable, then the reason should be written into
556       * the provided buffer.
557       *
558       * @param  value         The value for which to make the determination.
559       * @param  rejectReason  A buffer into which a human-readable reason for the
560       *                       reject may be written.
561       *
562       * @return  <CODE>true</CODE> if the provided value is acceptable for use in
563       *          this attribute, or <CODE>false</CODE> if not.
564       */
565      public boolean valueIsAcceptable(AttributeValue value,
566                                       StringBuilder rejectReason)
567      {
568        // Make sure that the value is not null.
569        if (value == null)
570        {
571          rejectReason.append(ERR_CONFIG_ATTR_DN_NULL.get(getName()));
572          return false;
573        }
574    
575    
576        // Make sure that it can be parsed as a DN.
577        try
578        {
579          DN.decode(value.getStringValue());
580        }
581        catch (Exception e)
582        {
583          if (debugEnabled())
584          {
585            TRACER.debugCaught(DebugLogLevel.ERROR, e);
586          }
587    
588          rejectReason.append(ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
589                  value.getStringValue(), getName(),
590                  String.valueOf(e)));
591          return false;
592        }
593    
594    
595        return true;
596      }
597    
598    
599    
600      /**
601       * Converts the provided set of strings to a corresponding set of attribute
602       * values.
603       *
604       * @param  valueStrings   The set of strings to be converted into attribute
605       *                        values.
606       * @param  allowFailures  Indicates whether the decoding process should allow
607       *                        any failures in which one or more values could be
608       *                        decoded but at least one could not.  If this is
609       *                        <CODE>true</CODE> and such a condition is acceptable
610       *                        for the underlying attribute type, then the returned
611       *                        set of values should simply not include those
612       *                        undecodable values.
613       *
614       * @return  The set of attribute values converted from the provided strings.
615       *
616       * @throws  ConfigException  If an unrecoverable problem occurs while
617       *                           performing the conversion.
618       */
619      public LinkedHashSet<AttributeValue>
620                  stringsToValues(List<String> valueStrings,
621                                  boolean allowFailures)
622             throws ConfigException
623      {
624        if ((valueStrings == null) || valueStrings.isEmpty())
625        {
626          if (isRequired())
627          {
628            Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
629            throw new ConfigException(message);
630          }
631          else
632          {
633            return new LinkedHashSet<AttributeValue>();
634          }
635        }
636    
637    
638        int numValues = valueStrings.size();
639        if ((! isMultiValued()) && (numValues > 1))
640        {
641          Message message =
642              ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(getName());
643          throw new ConfigException(message);
644        }
645    
646    
647        LinkedHashSet<AttributeValue> valueSet =
648             new LinkedHashSet<AttributeValue>(numValues);
649        for (String valueString : valueStrings)
650        {
651          if (valueString == null)
652          {
653            Message message = ERR_CONFIG_ATTR_DN_NULL.get(getName());
654            if (allowFailures)
655            {
656              ErrorLogger.logError(message);
657              continue;
658            }
659            else
660            {
661              throw new ConfigException(message);
662            }
663          }
664    
665    
666          DN dn;
667          try
668          {
669            dn = DN.decode(valueString);
670          }
671          catch (Exception e)
672          {
673            if (debugEnabled())
674            {
675              TRACER.debugCaught(DebugLogLevel.ERROR, e);
676            }
677    
678            Message message = ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
679                    valueString, getName(),
680                    String.valueOf(e));
681    
682            if (allowFailures)
683            {
684              ErrorLogger.logError(message);
685              continue;
686            }
687            else
688            {
689              throw new ConfigException(message);
690            }
691          }
692    
693    
694          valueSet.add(new AttributeValue(new ASN1OctetString(dn.toString()),
695                                new ASN1OctetString(dn.toNormalizedString())));
696        }
697    
698    
699        // If this method was configured to continue on error, then it is possible
700        // that we ended up with an empty list.  Check to see if this is a required
701        // attribute and if so deal with it accordingly.
702        if ((isRequired()) && valueSet.isEmpty())
703        {
704          Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(getName());
705          throw new ConfigException(message);
706        }
707    
708    
709        return valueSet;
710      }
711    
712    
713    
714      /**
715       * Converts the set of active values for this configuration attribute into a
716       * set of strings that may be stored in the configuration or represented over
717       * protocol.  The string representation used by this method should be
718       * compatible with the decoding used by the <CODE>stringsToValues</CODE>
719       * method.
720       *
721       * @return  The string representations of the set of active values for this
722       *          configuration attribute.
723       */
724      public List<String> activeValuesToStrings()
725      {
726        ArrayList<String> valueStrings = new ArrayList<String>(activeValues.size());
727        for (DN dn : activeValues)
728        {
729          valueStrings.add(dn.toString());
730        }
731    
732        return valueStrings;
733      }
734    
735    
736    
737      /**
738       * Converts the set of pending values for this configuration attribute into a
739       * set of strings that may be stored in the configuration or represented over
740       * protocol.  The string representation used by this method should be
741       * compatible with the decoding used by the <CODE>stringsToValues</CODE>
742       * method.
743       *
744       * @return  The string representations of the set of pending values for this
745       *          configuration attribute, or <CODE>null</CODE> if there are no
746       *          pending values.
747       */
748      public List<String> pendingValuesToStrings()
749      {
750        if (hasPendingValues())
751        {
752          ArrayList<String> valueStrings =
753               new ArrayList<String>(pendingValues.size());
754          for (DN dn : pendingValues)
755          {
756            valueStrings.add(dn.toString());
757          }
758    
759          return valueStrings;
760        }
761        else
762        {
763          return null;
764        }
765      }
766    
767    
768    
769      /**
770       * Retrieves a new configuration attribute of this type that will contain the
771       * values from the provided attribute.
772       *
773       * @param  attributeList  The list of attributes to use to create the config
774       *                        attribute.  The list must contain either one or two
775       *                        elements, with both attributes having the same base
776       *                        name and the only option allowed is ";pending" and
777       *                        only if this attribute is one that requires admin
778       *                        action before a change may take effect.
779       *
780       * @return  The generated configuration attribute.
781       *
782       * @throws  ConfigException  If the provided attribute cannot be treated as a
783       *                           configuration attribute of this type (e.g., if
784       *                           one or more of the values of the provided
785       *                           attribute are not suitable for an attribute of
786       *                           this type, or if this configuration attribute is
787       *                           single-valued and the provided attribute has
788       *                           multiple values).
789       */
790      public ConfigAttribute getConfigAttribute(List<Attribute> attributeList)
791             throws ConfigException
792      {
793        ArrayList<DN> activeValues  = null;
794        ArrayList<DN> pendingValues = null;
795    
796        for (Attribute a : attributeList)
797        {
798          if (a.hasOptions())
799          {
800            // This must be the pending value.
801            if (a.hasOption(OPTION_PENDING_VALUES))
802            {
803              if (pendingValues != null)
804              {
805                // We cannot have multiple pending value sets.
806                Message message =
807                    ERR_CONFIG_ATTR_MULTIPLE_PENDING_VALUE_SETS.get(a.getName());
808                throw new ConfigException(message);
809              }
810    
811    
812              LinkedHashSet<AttributeValue> values = a.getValues();
813              if (values.isEmpty())
814              {
815                if (isRequired())
816                {
817                  // This is illegal -- it must have a value.
818                  Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
819                  throw new ConfigException(message);
820                }
821                else
822                {
823                  // This is fine.  The pending value set can be empty.
824                  pendingValues = new ArrayList<DN>(0);
825                }
826              }
827              else
828              {
829                int numValues = values.size();
830                if ((numValues > 1) && (! isMultiValued()))
831                {
832                  // This is illegal -- the attribute is single-valued.
833                  Message message =
834                      ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
835                  throw new ConfigException(message);
836                }
837    
838                pendingValues = new ArrayList<DN>(numValues);
839                for (AttributeValue v : values)
840                {
841                  DN dn;
842                  try
843                  {
844                    dn = DN.decode(v.getStringValue());
845                  }
846                  catch (Exception e)
847                  {
848                    if (debugEnabled())
849                    {
850                      TRACER.debugCaught(DebugLogLevel.ERROR, e);
851                    }
852    
853                    Message message = ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
854                        v.getStringValue(), getName(), String.valueOf(e));
855                    throw new ConfigException(message, e);
856                  }
857    
858                  pendingValues.add(dn);
859                }
860              }
861            }
862            else
863            {
864              // This is illegal -- only the pending option is allowed for
865              // configuration attributes.
866              Message message =
867                  ERR_CONFIG_ATTR_OPTIONS_NOT_ALLOWED.get(a.getName());
868              throw new ConfigException(message);
869            }
870          }
871          else
872          {
873            // This must be the active value.
874            if (activeValues!= null)
875            {
876              // We cannot have multiple active value sets.
877              Message message =
878                  ERR_CONFIG_ATTR_MULTIPLE_ACTIVE_VALUE_SETS.get(a.getName());
879              throw new ConfigException(message);
880            }
881    
882    
883            LinkedHashSet<AttributeValue> values = a.getValues();
884            if (values.isEmpty())
885            {
886              if (isRequired())
887              {
888                // This is illegal -- it must have a value.
889                Message message = ERR_CONFIG_ATTR_IS_REQUIRED.get(a.getName());
890                throw new ConfigException(message);
891              }
892              else
893              {
894                // This is fine.  The active value set can be empty.
895                activeValues = new ArrayList<DN>(0);
896              }
897            }
898            else
899            {
900              int numValues = values.size();
901              if ((numValues > 1) && (! isMultiValued()))
902              {
903                // This is illegal -- the attribute is single-valued.
904                Message message =
905                    ERR_CONFIG_ATTR_SET_VALUES_IS_SINGLE_VALUED.get(a.getName());
906                throw new ConfigException(message);
907              }
908    
909              activeValues = new ArrayList<DN>(numValues);
910              for (AttributeValue v : values)
911              {
912                DN dn;
913                try
914                {
915                  dn = DN.decode(v.getStringValue());
916                }
917                catch (Exception e)
918                {
919                  if (debugEnabled())
920                  {
921                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
922                  }
923    
924                  Message message = ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
925                      v.getStringValue(), getName(), String.valueOf(e));
926                  throw new ConfigException(message, e);
927                }
928    
929                activeValues.add(dn);
930              }
931            }
932          }
933        }
934    
935        if (activeValues == null)
936        {
937          // This is not OK.  The value set must contain an active value.
938          Message message = ERR_CONFIG_ATTR_NO_ACTIVE_VALUE_SET.get(getName());
939          throw new ConfigException(message);
940        }
941    
942        if (pendingValues == null)
943        {
944          // This is OK.  We'll just use the active value set.
945          pendingValues = activeValues;
946        }
947    
948        return new DNConfigAttribute(getName(), getDescription(), isRequired(),
949                                     isMultiValued(), requiresAdminAction(),
950                                     activeValues, pendingValues);
951      }
952    
953    
954    
955      /**
956       * Retrieves a JMX attribute containing the requested value set for this
957       * configuration attribute (active or pending).
958       *
959       * @param pending indicates if pending or active  values are required.
960       *
961       * @return  A JMX attribute containing the active value set for this
962       *          configuration attribute, or <CODE>null</CODE> if it does not have
963       *          any active values.
964       */
965      private javax.management.Attribute _toJMXAttribute(boolean pending)
966      {
967        List<DN> requestedValues ;
968        String name ;
969        if (pending)
970        {
971            requestedValues = pendingValues ;
972            name = getName() + ";" + OPTION_PENDING_VALUES ;
973        }
974        else
975        {
976            requestedValues = activeValues ;
977            name = getName() ;
978        }
979    
980        if (isMultiValued())
981        {
982          String[] values = new String[requestedValues.size()];
983          for (int i=0; i < values.length; i++)
984          {
985            values[i] = requestedValues.get(i).toString();
986          }
987    
988          return new javax.management.Attribute(name, values);
989        }
990        else
991        {
992          if (requestedValues.isEmpty())
993          {
994            return null;
995          }
996          else
997          {
998            DN dn = requestedValues.get(0);
999            return new javax.management.Attribute(name, dn.toString());
1000          }
1001        }
1002      }
1003    
1004      /**
1005       * Retrieves a JMX attribute containing the active value set for this
1006       * configuration attribute.
1007       *
1008       * @return  A JMX attribute containing the active value set for this
1009       *          configuration attribute, or <CODE>null</CODE> if it does not have
1010       *          any active values.
1011       */
1012      public  javax.management.Attribute toJMXAttribute()
1013      {
1014          return _toJMXAttribute(false) ;
1015      }
1016    
1017      /**
1018       * Retrieves a JMX attribute containing the pending value set for this
1019       * configuration attribute.
1020       *
1021       * @return  A JMX attribute containing the pending value set for this
1022       *          configuration attribute.
1023       */
1024      public  javax.management.Attribute toJMXAttributePending()
1025      {
1026          return _toJMXAttribute(true) ;
1027      }
1028    
1029      /**
1030       * Adds information about this configuration attribute to the provided JMX
1031       * attribute list.  If this configuration attribute requires administrative
1032       * action before changes take effect and it has a set of pending values, then
1033       * two attributes should be added to the list -- one for the active value
1034       * and one for the pending value.  The pending value should be named with
1035       * the pending option.
1036       *
1037       * @param  attributeList  The attribute list to which the JMX attribute(s)
1038       *                        should be added.
1039       */
1040      public void toJMXAttribute(AttributeList attributeList)
1041      {
1042        if (activeValues.size() > 0)
1043        {
1044          if (isMultiValued())
1045          {
1046            String[] values = new String[activeValues.size()];
1047            for (int i=0; i < values.length; i++)
1048            {
1049              values[i] = activeValues.get(i).toString();
1050            }
1051    
1052            attributeList.add(new javax.management.Attribute(getName(), values));
1053          }
1054          else
1055          {
1056            attributeList.add(new javax.management.Attribute(getName(),
1057                                       activeValues.get(0).toString()));
1058          }
1059        }
1060        else
1061        {
1062          if (isMultiValued())
1063          {
1064            attributeList.add(new javax.management.Attribute(getName(),
1065                                                             new String[0]));
1066          }
1067          else
1068          {
1069            attributeList.add(new javax.management.Attribute(getName(), null));
1070          }
1071        }
1072    
1073    
1074        if (requiresAdminAction() && (pendingValues != null) &&
1075            (pendingValues != activeValues))
1076        {
1077          String name = getName() + ";" + OPTION_PENDING_VALUES;
1078    
1079          if (isMultiValued())
1080          {
1081            String[] values = new String[pendingValues.size()];
1082            for (int i=0; i < values.length; i++)
1083            {
1084              values[i] = pendingValues.get(i).toString();
1085            }
1086    
1087            attributeList.add(new javax.management.Attribute(name, values));
1088          }
1089          else if (! pendingValues.isEmpty())
1090          {
1091            attributeList.add(new javax.management.Attribute(name,
1092                                       pendingValues.get(0).toString()));
1093          }
1094        }
1095      }
1096    
1097    
1098    
1099      /**
1100       * Adds information about this configuration attribute to the provided list in
1101       * the form of a JMX <CODE>MBeanAttributeInfo</CODE> object.  If this
1102       * configuration attribute requires administrative action before changes take
1103       * effect and it has a set of pending values, then two attribute info objects
1104       * should be added to the list -- one for the active value (which should be
1105       * read-write) and one for the pending value (which should be read-only).  The
1106       * pending value should be named with the pending option.
1107       *
1108       * @param  attributeInfoList  The list to which the attribute information
1109       *                            should be added.
1110       */
1111      public void toJMXAttributeInfo(List<MBeanAttributeInfo> attributeInfoList)
1112      {
1113        if (isMultiValued())
1114        {
1115          attributeInfoList.add(new MBeanAttributeInfo(getName(),
1116                                                       JMX_TYPE_STRING_ARRAY,
1117                                                       String.valueOf(
1118                                                               getDescription()),
1119                                                       true, true, false));
1120        }
1121        else
1122        {
1123          attributeInfoList.add(new MBeanAttributeInfo(getName(),
1124                                                       String.class.getName(),
1125                                                       String.valueOf(
1126                                                               getDescription()),
1127                                                       true, true, false));
1128        }
1129    
1130    
1131        if (requiresAdminAction())
1132        {
1133          String name = getName() + ";" + OPTION_PENDING_VALUES;
1134    
1135          if (isMultiValued())
1136          {
1137            attributeInfoList.add(new MBeanAttributeInfo(name,
1138                                                         JMX_TYPE_STRING_ARRAY,
1139                                                         String.valueOf(
1140                                                                 getDescription()),
1141                                                         true, false, false));
1142          }
1143          else
1144          {
1145            attributeInfoList.add(new MBeanAttributeInfo(name,
1146                                                         String.class.getName(),
1147                                                         String.valueOf(
1148                                                                 getDescription()),
1149                                                         true, false, false));
1150          }
1151        }
1152      }
1153    
1154    
1155    
1156      /**
1157       * Retrieves a JMX <CODE>MBeanParameterInfo</CODE> object that describes this
1158       * configuration attribute.
1159       *
1160       * @return  A JMX <CODE>MBeanParameterInfo</CODE> object that describes this
1161       *          configuration attribute.
1162       */
1163      public MBeanParameterInfo toJMXParameterInfo()
1164      {
1165        if (isMultiValued())
1166        {
1167          return new MBeanParameterInfo(getName(), JMX_TYPE_STRING_ARRAY,
1168                                        String.valueOf(getDescription()));
1169        }
1170        else
1171        {
1172          return new MBeanParameterInfo(getName(), String.class.getName(),
1173                                        String.valueOf(getDescription()));
1174        }
1175      }
1176    
1177    
1178    
1179      /**
1180       * Attempts to set the value of this configuration attribute based on the
1181       * information in the provided JMX attribute.
1182       *
1183       * @param  jmxAttribute  The JMX attribute to use to attempt to set the value
1184       *                       of this configuration attribute.
1185       *
1186       * @throws  ConfigException  If the provided JMX attribute does not have an
1187       *                           acceptable value for this configuration
1188       *                           attribute.
1189       */
1190      public void setValue(javax.management.Attribute jmxAttribute)
1191             throws ConfigException
1192      {
1193        Object value = jmxAttribute.getValue();
1194        if (value == null)
1195        {
1196          Message message = ERR_CONFIG_ATTR_DN_NULL.get(getName());
1197          throw new ConfigException(message);
1198        }
1199        else if (value instanceof DN)
1200        {
1201          setValue((DN) value);
1202        }
1203        if (value instanceof String)
1204        {
1205          DN dn;
1206          try
1207          {
1208            dn = DN.decode((String) value);
1209          }
1210          catch (Exception e)
1211          {
1212            if (debugEnabled())
1213            {
1214              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1215            }
1216    
1217            Message message = ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
1218                (String) value, getName(), String.valueOf(e));
1219            throw new ConfigException(message, e);
1220          }
1221    
1222          setValue(dn);
1223        }
1224        else if (value.getClass().isArray())
1225        {
1226          String componentType = value.getClass().getComponentType().getName();
1227          int length = Array.getLength(value);
1228    
1229    
1230          if (componentType.equals(DN.class.getName()))
1231          {
1232            ArrayList<DN> dnList = new ArrayList<DN>(length);
1233            for (int i=0; i < length; i++)
1234            {
1235              dnList.add((DN) Array.get(value, i));
1236            }
1237    
1238            setValues(dnList);
1239          }
1240          else if (componentType.equals(String.class.getName()))
1241          {
1242            try
1243            {
1244              ArrayList<DN> values = new ArrayList<DN>(length);
1245              for (int i=0; i < length; i++)
1246              {
1247                String valueStr = (String) Array.get(value, i);
1248    
1249                DN dn;
1250                try
1251                {
1252                  dn = DN.decode(valueStr);
1253                }
1254                catch (Exception e)
1255                {
1256                  if (debugEnabled())
1257                  {
1258                    TRACER.debugCaught(DebugLogLevel.ERROR, e);
1259                  }
1260    
1261                  Message message = ERR_CONFIG_ATTR_DN_CANNOT_PARSE.get(
1262                      valueStr, getName(), String.valueOf(e));
1263                  throw new ConfigException(message, e);
1264                }
1265    
1266                values.add(dn);
1267              }
1268    
1269              setValues(values);
1270            }
1271            catch (ConfigException ce)
1272            {
1273              if (debugEnabled())
1274              {
1275                TRACER.debugCaught(DebugLogLevel.ERROR, ce);
1276              }
1277    
1278              throw ce;
1279            }
1280            catch (Exception e)
1281            {
1282              if (debugEnabled())
1283              {
1284                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1285              }
1286    
1287              Message message = ERR_CONFIG_ATTR_INVALID_DN_VALUE.get(
1288                  getName(), String.valueOf(value), String.valueOf(e));
1289              throw new ConfigException(message, e);
1290            }
1291          }
1292          else
1293          {
1294            Message message =
1295                ERR_CONFIG_ATTR_DN_INVALID_ARRAY_TYPE.get(
1296                        String.valueOf(jmxAttribute),
1297                        String.valueOf(componentType));
1298            throw new ConfigException(message);
1299          }
1300        }
1301        else
1302        {
1303          Message message = ERR_CONFIG_ATTR_DN_INVALID_TYPE.get(
1304              String.valueOf(value), getName(), value.getClass().getName());
1305          throw new ConfigException(message);
1306        }
1307      }
1308    
1309    
1310    
1311      /**
1312       * Creates a duplicate of this configuration attribute.
1313       *
1314       * @return  A duplicate of this configuration attribute.
1315       */
1316      public ConfigAttribute duplicate()
1317      {
1318        return new DNConfigAttribute(getName(), getDescription(), isRequired(),
1319                                     isMultiValued(), requiresAdminAction(),
1320                                     activeValues, pendingValues);
1321      }
1322    }
1323