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 2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.admin.server;
029
030
031
032 import static org.opends.server.loggers.debug.DebugLogger.*;
033 import static org.opends.server.util.StaticUtils.*;
034
035 import java.util.ArrayList;
036 import java.util.Collection;
037 import java.util.Collections;
038 import java.util.HashMap;
039 import java.util.LinkedList;
040 import java.util.List;
041 import java.util.Map;
042 import java.util.Set;
043 import java.util.SortedSet;
044 import java.util.TreeSet;
045
046 import org.opends.messages.AdminMessages;
047 import org.opends.messages.Message;
048 import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
049 import org.opends.server.admin.AbstractManagedObjectDefinition;
050 import org.opends.server.admin.AggregationPropertyDefinition;
051 import org.opends.server.admin.AliasDefaultBehaviorProvider;
052 import org.opends.server.admin.Configuration;
053 import org.opends.server.admin.ConfigurationClient;
054 import org.opends.server.admin.DefaultBehaviorException;
055 import org.opends.server.admin.DefaultBehaviorProviderVisitor;
056 import org.opends.server.admin.DefinedDefaultBehaviorProvider;
057 import org.opends.server.admin.DefinitionDecodingException;
058 import org.opends.server.admin.DefinitionResolver;
059 import org.opends.server.admin.IllegalPropertyValueException;
060 import org.opends.server.admin.IllegalPropertyValueStringException;
061 import org.opends.server.admin.InstantiableRelationDefinition;
062 import org.opends.server.admin.LDAPProfile;
063 import org.opends.server.admin.ManagedObjectDefinition;
064 import org.opends.server.admin.ManagedObjectPath;
065 import org.opends.server.admin.PropertyDefinition;
066 import org.opends.server.admin.PropertyDefinitionVisitor;
067 import org.opends.server.admin.PropertyException;
068 import org.opends.server.admin.PropertyIsMandatoryException;
069 import org.opends.server.admin.PropertyIsSingleValuedException;
070 import org.opends.server.admin.PropertyNotFoundException;
071 import org.opends.server.admin.PropertyOption;
072 import org.opends.server.admin.Reference;
073 import org.opends.server.admin.RelationDefinition;
074 import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
075 import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
076 import org.opends.server.admin.UnknownPropertyDefinitionException;
077 import org.opends.server.admin.DefinitionDecodingException.Reason;
078 import org.opends.server.admin.std.meta.RootCfgDefn;
079 import org.opends.server.admin.std.server.RootCfg;
080 import org.opends.server.api.AttributeValueDecoder;
081 import org.opends.server.config.ConfigEntry;
082 import org.opends.server.config.ConfigException;
083 import org.opends.server.core.DirectoryServer;
084 import org.opends.server.loggers.debug.DebugTracer;
085 import org.opends.server.types.AttributeType;
086 import org.opends.server.types.AttributeValue;
087 import org.opends.server.types.DN;
088 import org.opends.server.types.DebugLogLevel;
089 import org.opends.server.types.DirectoryException;
090
091
092
093 /**
094 * Server management connection context.
095 */
096 public final class ServerManagementContext {
097
098 /**
099 * A default behavior visitor used for retrieving the default values
100 * of a property.
101 *
102 * @param <T>
103 * The type of the property.
104 */
105 private class DefaultValueFinder<T> implements
106 DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
107
108 // Any exception that occurred whilst retrieving inherited default
109 // values.
110 private DefaultBehaviorException exception = null;
111
112 // Optional new configuration entry which does not yet exist in
113 // the configuration back-end.
114 private ConfigEntry newConfigEntry;
115
116 // The path of the managed object containing the next property.
117 private ManagedObjectPath<?, ?> nextPath = null;
118
119 // The next property whose default values were required.
120 private PropertyDefinition<T> nextProperty = null;
121
122
123
124 // Private constructor.
125 private DefaultValueFinder(ConfigEntry newConfigEntry) {
126 this.newConfigEntry = newConfigEntry;
127 }
128
129
130
131 /**
132 * {@inheritDoc}
133 */
134 public Collection<T> visitAbsoluteInherited(
135 AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
136 try {
137 return getInheritedProperty(d.getManagedObjectPath(), d
138 .getManagedObjectDefinition(), d.getPropertyName());
139 } catch (DefaultBehaviorException e) {
140 exception = e;
141 return Collections.emptySet();
142 }
143 }
144
145
146
147 /**
148 * {@inheritDoc}
149 */
150 public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
151 return Collections.emptySet();
152 }
153
154
155
156 /**
157 * {@inheritDoc}
158 */
159 public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
160 Void p) {
161 Collection<String> stringValues = d.getDefaultValues();
162 List<T> values = new ArrayList<T>(stringValues.size());
163
164 for (String stringValue : stringValues) {
165 try {
166 values.add(nextProperty.decodeValue(stringValue));
167 } catch (IllegalPropertyValueStringException e) {
168 exception = new DefaultBehaviorException(nextProperty, e);
169 break;
170 }
171 }
172
173 return values;
174 }
175
176
177
178 /**
179 * {@inheritDoc}
180 */
181 public Collection<T> visitRelativeInherited(
182 RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
183 try {
184 return getInheritedProperty(d.getManagedObjectPath(nextPath), d
185 .getManagedObjectDefinition(), d.getPropertyName());
186 } catch (DefaultBehaviorException e) {
187 exception = e;
188 return Collections.emptySet();
189 }
190 }
191
192
193
194 /**
195 * {@inheritDoc}
196 */
197 public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
198 Void p) {
199 return Collections.emptySet();
200 }
201
202
203
204 // Find the default values for the next path/property.
205 private Collection<T> find(ManagedObjectPath<?, ?> p,
206 PropertyDefinition<T> pd) throws DefaultBehaviorException {
207 nextPath = p;
208 nextProperty = pd;
209
210 Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
211 this, null);
212
213 if (exception != null) {
214 throw exception;
215 }
216
217 if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
218 throw new DefaultBehaviorException(pd,
219 new PropertyIsSingleValuedException(pd));
220 }
221
222 return values;
223 }
224
225
226
227 // Get an inherited property value.
228 @SuppressWarnings("unchecked")
229 private Collection<T> getInheritedProperty(ManagedObjectPath target,
230 AbstractManagedObjectDefinition<?, ?> d, String propertyName)
231 throws DefaultBehaviorException {
232 // First check that the requested type of managed object
233 // corresponds to the path.
234 AbstractManagedObjectDefinition<?, ?> supr = target
235 .getManagedObjectDefinition();
236 if (!supr.isParentOf(d)) {
237 throw new DefaultBehaviorException(
238 nextProperty, new DefinitionDecodingException(supr,
239 Reason.WRONG_TYPE_INFORMATION));
240 }
241
242 // Save the current property in case of recursion.
243 PropertyDefinition<T> pd1 = nextProperty;
244
245 try {
246 // Get the actual managed object definition.
247 DN dn = DNBuilder.create(target);
248 ConfigEntry configEntry;
249 if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
250 configEntry = newConfigEntry;
251 } else {
252 configEntry = getManagedObjectConfigEntry(dn);
253 }
254
255 DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
256 ManagedObjectDefinition<?, ?> mod = d
257 .resolveManagedObjectDefinition(resolver);
258
259 PropertyDefinition<T> pd2;
260 try {
261 PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
262 pd2 = pd1.getClass().cast(pdTmp);
263 } catch (IllegalArgumentException e) {
264 throw new PropertyNotFoundException(propertyName);
265 } catch (ClassCastException e) {
266 // FIXME: would be nice to throw a better exception here.
267 throw new PropertyNotFoundException(propertyName);
268 }
269
270 List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
271 if (values.isEmpty()) {
272 // Recursively retrieve this property's default values.
273 Collection<T> tmp = find(target, pd2);
274 Collection<T> pvalues = new ArrayList<T>(tmp.size());
275 for (T value : tmp) {
276 pd1.validateValue(value);
277 pvalues.add(value);
278 }
279 return pvalues;
280 } else {
281 Collection<T> pvalues = new ArrayList<T>(values.size());
282 for (AttributeValue value : values) {
283 pvalues.add(ValueDecoder.decode(pd1, value));
284 }
285 return pvalues;
286 }
287 } catch (DefinitionDecodingException e) {
288 throw new DefaultBehaviorException(pd1, e);
289 } catch (PropertyNotFoundException e) {
290 throw new DefaultBehaviorException(pd1, e);
291 } catch (IllegalPropertyValueException e) {
292 throw new DefaultBehaviorException(pd1, e);
293 } catch (IllegalPropertyValueStringException e) {
294 throw new DefaultBehaviorException(pd1, e);
295 } catch (ConfigException e) {
296 throw new DefaultBehaviorException(pd1, e);
297 }
298 }
299 }
300
301
302
303 /**
304 * A definition resolver that determines the managed object
305 * definition from the object classes of a ConfigEntry.
306 */
307 private class MyDefinitionResolver implements DefinitionResolver {
308
309 // The config entry.
310 private final ConfigEntry entry;
311
312
313
314 // Private constructor.
315 private MyDefinitionResolver(ConfigEntry entry) {
316 this.entry = entry;
317 }
318
319
320
321 /**
322 * {@inheritDoc}
323 */
324 public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
325 String oc = LDAPProfile.getInstance().getObjectClass(d);
326 return entry.hasObjectClass(oc);
327 }
328 }
329
330
331
332 /**
333 * A visitor which is used to decode property LDAP values.
334 */
335 private static final class ValueDecoder extends
336 PropertyDefinitionVisitor<Object, String> {
337
338 /**
339 * Decodes the provided property LDAP value.
340 *
341 * @param <PD>
342 * The type of the property.
343 * @param pd
344 * The property definition.
345 * @param value
346 * The LDAP string representation.
347 * @return Returns the decoded LDAP value.
348 * @throws IllegalPropertyValueStringException
349 * If the property value could not be decoded because it
350 * was invalid.
351 */
352 public static <PD> PD decode(PropertyDefinition<PD> pd,
353 AttributeValue value) throws IllegalPropertyValueStringException {
354 String s = value.getStringValue();
355 return pd.castValue(pd.accept(new ValueDecoder(), s));
356 }
357
358
359
360 // Prevent instantiation.
361 private ValueDecoder() {
362 // No implementation required.
363 }
364
365
366
367 /**
368 * {@inheritDoc}
369 */
370 @Override
371 public <C extends ConfigurationClient, S extends Configuration>
372 Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
373 // Aggregations values are stored as full DNs in LDAP, but
374 // just their common name is exposed in the admin framework.
375 try {
376 Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
377 .getRelationDefinition(), p);
378 return reference.getName();
379 } catch (IllegalArgumentException e) {
380 throw new IllegalPropertyValueStringException(d, p);
381 }
382 }
383
384
385
386 /**
387 * {@inheritDoc}
388 */
389 @Override
390 public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
391 throws UnknownPropertyDefinitionException {
392 // By default the property definition's decoder will do.
393 return d.decodeValue(p);
394 }
395 }
396
397
398
399 // Singleton instance.
400 private final static ServerManagementContext INSTANCE =
401 new ServerManagementContext();
402
403 /**
404 * The root server managed object.
405 */
406 private static final ServerManagedObject<RootCfg> ROOT =
407 new ServerManagedObject<RootCfg>(
408 ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
409 .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
410
411 /**
412 * The tracer object for the debug logger.
413 */
414 private static final DebugTracer TRACER = getTracer();
415
416
417
418 /**
419 * Get the single server-side management context.
420 *
421 * @return Returns the single server-side management context.
422 */
423 public static ServerManagementContext getInstance() {
424 return INSTANCE;
425 }
426
427
428
429 // Private constructor.
430 private ServerManagementContext() {
431 // No implementation required.
432 }
433
434
435
436 /**
437 * Gets the named managed object.
438 *
439 * @param <C>
440 * The type of client managed object configuration that the
441 * path definition refers to.
442 * @param <S>
443 * The type of server managed object configuration that the
444 * path definition refers to.
445 * @param path
446 * The path of the managed object.
447 * @return Returns the named managed object.
448 * @throws ConfigException
449 * If the named managed object could not be found or if it
450 * could not be decoded.
451 */
452 @SuppressWarnings("unchecked")
453 public <C extends ConfigurationClient, S extends Configuration>
454 ServerManagedObject<? extends S> getManagedObject(
455 ManagedObjectPath<C, S> path) throws ConfigException {
456 // Be careful to handle the root configuration.
457 if (path.isEmpty()) {
458 return (ServerManagedObject<S>) getRootConfigurationManagedObject();
459 }
460
461 // Get the configuration entry.
462 DN targetDN = DNBuilder.create(path);
463 ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
464 try {
465 ServerManagedObject<? extends S> managedObject;
466 managedObject = decode(path, configEntry);
467
468 // Enforce any constraints.
469 managedObject.ensureIsUsable();
470
471 return managedObject;
472 } catch (DefinitionDecodingException e) {
473 throw ConfigExceptionFactory.getInstance()
474 .createDecodingExceptionAdaptor(targetDN, e);
475 } catch (ServerManagedObjectDecodingException e) {
476 throw ConfigExceptionFactory.getInstance()
477 .createDecodingExceptionAdaptor(e);
478 } catch (ConstraintViolationException e) {
479 throw ConfigExceptionFactory.getInstance()
480 .createDecodingExceptionAdaptor(e);
481 }
482 }
483
484
485
486 /**
487 * Gets the effective value of a property in the named managed
488 * object.
489 *
490 * @param <C>
491 * The type of client managed object configuration that the
492 * path definition refers to.
493 * @param <S>
494 * The type of server managed object configuration that the
495 * path definition refers to.
496 * @param <PD>
497 * The type of the property to be retrieved.
498 * @param path
499 * The path of the managed object containing the property.
500 * @param pd
501 * The property to be retrieved.
502 * @return Returns the property's effective value, or
503 * <code>null</code> if there are no values defined.
504 * @throws IllegalArgumentException
505 * If the property definition is not associated with the
506 * referenced managed object's definition.
507 * @throws PropertyException
508 * If the managed object was found but the requested
509 * property could not be decoded.
510 * @throws ConfigException
511 * If the named managed object could not be found or if it
512 * could not be decoded.
513 */
514 public <C extends ConfigurationClient, S extends Configuration, PD>
515 PD getPropertyValue(ManagedObjectPath<C, S> path,
516 PropertyDefinition<PD> pd) throws IllegalArgumentException,
517 ConfigException, PropertyException {
518 SortedSet<PD> values = getPropertyValues(path, pd);
519 if (values.isEmpty()) {
520 return null;
521 } else {
522 return values.first();
523 }
524 }
525
526
527
528 /**
529 * Gets the effective values of a property in the named managed
530 * object.
531 *
532 * @param <C>
533 * The type of client managed object configuration that the
534 * path definition refers to.
535 * @param <S>
536 * The type of server managed object configuration that the
537 * path definition refers to.
538 * @param <PD>
539 * The type of the property to be retrieved.
540 * @param path
541 * The path of the managed object containing the property.
542 * @param pd
543 * The property to be retrieved.
544 * @return Returns the property's effective values, or an empty set
545 * if there are no values defined.
546 * @throws IllegalArgumentException
547 * If the property definition is not associated with the
548 * referenced managed object's definition.
549 * @throws PropertyException
550 * If the managed object was found but the requested
551 * property could not be decoded.
552 * @throws ConfigException
553 * If the named managed object could not be found or if it
554 * could not be decoded.
555 */
556 @SuppressWarnings("unchecked")
557 public <C extends ConfigurationClient, S extends Configuration, PD>
558 SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
559 PropertyDefinition<PD> pd) throws IllegalArgumentException,
560 ConfigException, PropertyException {
561 // Check that the requested property is from the definition
562 // associated with the path.
563 AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
564 PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
565 if (tmp != pd) {
566 throw new IllegalArgumentException("The property " + pd.getName()
567 + " is not associated with a " + d.getName());
568 }
569
570 // Determine the exact type of managed object referenced by the
571 // path.
572 DN dn = DNBuilder.create(path);
573 ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
574
575 DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
576 ManagedObjectDefinition<? extends C, ? extends S> mod;
577
578 try {
579 mod = d.resolveManagedObjectDefinition(resolver);
580 } catch (DefinitionDecodingException e) {
581 throw ConfigExceptionFactory.getInstance()
582 .createDecodingExceptionAdaptor(dn, e);
583 }
584
585 // Make sure we use the correct property definition, the
586 // provided one might have been overridden in the resolved
587 // definition.
588 pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
589
590 List<AttributeValue> values = getAttribute(mod, pd, configEntry);
591 return decodeProperty(path.asSubType(mod), pd, values, null);
592 }
593
594
595
596 /**
597 * Get the root configuration manager associated with this
598 * management context.
599 *
600 * @return Returns the root configuration manager associated with
601 * this management context.
602 */
603 public RootCfg getRootConfiguration() {
604 return getRootConfigurationManagedObject().getConfiguration();
605 }
606
607
608
609 /**
610 * Get the root configuration server managed object associated with
611 * this management context.
612 *
613 * @return Returns the root configuration server managed object
614 * associated with this management context.
615 */
616 public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
617 return ROOT;
618 }
619
620
621
622 /**
623 * Lists the child managed objects of the named parent managed
624 * object.
625 *
626 * @param <C>
627 * The type of client managed object configuration that the
628 * relation definition refers to.
629 * @param <S>
630 * The type of server managed object configuration that the
631 * relation definition refers to.
632 * @param parent
633 * The path of the parent managed object.
634 * @param rd
635 * The instantiable relation definition.
636 * @return Returns the names of the child managed objects.
637 * @throws IllegalArgumentException
638 * If the relation definition is not associated with the
639 * parent managed object's definition.
640 */
641 public <C extends ConfigurationClient, S extends Configuration>
642 String[] listManagedObjects(
643 ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
644 throws IllegalArgumentException {
645 validateRelationDefinition(parent, rd);
646
647 // Get the target entry.
648 DN targetDN = DNBuilder.create(parent, rd);
649 ConfigEntry configEntry;
650 try {
651 configEntry = DirectoryServer.getConfigEntry(targetDN);
652 } catch (ConfigException e) {
653 return new String[0];
654 }
655
656 if (configEntry == null) {
657 return new String[0];
658 }
659
660 // Retrieve the children.
661 Set<DN> children = configEntry.getChildren().keySet();
662 ArrayList<String> names = new ArrayList<String>(children.size());
663 for (DN child : children) {
664 // Assume that RDNs are single-valued and can be trimmed.
665 AttributeValue av = child.getRDN().getAttributeValue(0);
666 names.add(av.getStringValue().trim());
667 }
668
669 return names.toArray(new String[names.size()]);
670 }
671
672
673
674 /**
675 * Determines whether or not the named managed object exists.
676 *
677 * @param path
678 * The path of the named managed object.
679 * @return Returns <code>true</code> if the named managed object
680 * exists, <code>false</code> otherwise.
681 */
682 public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
683 // Get the configuration entry.
684 DN targetDN = DNBuilder.create(path);
685 try {
686 return (getManagedObjectConfigEntry(targetDN) != null);
687 } catch (ConfigException e) {
688 // Assume it doesn't exist.
689 return false;
690 }
691 }
692
693
694
695 /**
696 * Decodes a configuration entry into the required type of server
697 * managed object.
698 *
699 * @param <C>
700 * The type of client managed object configuration that the
701 * path definition refers to.
702 * @param <S>
703 * The type of server managed object configuration that the
704 * path definition refers to.
705 * @param path
706 * The location of the server managed object.
707 * @param configEntry
708 * The configuration entry that should be decoded.
709 * @return Returns the new server-side managed object from the
710 * provided definition and configuration entry.
711 * @throws DefinitionDecodingException
712 * If the managed object's type could not be determined.
713 * @throws ServerManagedObjectDecodingException
714 * If one or more of the managed object's properties could
715 * not be decoded.
716 */
717 <C extends ConfigurationClient, S extends Configuration>
718 ServerManagedObject<? extends S> decode(
719 ManagedObjectPath<C, S> path, ConfigEntry configEntry)
720 throws DefinitionDecodingException, ServerManagedObjectDecodingException {
721 return decode(path, configEntry, null);
722 }
723
724
725
726 /**
727 * Decodes a configuration entry into the required type of server
728 * managed object.
729 *
730 * @param <C>
731 * The type of client managed object configuration that the
732 * path definition refers to.
733 * @param <S>
734 * The type of server managed object configuration that the
735 * path definition refers to.
736 * @param path
737 * The location of the server managed object.
738 * @param configEntry
739 * The configuration entry that should be decoded.
740 * @param newConfigEntry
741 * Optional new configuration that does not exist yet in
742 * the configuration back-end. This will be used for
743 * resolving inherited default values.
744 * @return Returns the new server-side managed object from the
745 * provided definition and configuration entry.
746 * @throws DefinitionDecodingException
747 * If the managed object's type could not be determined.
748 * @throws ServerManagedObjectDecodingException
749 * If one or more of the managed object's properties could
750 * not be decoded.
751 */
752 <C extends ConfigurationClient, S extends Configuration>
753 ServerManagedObject<? extends S> decode(
754 ManagedObjectPath<C, S> path, ConfigEntry configEntry,
755 ConfigEntry newConfigEntry) throws DefinitionDecodingException,
756 ServerManagedObjectDecodingException {
757 // First determine the correct definition to use for the entry.
758 // This could either be the provided definition, or one of its
759 // sub-definitions.
760 DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
761 AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
762 ManagedObjectDefinition<? extends C, ? extends S> mod = d
763 .resolveManagedObjectDefinition(resolver);
764
765 // Build the managed object's properties.
766 List<PropertyException> exceptions = new LinkedList<PropertyException>();
767 Map<PropertyDefinition<?>, SortedSet<?>> properties =
768 new HashMap<PropertyDefinition<?>, SortedSet<?>>();
769 for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
770 List<AttributeValue> values = getAttribute(mod, pd, configEntry);
771 try {
772 SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
773 properties.put(pd, pvalues);
774 } catch (PropertyException e) {
775 exceptions.add(e);
776 }
777 }
778
779 // If there were no decoding problems then return the managed
780 // object, otherwise throw an operations exception.
781 ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
782 configEntry);
783 if (exceptions.isEmpty()) {
784 return mo;
785 } else {
786 throw new ServerManagedObjectDecodingException(mo, exceptions);
787 }
788 }
789
790
791
792 // Decode helper method required to avoid generics warning.
793 private <C extends ConfigurationClient, S extends Configuration>
794 ServerManagedObject<S> decodeAux(
795 ManagedObjectPath<? super C, ? super S> path,
796 ManagedObjectDefinition<C, S> d,
797 Map<PropertyDefinition<?>, SortedSet<?>> properties,
798 ConfigEntry configEntry) {
799 ManagedObjectPath<C, S> newPath = path.asSubType(d);
800 return new ServerManagedObject<S>(newPath, d, properties, configEntry);
801 }
802
803
804
805 // Create a property using the provided string values.
806 private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
807 PropertyDefinition<T> pd, List<AttributeValue> values,
808 ConfigEntry newConfigEntry) throws PropertyException {
809 PropertyException exception = null;
810 SortedSet<T> pvalues = new TreeSet<T>(pd);
811
812 if (!values.isEmpty()) {
813 // The property has values defined for it.
814 for (AttributeValue value : values) {
815 try {
816 pvalues.add(ValueDecoder.decode(pd, value));
817 } catch (IllegalPropertyValueStringException e) {
818 exception = e;
819 }
820 }
821 } else {
822 // No values defined so get the defaults.
823 try {
824 pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
825 } catch (DefaultBehaviorException e) {
826 exception = e;
827 }
828 }
829
830 if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
831 // This exception takes precedence over previous exceptions.
832 exception = new PropertyIsSingleValuedException(pd);
833 T value = pvalues.first();
834 pvalues.clear();
835 pvalues.add(value);
836 }
837
838 if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
839 // The values maybe empty because of a previous exception.
840 if (exception == null) {
841 exception = new PropertyIsMandatoryException(pd);
842 }
843 }
844
845 if (exception != null) {
846 throw exception;
847 } else {
848 return pvalues;
849 }
850 }
851
852
853
854 // Gets the attribute associated with a property from a ConfigEntry.
855 private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d,
856 PropertyDefinition<?> pd, ConfigEntry configEntry) {
857 // TODO: we create a default attribute type if it is
858 // undefined. We should log a warning here if this is the case
859 // since the attribute should have been defined.
860 String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
861 AttributeType type = DirectoryServer.getAttributeType(attrID, true);
862 AttributeValueDecoder<AttributeValue> decoder =
863 new AttributeValueDecoder<AttributeValue>() {
864
865 public AttributeValue decode(AttributeValue value)
866 throws DirectoryException {
867 return value;
868 }
869 };
870
871 List<AttributeValue> values = new LinkedList<AttributeValue>();
872 try {
873 configEntry.getEntry().getAttributeValues(type, decoder, values);
874 } catch (DirectoryException e) {
875 // Should not happen.
876 throw new RuntimeException(e);
877 }
878 return values;
879 }
880
881
882
883 // Get the default values for the specified property.
884 private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
885 PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
886 throws DefaultBehaviorException {
887 DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
888 return v.find(p, pd);
889 }
890
891
892
893 // Gets a config entry required for a managed object and throws a
894 // config exception on failure.
895 private ConfigEntry getManagedObjectConfigEntry(
896 DN dn) throws ConfigException {
897 ConfigEntry configEntry;
898 try {
899 configEntry = DirectoryServer.getConfigEntry(dn);
900 } catch (ConfigException e) {
901 if (debugEnabled()) {
902 TRACER.debugCaught(DebugLogLevel.ERROR, e);
903 }
904
905 Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
906 String.valueOf(dn), stackTraceToSingleLineString(e));
907 throw new ConfigException(message, e);
908 }
909
910 // The configuration handler is free to return null indicating
911 // that the entry does not exist.
912 if (configEntry == null) {
913 Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
914 .get(String.valueOf(dn));
915 throw new ConfigException(message);
916 }
917
918 return configEntry;
919 }
920
921
922
923 // Validate that a relation definition belongs to the path.
924 private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
925 RelationDefinition<?, ?> rd) throws IllegalArgumentException {
926 AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
927 RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
928 if (tmp != rd) {
929 throw new IllegalArgumentException("The relation " + rd.getName()
930 + " is not associated with a " + d.getName());
931 }
932 }
933 }