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