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