From 02805157fa504b3dbf701a62280ca9aaf82183b5 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Wed, 05 Sep 2007 20:12:11 +0000
Subject: [PATCH] Partial fix for issue 1449: administration framework aggregation support

---
 opends/src/server/org/opends/server/admin/server/ServerManagementContext.java |  174 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 139 insertions(+), 35 deletions(-)

diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java b/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
index ea2f943..a8d3391 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
+++ b/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -47,6 +47,7 @@
 import org.opends.messages.Message;
 import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AggregationPropertyDefinition;
 import org.opends.server.admin.AliasDefaultBehaviorProvider;
 import org.opends.server.admin.Configuration;
 import org.opends.server.admin.ConfigurationClient;
@@ -62,14 +63,17 @@
 import org.opends.server.admin.ManagedObjectDefinition;
 import org.opends.server.admin.ManagedObjectPath;
 import org.opends.server.admin.PropertyDefinition;
+import org.opends.server.admin.PropertyDefinitionVisitor;
 import org.opends.server.admin.PropertyException;
 import org.opends.server.admin.PropertyIsMandatoryException;
 import org.opends.server.admin.PropertyIsSingleValuedException;
 import org.opends.server.admin.PropertyNotFoundException;
 import org.opends.server.admin.PropertyOption;
+import org.opends.server.admin.Reference;
 import org.opends.server.admin.RelationDefinition;
 import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
 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;
@@ -263,22 +267,22 @@
           throw new PropertyNotFoundException(propertyName);
         }
 
-        List<String> stringValues = getAttribute(mod, pd2, configEntry);
-        if (stringValues.isEmpty()) {
+        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> values = new ArrayList<T>(tmp.size());
+          Collection<T> pvalues = new ArrayList<T>(tmp.size());
           for (T value : tmp) {
             pd1.validateValue(value);
-            values.add(value);
+            pvalues.add(value);
           }
-          return values;
+          return pvalues;
         } else {
-          Collection<T> values = new ArrayList<T>(stringValues.size());
-          for (String s : stringValues) {
-            values.add(pd1.decodeValue(s));
+          Collection<T> pvalues = new ArrayList<T>(values.size());
+          for (AttributeValue value : values) {
+            pvalues.add(ValueDecoder.decode(pd1, value));
           }
-          return values;
+          return pvalues;
         }
       } catch (DefinitionDecodingException e) {
         throw new DefaultBehaviorException(pd1, e);
@@ -321,7 +325,76 @@
       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.getStringValue();
+      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 =
@@ -399,6 +472,12 @@
    * 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
@@ -417,7 +496,8 @@
    *           If the named managed object could not be found or if it
    *           could not be decoded.
    */
-  public <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
+  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);
@@ -434,6 +514,12 @@
    * 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
@@ -452,15 +538,27 @@
    *           If the named managed object could not be found or if it
    *           could not be decoded.
    */
-  public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
+  @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);
-    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
-    ManagedObjectDefinition<?, ?> mod;
+    ManagedObjectDefinition<? extends C, ? extends S> mod;
 
     try {
       mod = d.resolveManagedObjectDefinition(resolver);
@@ -469,8 +567,13 @@
           .createDecodingExceptionAdaptor(dn, e);
     }
 
-    List<String> values = getAttribute(mod, pd, configEntry);
-    return decodeProperty(path, pd, values, null);
+    // 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);
   }
 
 
@@ -649,7 +752,7 @@
     Map<PropertyDefinition<?>, SortedSet<?>> properties =
       new HashMap<PropertyDefinition<?>, SortedSet<?>>();
     for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-      List<String> values = getAttribute(mod, pd, configEntry);
+      List<AttributeValue> values = getAttribute(mod, pd, configEntry);
       try {
         SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
         properties.put(pd, pvalues);
@@ -686,16 +789,16 @@
 
   // Create a property using the provided string values.
   private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
-      PropertyDefinition<T> pd, List<String> stringValues,
+      PropertyDefinition<T> pd, List<AttributeValue> values,
       ConfigEntry newConfigEntry) throws PropertyException {
     PropertyException exception = null;
-    SortedSet<T> values = new TreeSet<T>(pd);
+    SortedSet<T> pvalues = new TreeSet<T>(pd);
 
-    if (!stringValues.isEmpty()) {
+    if (!values.isEmpty()) {
       // The property has values defined for it.
-      for (String value : stringValues) {
+      for (AttributeValue value : values) {
         try {
-          values.add(pd.decodeValue(value));
+          pvalues.add(ValueDecoder.decode(pd, value));
         } catch (IllegalPropertyValueStringException e) {
           exception = e;
         }
@@ -703,21 +806,21 @@
     } else {
       // No values defined so get the defaults.
       try {
-        values.addAll(getDefaultValues(path, pd, newConfigEntry));
+        pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
       } catch (DefaultBehaviorException e) {
         exception = e;
       }
     }
 
-    if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+    if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
       // This exception takes precedence over previous exceptions.
       exception = new PropertyIsSingleValuedException(pd);
-      T value = values.first();
-      values.clear();
-      values.add(value);
+      T value = pvalues.first();
+      pvalues.clear();
+      pvalues.add(value);
     }
 
-    if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+    if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
       // The values maybe empty because of a previous exception.
       if (exception == null) {
         exception = new PropertyIsMandatoryException(pd);
@@ -727,29 +830,30 @@
     if (exception != null) {
       throw exception;
     } else {
-      return values;
+      return pvalues;
     }
   }
 
 
 
   // Gets the attribute associated with a property from a ConfigEntry.
-  private List<String> getAttribute(ManagedObjectDefinition<?, ?> d,
+  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<String> decoder =
-      new AttributeValueDecoder<String>() {
+    AttributeValueDecoder<AttributeValue> decoder =
+      new AttributeValueDecoder<AttributeValue>() {
 
-      public String decode(AttributeValue value) throws DirectoryException {
-        return value.getStringValue();
+      public AttributeValue decode(AttributeValue value)
+          throws DirectoryException {
+        return value;
       }
     };
 
-    List<String> values = new LinkedList<String>();
+    List<AttributeValue> values = new LinkedList<AttributeValue>();
     try {
       configEntry.getEntry().getAttributeValues(type, decoder, values);
     } catch (DirectoryException e) {

--
Gitblit v1.10.0