From e538344449d345daa6e5ecb9b05ceba5427408e9 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Wed, 04 Dec 2013 14:13:59 +0000
Subject: [PATCH] OpenDJ 3 : config framework

---
 opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java | 1576 ++++++++++++++++++++++++++-------------------------------
 1 files changed, 722 insertions(+), 854 deletions(-)

diff --git a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
index e982e4d..90f94ec 100644
--- a/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
+++ b/opendj-admin/src/main/java/org/opends/server/admin/server/ServerManagementContext.java
@@ -27,11 +27,7 @@
 
 package org.opends.server.admin.server;
 
-
-
 import static com.forgerock.opendj.ldap.AdminMessages.*;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import static org.opends.server.util.StaticUtils.*;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -76,911 +72,783 @@
 import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
 import org.opends.server.admin.UnknownPropertyDefinitionException;
 import org.opends.server.admin.DefinitionDecodingException.Reason;
-import org.opends.server.admin.std.meta.RootCfgDefn;
-import org.opends.server.admin.std.server.RootCfg;
-import org.opends.server.api.AttributeValueDecoder;
-import org.opends.server.config.ConfigEntry;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.DirectoryServer;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.types.AttributeType;
-import org.opends.server.types.AttributeValue;
+import org.forgerock.opendj.admin.meta.RootCfgDefn;
+import org.forgerock.opendj.admin.server.RootCfg;
 import org.forgerock.opendj.ldap.DN;
-import org.opends.server.types.DebugLogLevel;
 import org.opends.server.types.DirectoryException;
 
-
-
 /**
  * Server management connection context.
  */
 public final class ServerManagementContext {
 
-  /**
-   * A default behavior visitor used for retrieving the default values
-   * of a property.
-   *
-   * @param <T>
-   *          The type of the property.
-   */
-  private class DefaultValueFinder<T> implements
-      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
+    /**
+     * A default behavior visitor used for retrieving the default values of a
+     * property.
+     *
+     * @param <T>
+     *            The type of the property.
+     */
+    private class DefaultValueFinder<T> implements DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
 
-    // Any exception that occurred whilst retrieving inherited default
-    // values.
-    private DefaultBehaviorException exception = null;
+        // Any exception that occurred whilst retrieving inherited default
+        // values.
+        private DefaultBehaviorException exception = null;
 
-    // Optional new configuration entry which does not yet exist in
-    // the configuration back-end.
-    private final ConfigEntry newConfigEntry;
+        // Optional new configuration entry which does not yet exist in
+        // the configuration back-end.
+        private final ConfigEntry newConfigEntry;
 
-    // The path of the managed object containing the next property.
-    private ManagedObjectPath<?, ?> nextPath = null;
+        // The path of the managed object containing the next property.
+        private ManagedObjectPath<?, ?> nextPath = null;
 
-    // The next property whose default values were required.
-    private PropertyDefinition<T> nextProperty = null;
+        // The next property whose default values were required.
+        private PropertyDefinition<T> nextProperty = null;
 
+        // Private constructor.
+        private DefaultValueFinder(ConfigEntry newConfigEntry) {
+            this.newConfigEntry = newConfigEntry;
+        }
 
+        /**
+         * {@inheritDoc}
+         */
+        public Collection<T> visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
+            try {
+                return getInheritedProperty(d.getManagedObjectPath(), d.getManagedObjectDefinition(),
+                        d.getPropertyName());
+            } catch (DefaultBehaviorException e) {
+                exception = e;
+                return Collections.emptySet();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
+            return Collections.emptySet();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d, Void p) {
+            Collection<String> stringValues = d.getDefaultValues();
+            List<T> values = new ArrayList<T>(stringValues.size());
+
+            for (String stringValue : stringValues) {
+                try {
+                    values.add(nextProperty.decodeValue(stringValue));
+                } catch (IllegalPropertyValueStringException e) {
+                    exception = new DefaultBehaviorException(nextProperty, e);
+                    break;
+                }
+            }
+
+            return values;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Collection<T> visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
+            try {
+                return getInheritedProperty(d.getManagedObjectPath(nextPath), d.getManagedObjectDefinition(),
+                        d.getPropertyName());
+            } catch (DefaultBehaviorException e) {
+                exception = e;
+                return Collections.emptySet();
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d, Void p) {
+            return Collections.emptySet();
+        }
+
+        // Find the default values for the next path/property.
+        private Collection<T> find(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd) throws DefaultBehaviorException {
+            nextPath = p;
+            nextProperty = pd;
+
+            Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(this, null);
+
+            if (exception != null) {
+                throw exception;
+            }
+
+            if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+                throw new DefaultBehaviorException(pd, new PropertyIsSingleValuedException(pd));
+            }
+
+            return values;
+        }
+
+        // Get an inherited property value.
+        @SuppressWarnings("unchecked")
+        private Collection<T> getInheritedProperty(ManagedObjectPath target, AbstractManagedObjectDefinition<?, ?> d,
+                String propertyName) throws DefaultBehaviorException {
+            // First check that the requested type of managed object
+            // corresponds to the path.
+            AbstractManagedObjectDefinition<?, ?> supr = target.getManagedObjectDefinition();
+            if (!supr.isParentOf(d)) {
+                throw new DefaultBehaviorException(nextProperty, new DefinitionDecodingException(supr,
+                        Reason.WRONG_TYPE_INFORMATION));
+            }
+
+            // Save the current property in case of recursion.
+            PropertyDefinition<T> pd1 = nextProperty;
+
+            try {
+                // Get the actual managed object definition.
+                DN dn = DNBuilder.create(target);
+                ConfigEntry configEntry;
+                if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
+                    configEntry = newConfigEntry;
+                } else {
+                    configEntry = getManagedObjectConfigEntry(dn);
+                }
+
+                DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+                ManagedObjectDefinition<?, ?> mod = d.resolveManagedObjectDefinition(resolver);
+
+                PropertyDefinition<T> pd2;
+                try {
+                    PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
+                    pd2 = pd1.getClass().cast(pdTmp);
+                } catch (IllegalArgumentException e) {
+                    throw new PropertyNotFoundException(propertyName);
+                } catch (ClassCastException e) {
+                    // FIXME: would be nice to throw a better exception here.
+                    throw new PropertyNotFoundException(propertyName);
+                }
+
+                List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
+                if (values.isEmpty()) {
+                    // Recursively retrieve this property's default values.
+                    Collection<T> tmp = find(target, pd2);
+                    Collection<T> pvalues = new ArrayList<T>(tmp.size());
+                    for (T value : tmp) {
+                        pd1.validateValue(value);
+                        pvalues.add(value);
+                    }
+                    return pvalues;
+                } else {
+                    Collection<T> pvalues = new ArrayList<T>(values.size());
+                    for (AttributeValue value : values) {
+                        pvalues.add(ValueDecoder.decode(pd1, value));
+                    }
+                    return pvalues;
+                }
+            } catch (DefinitionDecodingException e) {
+                throw new DefaultBehaviorException(pd1, e);
+            } catch (PropertyNotFoundException e) {
+                throw new DefaultBehaviorException(pd1, e);
+            } catch (IllegalPropertyValueException e) {
+                throw new DefaultBehaviorException(pd1, e);
+            } catch (IllegalPropertyValueStringException e) {
+                throw new DefaultBehaviorException(pd1, e);
+            } catch (ConfigException e) {
+                throw new DefaultBehaviorException(pd1, e);
+            }
+        }
+    }
+
+    /**
+     * A definition resolver that determines the managed object definition from
+     * the object classes of a ConfigEntry.
+     */
+    private class MyDefinitionResolver implements DefinitionResolver {
+
+        // The config entry.
+        private final ConfigEntry entry;
+
+        // Private constructor.
+        private MyDefinitionResolver(ConfigEntry entry) {
+            this.entry = entry;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+            String oc = LDAPProfile.getInstance().getObjectClass(d);
+            return entry.hasObjectClass(oc);
+        }
+    }
+
+    /**
+     * A visitor which is used to decode property LDAP values.
+     */
+    private static final class ValueDecoder extends PropertyDefinitionVisitor<Object, String> {
+
+        /**
+         * Decodes the provided property LDAP value.
+         *
+         * @param <PD>
+         *            The type of the property.
+         * @param pd
+         *            The property definition.
+         * @param value
+         *            The LDAP string representation.
+         * @return Returns the decoded LDAP value.
+         * @throws IllegalPropertyValueStringException
+         *             If the property value could not be decoded because it was
+         *             invalid.
+         */
+        public static <PD> PD decode(PropertyDefinition<PD> pd, AttributeValue value)
+                throws IllegalPropertyValueStringException {
+            String s = value.getValue().toString();
+            return pd.castValue(pd.accept(new ValueDecoder(), s));
+        }
+
+        // Prevent instantiation.
+        private ValueDecoder() {
+            // No implementation required.
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public <C extends ConfigurationClient, S extends Configuration> Object visitAggregation(
+                AggregationPropertyDefinition<C, S> d, String p) {
+            // Aggregations values are stored as full DNs in LDAP, but
+            // just their common name is exposed in the admin framework.
+            try {
+                Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d.getRelationDefinition(), p);
+                return reference.getName();
+            } catch (IllegalArgumentException e) {
+                throw new IllegalPropertyValueStringException(d, p);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public <T> Object visitUnknown(PropertyDefinition<T> d, String p) throws UnknownPropertyDefinitionException {
+            // By default the property definition's decoder will do.
+            return d.decodeValue(p);
+        }
+    }
+
+    // Singleton instance.
+    private final static ServerManagementContext INSTANCE = new ServerManagementContext();
+
+    /**
+     * The root server managed object.
+     */
+    private static final ServerManagedObject<RootCfg> ROOT = new ServerManagedObject<RootCfg>(
+            ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(),
+            Collections.<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
+
+    /**
+     * Get the single server-side management context.
+     *
+     * @return Returns the single server-side management context.
+     */
+    public static ServerManagementContext getInstance() {
+        return INSTANCE;
+    }
 
     // Private constructor.
-    private DefaultValueFinder(ConfigEntry newConfigEntry) {
-      this.newConfigEntry = newConfigEntry;
+    private ServerManagementContext() {
+        // No implementation required.
     }
 
-
-
     /**
-     * {@inheritDoc}
+     * Gets the named managed object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the path
+     *            definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the path
+     *            definition refers to.
+     * @param path
+     *            The path of the managed object.
+     * @return Returns the named managed object.
+     * @throws ConfigException
+     *             If the named managed object could not be found or if it could
+     *             not be decoded.
      */
-    public Collection<T> visitAbsoluteInherited(
-        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
-      try {
-        return getInheritedProperty(d.getManagedObjectPath(), d
-            .getManagedObjectDefinition(), d.getPropertyName());
-      } catch (DefaultBehaviorException e) {
-        exception = e;
-        return Collections.emptySet();
-      }
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
-      return Collections.emptySet();
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
-        Void p) {
-      Collection<String> stringValues = d.getDefaultValues();
-      List<T> values = new ArrayList<T>(stringValues.size());
-
-      for (String stringValue : stringValues) {
-        try {
-          values.add(nextProperty.decodeValue(stringValue));
-        } catch (IllegalPropertyValueStringException e) {
-          exception = new DefaultBehaviorException(nextProperty, e);
-          break;
-        }
-      }
-
-      return values;
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitRelativeInherited(
-        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
-      try {
-        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
-            .getManagedObjectDefinition(), d.getPropertyName());
-      } catch (DefaultBehaviorException e) {
-        exception = e;
-        return Collections.emptySet();
-      }
-    }
-
-
-
-    /**
-     * {@inheritDoc}
-     */
-    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
-        Void p) {
-      return Collections.emptySet();
-    }
-
-
-
-    // Find the default values for the next path/property.
-    private Collection<T> find(ManagedObjectPath<?, ?> p,
-        PropertyDefinition<T> pd) throws DefaultBehaviorException {
-      nextPath = p;
-      nextProperty = pd;
-
-      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
-          this, null);
-
-      if (exception != null) {
-        throw exception;
-      }
-
-      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-        throw new DefaultBehaviorException(pd,
-            new PropertyIsSingleValuedException(pd));
-      }
-
-      return values;
-    }
-
-
-
-    // Get an inherited property value.
     @SuppressWarnings("unchecked")
-    private Collection<T> getInheritedProperty(ManagedObjectPath target,
-        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
-        throws DefaultBehaviorException {
-      // First check that the requested type of managed object
-      // corresponds to the path.
-      AbstractManagedObjectDefinition<?, ?> supr = target
-          .getManagedObjectDefinition();
-      if (!supr.isParentOf(d)) {
-        throw new DefaultBehaviorException(
-            nextProperty, new DefinitionDecodingException(supr,
-                Reason.WRONG_TYPE_INFORMATION));
-      }
-
-      // Save the current property in case of recursion.
-      PropertyDefinition<T> pd1 = nextProperty;
-
-      try {
-        // Get the actual managed object definition.
-        DN dn = DNBuilder.create(target);
-        ConfigEntry configEntry;
-        if (newConfigEntry != null && newConfigEntry.getDN().equals(dn)) {
-          configEntry = newConfigEntry;
-        } else {
-          configEntry = getManagedObjectConfigEntry(dn);
+    public <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> getManagedObject(
+            ManagedObjectPath<C, S> path) throws ConfigException {
+        // Be careful to handle the root configuration.
+        if (path.isEmpty()) {
+            return (ServerManagedObject<S>) getRootConfigurationManagedObject();
         }
 
+        // Get the configuration entry.
+        DN targetDN = DNBuilder.create(path);
+        ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
+        try {
+            ServerManagedObject<? extends S> managedObject;
+            managedObject = decode(path, configEntry);
+
+            // Enforce any constraints.
+            managedObject.ensureIsUsable();
+
+            return managedObject;
+        } catch (DefinitionDecodingException e) {
+            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(targetDN, e);
+        } catch (ServerManagedObjectDecodingException e) {
+            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
+        } catch (ConstraintViolationException e) {
+            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(e);
+        }
+    }
+
+    /**
+     * Gets the effective value of a property in the named managed object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the path
+     *            definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the path
+     *            definition refers to.
+     * @param <PD>
+     *            The type of the property to be retrieved.
+     * @param path
+     *            The path of the managed object containing the property.
+     * @param pd
+     *            The property to be retrieved.
+     * @return Returns the property's effective value, or <code>null</code> if
+     *         there are no values defined.
+     * @throws IllegalArgumentException
+     *             If the property definition is not associated with the
+     *             referenced managed object's definition.
+     * @throws PropertyException
+     *             If the managed object was found but the requested property
+     *             could not be decoded.
+     * @throws ConfigException
+     *             If the named managed object could not be found or if it could
+     *             not be decoded.
+     */
+    public <C extends ConfigurationClient, S extends Configuration, PD> PD getPropertyValue(
+            ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd) throws IllegalArgumentException, ConfigException,
+            PropertyException {
+        SortedSet<PD> values = getPropertyValues(path, pd);
+        if (values.isEmpty()) {
+            return null;
+        } else {
+            return values.first();
+        }
+    }
+
+    /**
+     * Gets the effective values of a property in the named managed object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the path
+     *            definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the path
+     *            definition refers to.
+     * @param <PD>
+     *            The type of the property to be retrieved.
+     * @param path
+     *            The path of the managed object containing the property.
+     * @param pd
+     *            The property to be retrieved.
+     * @return Returns the property's effective values, or an empty set if there
+     *         are no values defined.
+     * @throws IllegalArgumentException
+     *             If the property definition is not associated with the
+     *             referenced managed object's definition.
+     * @throws PropertyException
+     *             If the managed object was found but the requested property
+     *             could not be decoded.
+     * @throws ConfigException
+     *             If the named managed object could not be found or if it could
+     *             not be decoded.
+     */
+    @SuppressWarnings("unchecked")
+    public <C extends ConfigurationClient, S extends Configuration, PD> SortedSet<PD> getPropertyValues(
+            ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd) throws IllegalArgumentException, ConfigException,
+            PropertyException {
+        // Check that the requested property is from the definition
+        // associated with the path.
+        AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+        PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
+        if (tmp != pd) {
+            throw new IllegalArgumentException("The property " + pd.getName() + " is not associated with a "
+                    + d.getName());
+        }
+
+        // Determine the exact type of managed object referenced by the
+        // path.
+        DN dn = DNBuilder.create(path);
+        ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
+
         DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
-        ManagedObjectDefinition<?, ?> mod = d
-            .resolveManagedObjectDefinition(resolver);
+        ManagedObjectDefinition<? extends C, ? extends S> mod;
 
-        PropertyDefinition<T> pd2;
         try {
-          PropertyDefinition<?> pdTmp = mod.getPropertyDefinition(propertyName);
-          pd2 = pd1.getClass().cast(pdTmp);
-        } catch (IllegalArgumentException e) {
-          throw new PropertyNotFoundException(propertyName);
-        } catch (ClassCastException e) {
-          // FIXME: would be nice to throw a better exception here.
-          throw new PropertyNotFoundException(propertyName);
+            mod = d.resolveManagedObjectDefinition(resolver);
+        } catch (DefinitionDecodingException e) {
+            throw ConfigExceptionFactory.getInstance().createDecodingExceptionAdaptor(dn, e);
         }
 
-        List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
-        if (values.isEmpty()) {
-          // Recursively retrieve this property's default values.
-          Collection<T> tmp = find(target, pd2);
-          Collection<T> pvalues = new ArrayList<T>(tmp.size());
-          for (T value : tmp) {
-            pd1.validateValue(value);
-            pvalues.add(value);
-          }
-          return pvalues;
-        } else {
-          Collection<T> pvalues = new ArrayList<T>(values.size());
-          for (AttributeValue value : values) {
-            pvalues.add(ValueDecoder.decode(pd1, value));
-          }
-          return pvalues;
-        }
-      } catch (DefinitionDecodingException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (PropertyNotFoundException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (IllegalPropertyValueException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (IllegalPropertyValueStringException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      } catch (ConfigException e) {
-        throw new DefaultBehaviorException(pd1, e);
-      }
+        // Make sure we use the correct property definition, the
+        // provided one might have been overridden in the resolved
+        // definition.
+        pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
+
+        List<AttributeValue> values = getAttribute(mod, pd, configEntry);
+        return decodeProperty(path.asSubType(mod), pd, values, null);
     }
-  }
-
-
-
-  /**
-   * A definition resolver that determines the managed object
-   * definition from the object classes of a ConfigEntry.
-   */
-  private class MyDefinitionResolver implements DefinitionResolver {
-
-    // The config entry.
-    private final ConfigEntry entry;
-
-
-
-    // Private constructor.
-    private MyDefinitionResolver(ConfigEntry entry) {
-      this.entry = entry;
-    }
-
-
 
     /**
-     * {@inheritDoc}
-     */
-    public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
-      String oc = LDAPProfile.getInstance().getObjectClass(d);
-      return entry.hasObjectClass(oc);
-    }
-  }
-
-
-
-  /**
-   * A visitor which is used to decode property LDAP values.
-   */
-  private static final class ValueDecoder extends
-      PropertyDefinitionVisitor<Object, String> {
-
-    /**
-     * Decodes the provided property LDAP value.
+     * Get the root configuration manager associated with this management
+     * context.
      *
-     * @param <PD>
-     *          The type of the property.
-     * @param pd
-     *          The property definition.
-     * @param value
-     *          The LDAP string representation.
-     * @return Returns the decoded LDAP value.
-     * @throws IllegalPropertyValueStringException
-     *           If the property value could not be decoded because it
-     *           was invalid.
+     * @return Returns the root configuration manager associated with this
+     *         management context.
      */
-    public static <PD> PD decode(PropertyDefinition<PD> pd,
-        AttributeValue value) throws IllegalPropertyValueStringException {
-      String s = value.getValue().toString();
-      return pd.castValue(pd.accept(new ValueDecoder(), s));
+    public RootCfg getRootConfiguration() {
+        return getRootConfigurationManagedObject().getConfiguration();
     }
 
-
-
-    // Prevent instantiation.
-    private ValueDecoder() {
-      // No implementation required.
-    }
-
-
-
     /**
-     * {@inheritDoc}
+     * Get the root configuration server managed object associated with this
+     * management context.
+     *
+     * @return Returns the root configuration server managed object associated
+     *         with this management context.
      */
-    @Override
-    public <C extends ConfigurationClient, S extends Configuration>
-    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
-      // Aggregations values are stored as full DNs in LDAP, but
-      // just their common name is exposed in the admin framework.
-      try {
-        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
-            .getRelationDefinition(), p);
-        return reference.getName();
-      } catch (IllegalArgumentException e) {
-        throw new IllegalPropertyValueStringException(d, p);
-      }
+    public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
+        return ROOT;
     }
 
-
-
     /**
-     * {@inheritDoc}
+     * Lists the child managed objects of the named parent managed object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the
+     *            relation definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the
+     *            relation definition refers to.
+     * @param parent
+     *            The path of the parent managed object.
+     * @param rd
+     *            The instantiable relation definition.
+     * @return Returns the names of the child managed objects.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with the parent
+     *             managed object's definition.
      */
-    @Override
-    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
-        throws UnknownPropertyDefinitionException {
-      // By default the property definition's decoder will do.
-      return d.decodeValue(p);
-    }
-  }
+    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
+            ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd) throws IllegalArgumentException {
+        validateRelationDefinition(parent, rd);
 
-
-
-  // Singleton instance.
-  private final static ServerManagementContext INSTANCE =
-    new ServerManagementContext();
-
-  /**
-   * The root server managed object.
-   */
-  private static final ServerManagedObject<RootCfg> ROOT =
-    new ServerManagedObject<RootCfg>(
-      ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
-          .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
-
-  /**
-   * The tracer object for the debug logger.
-   */
-  private static final DebugTracer TRACER = getTracer();
-
-
-
-  /**
-   * Get the single server-side management context.
-   *
-   * @return Returns the single server-side management context.
-   */
-  public static ServerManagementContext getInstance() {
-    return INSTANCE;
-  }
-
-
-
-  // Private constructor.
-  private ServerManagementContext() {
-    // No implementation required.
-  }
-
-
-
-  /**
-   * Gets the named managed object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          path definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          path definition refers to.
-   * @param path
-   *          The path of the managed object.
-   * @return Returns the named managed object.
-   * @throws ConfigException
-   *           If the named managed object could not be found or if it
-   *           could not be decoded.
-   */
-  @SuppressWarnings("unchecked")
-  public <C extends ConfigurationClient, S extends Configuration>
-  ServerManagedObject<? extends S> getManagedObject(
-      ManagedObjectPath<C, S> path) throws ConfigException {
-    // Be careful to handle the root configuration.
-    if (path.isEmpty()) {
-      return (ServerManagedObject<S>) getRootConfigurationManagedObject();
-    }
-
-    // Get the configuration entry.
-    DN targetDN = DNBuilder.create(path);
-    ConfigEntry configEntry = getManagedObjectConfigEntry(targetDN);
-    try {
-      ServerManagedObject<? extends S> managedObject;
-      managedObject = decode(path, configEntry);
-
-      // Enforce any constraints.
-      managedObject.ensureIsUsable();
-
-      return managedObject;
-    } catch (DefinitionDecodingException e) {
-      throw ConfigExceptionFactory.getInstance()
-          .createDecodingExceptionAdaptor(targetDN, e);
-    } catch (ServerManagedObjectDecodingException e) {
-      throw ConfigExceptionFactory.getInstance()
-          .createDecodingExceptionAdaptor(e);
-    } catch (ConstraintViolationException e) {
-      throw ConfigExceptionFactory.getInstance()
-          .createDecodingExceptionAdaptor(e);
-    }
-  }
-
-
-
-  /**
-   * Gets the effective value of a property in the named managed
-   * object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          path definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          path definition refers to.
-   * @param <PD>
-   *          The type of the property to be retrieved.
-   * @param path
-   *          The path of the managed object containing the property.
-   * @param pd
-   *          The property to be retrieved.
-   * @return Returns the property's effective value, or
-   *         <code>null</code> if there are no values defined.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with the
-   *           referenced managed object's definition.
-   * @throws PropertyException
-   *           If the managed object was found but the requested
-   *           property could not be decoded.
-   * @throws ConfigException
-   *           If the named managed object could not be found or if it
-   *           could not be decoded.
-   */
-  public <C extends ConfigurationClient, S extends Configuration, PD>
-  PD getPropertyValue(ManagedObjectPath<C, S> path,
-      PropertyDefinition<PD> pd) throws IllegalArgumentException,
-      ConfigException, PropertyException {
-    SortedSet<PD> values = getPropertyValues(path, pd);
-    if (values.isEmpty()) {
-      return null;
-    } else {
-      return values.first();
-    }
-  }
-
-
-
-  /**
-   * Gets the effective values of a property in the named managed
-   * object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          path definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          path definition refers to.
-   * @param <PD>
-   *          The type of the property to be retrieved.
-   * @param path
-   *          The path of the managed object containing the property.
-   * @param pd
-   *          The property to be retrieved.
-   * @return Returns the property's effective values, or an empty set
-   *         if there are no values defined.
-   * @throws IllegalArgumentException
-   *           If the property definition is not associated with the
-   *           referenced managed object's definition.
-   * @throws PropertyException
-   *           If the managed object was found but the requested
-   *           property could not be decoded.
-   * @throws ConfigException
-   *           If the named managed object could not be found or if it
-   *           could not be decoded.
-   */
-  @SuppressWarnings("unchecked")
-  public <C extends ConfigurationClient, S extends Configuration, PD>
-  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
-      PropertyDefinition<PD> pd) throws IllegalArgumentException,
-      ConfigException, PropertyException {
-    // Check that the requested property is from the definition
-    // associated with the path.
-    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
-    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
-    if (tmp != pd) {
-      throw new IllegalArgumentException("The property " + pd.getName()
-          + " is not associated with a " + d.getName());
-    }
-
-    // Determine the exact type of managed object referenced by the
-    // path.
-    DN dn = DNBuilder.create(path);
-    ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
-
-    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
-    ManagedObjectDefinition<? extends C, ? extends S> mod;
-
-    try {
-      mod = d.resolveManagedObjectDefinition(resolver);
-    } catch (DefinitionDecodingException e) {
-      throw ConfigExceptionFactory.getInstance()
-          .createDecodingExceptionAdaptor(dn, e);
-    }
-
-    // Make sure we use the correct property definition, the
-    // provided one might have been overridden in the resolved
-    // definition.
-    pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
-
-    List<AttributeValue> values = getAttribute(mod, pd, configEntry);
-    return decodeProperty(path.asSubType(mod), pd, values, null);
-  }
-
-
-
-  /**
-   * Get the root configuration manager associated with this
-   * management context.
-   *
-   * @return Returns the root configuration manager associated with
-   *         this management context.
-   */
-  public RootCfg getRootConfiguration() {
-    return getRootConfigurationManagedObject().getConfiguration();
-  }
-
-
-
-  /**
-   * Get the root configuration server managed object associated with
-   * this management context.
-   *
-   * @return Returns the root configuration server managed object
-   *         associated with this management context.
-   */
-  public ServerManagedObject<RootCfg> getRootConfigurationManagedObject() {
-    return ROOT;
-  }
-
-
-
-  /**
-   * Lists the child managed objects of the named parent managed
-   * object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          relation definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          relation definition refers to.
-   * @param parent
-   *          The path of the parent managed object.
-   * @param rd
-   *          The instantiable relation definition.
-   * @return Returns the names of the child managed objects.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with the
-   *           parent managed object's definition.
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listManagedObjects(
-      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
-      throws IllegalArgumentException {
-    validateRelationDefinition(parent, rd);
-
-    // Get the target entry.
-    DN targetDN = DNBuilder.create(parent, rd);
-    ConfigEntry configEntry;
-    try {
-      configEntry = DirectoryServer.getConfigEntry(targetDN);
-    } catch (ConfigException e) {
-      return new String[0];
-    }
-
-    if (configEntry == null) {
-      return new String[0];
-    }
-
-    // Retrieve the children.
-    Set<DN> children = configEntry.getChildren().keySet();
-    ArrayList<String> names = new ArrayList<String>(children.size());
-    for (DN child : children) {
-      // Assume that RDNs are single-valued and can be trimmed.
-      AttributeValue av = child.getRDN().getAttributeValue(0);
-      names.add(av.getValue().toString().trim());
-    }
-
-    return names.toArray(new String[names.size()]);
-  }
-
-
-
-  /**
-   * Lists the child managed objects of the named parent managed
-   * object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          relation definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          relation definition refers to.
-   * @param parent
-   *          The path of the parent managed object.
-   * @param rd
-   *          The set relation definition.
-   * @return Returns the names of the child managed objects.
-   * @throws IllegalArgumentException
-   *           If the relation definition is not associated with the
-   *           parent managed object's definition.
-   */
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listManagedObjects(
-      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
-      throws IllegalArgumentException {
-    validateRelationDefinition(parent, rd);
-
-    // Get the target entry.
-    DN targetDN = DNBuilder.create(parent, rd);
-    ConfigEntry configEntry;
-    try {
-      configEntry = DirectoryServer.getConfigEntry(targetDN);
-    } catch (ConfigException e) {
-      return new String[0];
-    }
-
-    if (configEntry == null) {
-      return new String[0];
-    }
-
-    // Retrieve the children.
-    Set<DN> children = configEntry.getChildren().keySet();
-    ArrayList<String> names = new ArrayList<String>(children.size());
-    for (DN child : children) {
-      // Assume that RDNs are single-valued and can be trimmed.
-      AttributeValue av = child.getRDN().getAttributeValue(0);
-      names.add(av.toString().trim());
-    }
-
-    return names.toArray(new String[names.size()]);
-  }
-
-
-
-  /**
-   * Determines whether or not the named managed object exists.
-   *
-   * @param path
-   *          The path of the named managed object.
-   * @return Returns <code>true</code> if the named managed object
-   *         exists, <code>false</code> otherwise.
-   */
-  public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
-    // Get the configuration entry.
-    DN targetDN = DNBuilder.create(path);
-    try {
-      return (getManagedObjectConfigEntry(targetDN) != null);
-    } catch (ConfigException e) {
-      // Assume it doesn't exist.
-      return false;
-    }
-  }
-
-
-
-  /**
-   * Decodes a configuration entry into the required type of server
-   * managed object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          path definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          path definition refers to.
-   * @param path
-   *          The location of the server managed object.
-   * @param configEntry
-   *          The configuration entry that should be decoded.
-   * @return Returns the new server-side managed object from the
-   *         provided definition and configuration entry.
-   * @throws DefinitionDecodingException
-   *           If the managed object's type could not be determined.
-   * @throws ServerManagedObjectDecodingException
-   *           If one or more of the managed object's properties could
-   *           not be decoded.
-   */
-  <C extends ConfigurationClient, S extends Configuration>
-  ServerManagedObject<? extends S> decode(
-      ManagedObjectPath<C, S> path, ConfigEntry configEntry)
-      throws DefinitionDecodingException, ServerManagedObjectDecodingException {
-    return decode(path, configEntry, null);
-  }
-
-
-
-  /**
-   * Decodes a configuration entry into the required type of server
-   * managed object.
-   *
-   * @param <C>
-   *          The type of client managed object configuration that the
-   *          path definition refers to.
-   * @param <S>
-   *          The type of server managed object configuration that the
-   *          path definition refers to.
-   * @param path
-   *          The location of the server managed object.
-   * @param configEntry
-   *          The configuration entry that should be decoded.
-   * @param newConfigEntry
-   *          Optional new configuration that does not exist yet in
-   *          the configuration back-end. This will be used for
-   *          resolving inherited default values.
-   * @return Returns the new server-side managed object from the
-   *         provided definition and configuration entry.
-   * @throws DefinitionDecodingException
-   *           If the managed object's type could not be determined.
-   * @throws ServerManagedObjectDecodingException
-   *           If one or more of the managed object's properties could
-   *           not be decoded.
-   */
-  <C extends ConfigurationClient, S extends Configuration>
-  ServerManagedObject<? extends S> decode(
-      ManagedObjectPath<C, S> path, ConfigEntry configEntry,
-      ConfigEntry newConfigEntry) throws DefinitionDecodingException,
-      ServerManagedObjectDecodingException {
-    // First determine the correct definition to use for the entry.
-    // This could either be the provided definition, or one of its
-    // sub-definitions.
-    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
-    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
-    ManagedObjectDefinition<? extends C, ? extends S> mod = d
-        .resolveManagedObjectDefinition(resolver);
-
-    // Build the managed object's properties.
-    List<PropertyException> exceptions = new LinkedList<PropertyException>();
-    Map<PropertyDefinition<?>, SortedSet<?>> properties =
-      new HashMap<PropertyDefinition<?>, SortedSet<?>>();
-    for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-      List<AttributeValue> values = getAttribute(mod, pd, configEntry);
-      try {
-        SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
-        properties.put(pd, pvalues);
-      } catch (PropertyException e) {
-        exceptions.add(e);
-      }
-    }
-
-    // If there were no decoding problems then return the managed
-    // object, otherwise throw an operations exception.
-    ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
-        configEntry);
-    if (exceptions.isEmpty()) {
-      return mo;
-    } else {
-      throw new ServerManagedObjectDecodingException(mo, exceptions);
-    }
-  }
-
-
-
-  // Decode helper method required to avoid generics warning.
-  private <C extends ConfigurationClient, S extends Configuration>
-  ServerManagedObject<S> decodeAux(
-      ManagedObjectPath<? super C, ? super S> path,
-      ManagedObjectDefinition<C, S> d,
-      Map<PropertyDefinition<?>, SortedSet<?>> properties,
-      ConfigEntry configEntry) {
-    ManagedObjectPath<C, S> newPath = path.asSubType(d);
-    return new ServerManagedObject<S>(newPath, d, properties, configEntry);
-  }
-
-
-
-  // Create a property using the provided string values.
-  private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
-      PropertyDefinition<T> pd, List<AttributeValue> values,
-      ConfigEntry newConfigEntry) throws PropertyException {
-    PropertyException exception = null;
-    SortedSet<T> pvalues = new TreeSet<T>(pd);
-
-    if (!values.isEmpty()) {
-      // The property has values defined for it.
-      for (AttributeValue value : values) {
+        // Get the target entry.
+        DN targetDN = DNBuilder.create(parent, rd);
+        ConfigEntry configEntry;
         try {
-          pvalues.add(ValueDecoder.decode(pd, value));
-        } catch (IllegalPropertyValueStringException e) {
-          exception = e;
+            configEntry = DirectoryServer.getConfigEntry(targetDN);
+        } catch (ConfigException e) {
+            return new String[0];
         }
-      }
-    } else {
-      // No values defined so get the defaults.
-      try {
-        pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
-      } catch (DefaultBehaviorException e) {
-        exception = e;
-      }
+
+        if (configEntry == null) {
+            return new String[0];
+        }
+
+        // Retrieve the children.
+        Set<DN> children = configEntry.getChildren().keySet();
+        ArrayList<String> names = new ArrayList<String>(children.size());
+        for (DN child : children) {
+            // Assume that RDNs are single-valued and can be trimmed.
+            AttributeValue av = child.rdn().getAttributeValue(0);
+            names.add(av.getValue().toString().trim());
+        }
+
+        return names.toArray(new String[names.size()]);
     }
 
-    if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-      // This exception takes precedence over previous exceptions.
-      exception = new PropertyIsSingleValuedException(pd);
-      T value = pvalues.first();
-      pvalues.clear();
-      pvalues.add(value);
+    /**
+     * Lists the child managed objects of the named parent managed object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the
+     *            relation definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the
+     *            relation definition refers to.
+     * @param parent
+     *            The path of the parent managed object.
+     * @param rd
+     *            The set relation definition.
+     * @return Returns the names of the child managed objects.
+     * @throws IllegalArgumentException
+     *             If the relation definition is not associated with the parent
+     *             managed object's definition.
+     */
+    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
+            ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd) throws IllegalArgumentException {
+        validateRelationDefinition(parent, rd);
+
+        // Get the target entry.
+        DN targetDN = DNBuilder.create(parent, rd);
+        ConfigEntry configEntry;
+        try {
+            configEntry = DirectoryServer.getConfigEntry(targetDN);
+        } catch (ConfigException e) {
+            return new String[0];
+        }
+
+        if (configEntry == null) {
+            return new String[0];
+        }
+
+        // Retrieve the children.
+        Set<DN> children = configEntry.getChildren().keySet();
+        ArrayList<String> names = new ArrayList<String>(children.size());
+        for (DN child : children) {
+            // Assume that RDNs are single-valued and can be trimmed.
+            AttributeValue av = child.rdn().getAttributeValue(0);
+            names.add(av.toString().trim());
+        }
+
+        return names.toArray(new String[names.size()]);
     }
 
-    if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
-      // The values maybe empty because of a previous exception.
-      if (exception == null) {
-        exception = new PropertyIsMandatoryException(pd);
-      }
+    /**
+     * Determines whether or not the named managed object exists.
+     *
+     * @param path
+     *            The path of the named managed object.
+     * @return Returns <code>true</code> if the named managed object exists,
+     *         <code>false</code> otherwise.
+     */
+    public boolean managedObjectExists(ManagedObjectPath<?, ?> path) {
+        // Get the configuration entry.
+        DN targetDN = DNBuilder.create(path);
+        try {
+            return (getManagedObjectConfigEntry(targetDN) != null);
+        } catch (ConfigException e) {
+            // Assume it doesn't exist.
+            return false;
+        }
     }
 
-    if (exception != null) {
-      throw exception;
-    } else {
-      return pvalues;
-    }
-  }
-
-
-
-  // Gets the attribute associated with a property from a ConfigEntry.
-  private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d,
-      PropertyDefinition<?> pd, ConfigEntry configEntry) {
-    // TODO: we create a default attribute type if it is
-    // undefined. We should log a warning here if this is the case
-    // since the attribute should have been defined.
-    String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
-    AttributeType type = DirectoryServer.getAttributeType(attrID, true);
-    AttributeValueDecoder<AttributeValue> decoder =
-      new AttributeValueDecoder<AttributeValue>() {
-
-      public AttributeValue decode(AttributeValue value)
-          throws DirectoryException {
-        return value;
-      }
-    };
-
-    List<AttributeValue> values = new LinkedList<AttributeValue>();
-    try {
-      configEntry.getEntry().getAttributeValues(type, decoder, values);
-    } catch (DirectoryException e) {
-      // Should not happen.
-      throw new RuntimeException(e);
-    }
-    return values;
-  }
-
-
-
-  // Get the default values for the specified property.
-  private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p,
-      PropertyDefinition<T> pd, ConfigEntry newConfigEntry)
-      throws DefaultBehaviorException {
-    DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
-    return v.find(p, pd);
-  }
-
-
-
-  // Gets a config entry required for a managed object and throws a
-  // config exception on failure.
-  private ConfigEntry getManagedObjectConfigEntry(
-      DN dn) throws ConfigException {
-    ConfigEntry configEntry;
-    try {
-      configEntry = DirectoryServer.getConfigEntry(dn);
-    } catch (ConfigException e) {
-      if (debugEnabled()) {
-        TRACER.debugCaught(DebugLogLevel.ERROR, e);
-      }
-
-      LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
-          String.valueOf(dn), stackTraceToSingleLineString(e));
-      throw new ConfigException(message, e);
+    /**
+     * Decodes a configuration entry into the required type of server managed
+     * object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the path
+     *            definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the path
+     *            definition refers to.
+     * @param path
+     *            The location of the server managed object.
+     * @param configEntry
+     *            The configuration entry that should be decoded.
+     * @return Returns the new server-side managed object from the provided
+     *         definition and configuration entry.
+     * @throws DefinitionDecodingException
+     *             If the managed object's type could not be determined.
+     * @throws ServerManagedObjectDecodingException
+     *             If one or more of the managed object's properties could not
+     *             be decoded.
+     */
+    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
+            ManagedObjectPath<C, S> path, ConfigEntry configEntry) throws DefinitionDecodingException,
+            ServerManagedObjectDecodingException {
+        return decode(path, configEntry, null);
     }
 
-    // The configuration handler is free to return null indicating
-    // that the entry does not exist.
-    if (configEntry == null) {
-      LocalizableMessage message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
-          .get(String.valueOf(dn));
-      throw new ConfigException(message);
+    /**
+     * Decodes a configuration entry into the required type of server managed
+     * object.
+     *
+     * @param <C>
+     *            The type of client managed object configuration that the path
+     *            definition refers to.
+     * @param <S>
+     *            The type of server managed object configuration that the path
+     *            definition refers to.
+     * @param path
+     *            The location of the server managed object.
+     * @param configEntry
+     *            The configuration entry that should be decoded.
+     * @param newConfigEntry
+     *            Optional new configuration that does not exist yet in the
+     *            configuration back-end. This will be used for resolving
+     *            inherited default values.
+     * @return Returns the new server-side managed object from the provided
+     *         definition and configuration entry.
+     * @throws DefinitionDecodingException
+     *             If the managed object's type could not be determined.
+     * @throws ServerManagedObjectDecodingException
+     *             If one or more of the managed object's properties could not
+     *             be decoded.
+     */
+    <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<? extends S> decode(
+            ManagedObjectPath<C, S> path, ConfigEntry configEntry, ConfigEntry newConfigEntry)
+            throws DefinitionDecodingException, ServerManagedObjectDecodingException {
+        // First determine the correct definition to use for the entry.
+        // This could either be the provided definition, or one of its
+        // sub-definitions.
+        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
+        AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+        ManagedObjectDefinition<? extends C, ? extends S> mod = d.resolveManagedObjectDefinition(resolver);
+
+        // Build the managed object's properties.
+        List<PropertyException> exceptions = new LinkedList<PropertyException>();
+        Map<PropertyDefinition<?>, SortedSet<?>> properties = new HashMap<PropertyDefinition<?>, SortedSet<?>>();
+        for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+            List<AttributeValue> values = getAttribute(mod, pd, configEntry);
+            try {
+                SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
+                properties.put(pd, pvalues);
+            } catch (PropertyException e) {
+                exceptions.add(e);
+            }
+        }
+
+        // If there were no decoding problems then return the managed
+        // object, otherwise throw an operations exception.
+        ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties, configEntry);
+        if (exceptions.isEmpty()) {
+            return mo;
+        } else {
+            throw new ServerManagedObjectDecodingException(mo, exceptions);
+        }
     }
 
-    return configEntry;
-  }
-
-
-
-  // Validate that a relation definition belongs to the path.
-  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
-      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
-    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
-    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
-    if (tmp != rd) {
-      throw new IllegalArgumentException("The relation " + rd.getName()
-          + " is not associated with a " + d.getName());
+    // Decode helper method required to avoid generics warning.
+    private <C extends ConfigurationClient, S extends Configuration> ServerManagedObject<S> decodeAux(
+            ManagedObjectPath<? super C, ? super S> path, ManagedObjectDefinition<C, S> d,
+            Map<PropertyDefinition<?>, SortedSet<?>> properties, ConfigEntry configEntry) {
+        ManagedObjectPath<C, S> newPath = path.asSubType(d);
+        return new ServerManagedObject<S>(newPath, d, properties, configEntry);
     }
-  }
+
+    // Create a property using the provided string values.
+    private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path, PropertyDefinition<T> pd,
+            List<AttributeValue> values, ConfigEntry newConfigEntry) throws PropertyException {
+        PropertyException exception = null;
+        SortedSet<T> pvalues = new TreeSet<T>(pd);
+
+        if (!values.isEmpty()) {
+            // The property has values defined for it.
+            for (AttributeValue value : values) {
+                try {
+                    pvalues.add(ValueDecoder.decode(pd, value));
+                } catch (IllegalPropertyValueStringException e) {
+                    exception = e;
+                }
+            }
+        } else {
+            // No values defined so get the defaults.
+            try {
+                pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
+            } catch (DefaultBehaviorException e) {
+                exception = e;
+            }
+        }
+
+        if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+            // This exception takes precedence over previous exceptions.
+            exception = new PropertyIsSingleValuedException(pd);
+            T value = pvalues.first();
+            pvalues.clear();
+            pvalues.add(value);
+        }
+
+        if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+            // The values maybe empty because of a previous exception.
+            if (exception == null) {
+                exception = new PropertyIsMandatoryException(pd);
+            }
+        }
+
+        if (exception != null) {
+            throw exception;
+        } else {
+            return pvalues;
+        }
+    }
+
+    // Gets the attribute associated with a property from a ConfigEntry.
+    private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d, PropertyDefinition<?> pd,
+            ConfigEntry configEntry) {
+        // TODO: we create a default attribute type if it is
+        // undefined. We should log a warning here if this is the case
+        // since the attribute should have been defined.
+        String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
+        AttributeType type = DirectoryServer.getAttributeType(attrID, true);
+        AttributeValueDecoder<AttributeValue> decoder = new AttributeValueDecoder<AttributeValue>() {
+
+            public AttributeValue decode(AttributeValue value) throws DirectoryException {
+                return value;
+            }
+        };
+
+        List<AttributeValue> values = new LinkedList<AttributeValue>();
+        try {
+            configEntry.getEntry().getAttributeValues(type, decoder, values);
+        } catch (DirectoryException e) {
+            // Should not happen.
+            throw new RuntimeException(e);
+        }
+        return values;
+    }
+
+    // Get the default values for the specified property.
+    private <T> Collection<T> getDefaultValues(ManagedObjectPath<?, ?> p, PropertyDefinition<T> pd,
+            ConfigEntry newConfigEntry) throws DefaultBehaviorException {
+        DefaultValueFinder<T> v = new DefaultValueFinder<T>(newConfigEntry);
+        return v.find(p, pd);
+    }
+
+    // Gets a config entry required for a managed object and throws a
+    // config exception on failure.
+    private ConfigEntry getManagedObjectConfigEntry(DN dn) throws ConfigException {
+        ConfigEntry configEntry;
+        try {
+            configEntry = DirectoryServer.getConfigEntry(dn);
+        } catch (ConfigException e) {
+            if (debugEnabled()) {
+                TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+
+            LocalizableMessage message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(String.valueOf(dn),
+                    stackTraceToSingleLineString(e));
+            throw new ConfigException(message, e);
+        }
+
+        // The configuration handler is free to return null indicating
+        // that the entry does not exist.
+        if (configEntry == null) {
+            LocalizableMessage message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST.get(String.valueOf(dn));
+            throw new ConfigException(message);
+        }
+
+        return configEntry;
+    }
+
+    // Validate that a relation definition belongs to the path.
+    private void validateRelationDefinition(ManagedObjectPath<?, ?> path, RelationDefinition<?, ?> rd)
+            throws IllegalArgumentException {
+        AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+        RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
+        if (tmp != rd) {
+            throw new IllegalArgumentException("The relation " + rd.getName() + " is not associated with a "
+                    + d.getName());
+        }
+    }
 }

--
Gitblit v1.10.0