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