From 45359adc09ac1d9e48206c549e667ed6965c7cd3 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 28 May 2007 15:31:13 +0000
Subject: [PATCH] Fix the following issues:

---
 opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java |  799 +++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 524 insertions(+), 275 deletions(-)

diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index d2f3d56..a0a5bfc 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -51,18 +51,22 @@
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
+import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
+import org.opends.server.admin.AliasDefaultBehaviorProvider;
 import org.opends.server.admin.ConfigurationClient;
+import org.opends.server.admin.DefaultBehaviorException;
+import org.opends.server.admin.DefaultBehaviorProviderVisitor;
+import org.opends.server.admin.DefinedDefaultBehaviorProvider;
 import org.opends.server.admin.DefinitionDecodingException;
 import org.opends.server.admin.DefinitionResolver;
 import org.opends.server.admin.IllegalPropertyValueException;
-import org.opends.server.admin.InheritedDefaultValueProvider;
+import org.opends.server.admin.IllegalPropertyValueStringException;
 import org.opends.server.admin.InstantiableRelationDefinition;
 import org.opends.server.admin.ManagedObjectAlreadyExistsException;
 import org.opends.server.admin.ManagedObjectDefinition;
 import org.opends.server.admin.ManagedObjectNotFoundException;
 import org.opends.server.admin.ManagedObjectPath;
-import org.opends.server.admin.OperationsException;
 import org.opends.server.admin.OptionalRelationDefinition;
 import org.opends.server.admin.PropertyDefinition;
 import org.opends.server.admin.PropertyException;
@@ -70,16 +74,18 @@
 import org.opends.server.admin.PropertyIsReadOnlyException;
 import org.opends.server.admin.PropertyIsSingleValuedException;
 import org.opends.server.admin.PropertyNotFoundException;
-import org.opends.server.admin.PropertyProvider;
+import org.opends.server.admin.PropertyOption;
 import org.opends.server.admin.RelationDefinition;
+import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
 import org.opends.server.admin.SingletonRelationDefinition;
-import org.opends.server.admin.StringPropertyProvider;
+import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
 import org.opends.server.admin.DefinitionDecodingException.Reason;
 import org.opends.server.admin.client.AuthorizationException;
 import org.opends.server.admin.client.CommunicationException;
 import org.opends.server.admin.client.ConcurrentModificationException;
 import org.opends.server.admin.client.ManagedObject;
 import org.opends.server.admin.client.ManagedObjectDecodingException;
+import org.opends.server.admin.client.MissingMandatoryPropertiesException;
 import org.opends.server.admin.client.OperationRejectedException;
 import org.opends.server.admin.client.Property;
 import org.opends.server.admin.client.PropertySet;
@@ -99,31 +105,66 @@
     ManagedObject<C> {
 
   /**
-   * Internal inherited default value provider implementation.
+   * A default behavior visitor used for retrieving the default values
+   * of a property.
+   *
+   * @param <T>
+   *          The type of the property.
    */
-  private static class MyInheritedDefaultValueProvider implements
-      InheritedDefaultValueProvider {
+  private static class DefaultValueFinder<T> implements
+      DefaultBehaviorProviderVisitor<T, Collection<T>, ManagedObjectPath> {
+
+    /**
+     * Get the default values for the specified property.
+     *
+     * @param <T>
+     *          The type of the property.
+     * @param context
+     *          The LDAP management context.
+     * @param p
+     *          The managed object path of the current managed object.
+     * @param pd
+     *          The property definition.
+     * @return Returns the default values for the specified property.
+     * @throws DefaultBehaviorException
+     *           If the default values could not be retrieved or
+     *           decoded properly.
+     */
+    public static <T> Collection<T> getDefaultValues(
+        LDAPManagementContext context, ManagedObjectPath p,
+        PropertyDefinition<T> pd) throws DefaultBehaviorException {
+      DefaultValueFinder<T> v = new DefaultValueFinder<T>(context, pd);
+      Collection<T> values = pd.getDefaultBehaviorProvider().accept(v, p);
+
+      if (v.exception != null) {
+        throw v.exception;
+      }
+
+      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+        throw new DefaultBehaviorException(pd,
+            new PropertyIsSingleValuedException(pd));
+      }
+
+      return values;
+    }
 
     // The LDAP management context.
     private final LDAPManagementContext context;
 
-    // The base path.
-    private final ManagedObjectPath path;
+    // Any exception that occurred whilst retrieving inherited default
+    // values.
+    private DefaultBehaviorException exception = null;
+
+    // The property definition whose default values are required.
+    private final PropertyDefinition<T> pd;
 
 
 
-    /**
-     * Create a new inherited default value provider.
-     *
-     * @param context
-     *          The LDAP management context.
-     * @param path
-     *          The base path.
-     */
-    public MyInheritedDefaultValueProvider(LDAPManagementContext context,
-        ManagedObjectPath path) {
+    // Private constructor.
+    private DefaultValueFinder(LDAPManagementContext context,
+        PropertyDefinition<T> pd) {
       this.context = context;
-      this.path = path;
+      this.pd = pd;
     }
 
 
@@ -131,26 +172,15 @@
     /**
      * {@inheritDoc}
      */
-    public Collection<?> getDefaultPropertyValues(ManagedObjectPath path,
-        String propertyName) throws OperationsException,
-        PropertyNotFoundException {
-      ManagedObjectPath<?, ?> tmp = path;
-      ManagedObject<?> mo;
+    public Collection<T> visitAbsoluteInherited(
+        AbsoluteInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
       try {
-        mo = readEntry(context, tmp, tmp.getManagedObjectDefinition());
-      } catch (AuthorizationException e) {
-        throw new OperationsException(e);
-      } catch (CommunicationException e) {
-        throw new OperationsException(e);
+        return getInheritedProperty(d.getManagedObjectPath(), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = new DefaultBehaviorException(pd, e);
       }
-      ManagedObjectDefinition<?, ?> mod = mo.getManagedObjectDefinition();
-
-      try {
-        PropertyDefinition<?> dpd = mod.getPropertyDefinition(propertyName);
-        return mo.getPropertyValues(dpd);
-      } catch (IllegalArgumentException e) {
-        throw new PropertyNotFoundException(propertyName);
-      }
+      return Collections.emptySet();
     }
 
 
@@ -158,10 +188,133 @@
     /**
      * {@inheritDoc}
      */
-    public ManagedObjectPath getManagedObjectPath() {
-      return path;
+    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      return Collections.emptySet();
     }
-  }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      Collection<String> stringValues = d.getDefaultValues();
+      List<T> values = new ArrayList<T>(stringValues.size());
+
+      for (String stringValue : stringValues) {
+        try {
+          values.add(pd.decodeValue(stringValue));
+        } catch (IllegalPropertyValueStringException e) {
+          exception = new DefaultBehaviorException(pd, e);
+          break;
+        }
+      }
+
+      return values;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitRelativeInherited(
+        RelativeInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
+      try {
+        return getInheritedProperty(d.getManagedObjectPath(p), d
+            .getManagedObjectDefinition(), d.getPropertyName());
+      } catch (DefaultBehaviorException e) {
+        exception = new DefaultBehaviorException(pd, e);
+      }
+      return Collections.emptySet();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
+        ManagedObjectPath p) {
+      return Collections.emptySet();
+    }
+
+
+
+    // Get an inherited property value.
+    private Collection<T> getInheritedProperty(ManagedObjectPath target,
+        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
+        throws DefaultBehaviorException {
+      try {
+        // First check that the requested type of managed object
+        // corresponds to the path.
+        AbstractManagedObjectDefinition<?, ?> supr = target
+            .getManagedObjectDefinition();
+        if (!supr.isParentOf(d)) {
+          throw new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION);
+        }
+
+        // Get the actual managed object definition.
+        LdapName dn = LDAPNameBuilder.create(target, context.getLDAPProfile());
+        ManagedObjectDefinition<?, ?> mod = getEntryDefinition(context, d, dn);
+
+        PropertyDefinition<?> pd2;
+        try {
+          pd2 = mod.getPropertyDefinition(propertyName);
+        } catch (IllegalArgumentException e) {
+          throw new PropertyNotFoundException(propertyName);
+        }
+
+        String attrID = context.getLDAPProfile().getAttributeName(mod, pd2);
+        Attributes attributes = context.getLDAPConnection().readEntry(dn,
+            Collections.singleton(attrID));
+        Attribute attr = attributes.get(attrID);
+        if (attr == null || attr.size() == 0) {
+          // Recursively retrieve this property's default values.
+          Collection<?> tmp = getDefaultValues(context, target, pd2);
+          Collection<T> values = new ArrayList<T>(tmp.size());
+          for (Object o : tmp) {
+            T value;
+            try {
+              value = pd.castValue(o);
+            } catch (ClassCastException e) {
+              throw new IllegalPropertyValueException(pd, o);
+            }
+            pd.validateValue(value);
+            values.add(value);
+          }
+          return values;
+        } else {
+          Collection<T> values = new LinkedList<T>();
+          NamingEnumeration<?> ne = attr.getAll();
+          while (ne.hasMore()) {
+            Object value = ne.next();
+            if (value != null) {
+              values.add(pd.decodeValue(value.toString()));
+            }
+          }
+          return values;
+        }
+      } catch (DefinitionDecodingException e) {
+        throw new DefaultBehaviorException(pd, e);
+      } catch (PropertyNotFoundException e) {
+        throw new DefaultBehaviorException(pd, e);
+      } catch (IllegalPropertyValueException e) {
+        throw new DefaultBehaviorException(pd, e);
+      } catch (IllegalPropertyValueStringException e) {
+        throw new DefaultBehaviorException(pd, e);
+      } catch (NameNotFoundException e) {
+        throw new DefaultBehaviorException(pd,
+            new ManagedObjectNotFoundException());
+      } catch (NoPermissionException e) {
+        throw new DefaultBehaviorException(pd, new AuthorizationException(e));
+      } catch (NamingException e) {
+        throw new DefaultBehaviorException(pd, new CommunicationException(e));
+      }
+    }
+  };
 
 
 
@@ -176,30 +329,8 @@
    */
   static ManagedObject<RootCfgClient> getRootManagedObject(
       LDAPManagementContext context) {
-    List<PropertyException> exceptions = new LinkedList<PropertyException>();
-    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        context, ManagedObjectPath.emptyPath());
-    PropertySet properties = PropertySet.create(RootCfgDefn.getInstance(),
-        PropertyProvider.DEFAULT_PROVIDER, i, exceptions);
-
-    // Should never get any exceptions.
-    if (!exceptions.isEmpty()) {
-      throw new RuntimeException(
-          "Got exceptions when creating root managed object");
-    }
-
     return new LDAPManagedObject<RootCfgClient>(context, RootCfgDefn
-        .getInstance(), ManagedObjectPath.emptyPath(), properties);
-  }
-
-
-
-  // Create a new child LDAP managed object.
-  private static <M extends ConfigurationClient>
-      ManagedObject<M> createLDAPManagedObject(
-      LDAPManagementContext context, ManagedObjectDefinition<M, ?> d,
-      ManagedObjectPath p, PropertySet properties) {
-    return new LDAPManagedObject<M>(context, d, p, properties);
+        .getInstance(), ManagedObjectPath.emptyPath(), new PropertySet(), true);
   }
 
 
@@ -247,104 +378,17 @@
     return d.resolveManagedObjectDefinition(resolver);
   }
 
-
-
-  // Read the entry identified by the path and which is a sub-type of
-  // the specified definition.
-  private static <M extends ConfigurationClient>
-      ManagedObject<? extends M> readEntry(
-      final LDAPManagementContext context, ManagedObjectPath p,
-      AbstractManagedObjectDefinition<M, ?> d)
-      throws DefinitionDecodingException, ManagedObjectDecodingException,
-      ManagedObjectNotFoundException, AuthorizationException,
-      CommunicationException {
-    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
-
-    final ManagedObjectDefinition<? extends M, ?> mod;
-    final Attributes attributes;
-    try {
-      mod = getEntryDefinition(context, d, dn);
-      ArrayList<String> attrIds = new ArrayList<String>();
-      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-        String attrId = context.getLDAPProfile().getAttributeName(mod, pd);
-        attrIds.add(attrId);
-      }
-      attributes = context.getLDAPConnection().readEntry(dn, attrIds);
-    } catch (NameNotFoundException e) {
-      throw new ManagedObjectNotFoundException();
-    } catch (NoPermissionException e) {
-      throw new AuthorizationException(e);
-    } catch (NamingException e) {
-      throw new CommunicationException(e);
-    }
-
-    // Create a provider which uses LDAP string representations.
-
-    // TODO: the exception handling is a bit of a hack here.
-    final List<NamingException> nelist = new LinkedList<NamingException>();
-    StringPropertyProvider provider = new StringPropertyProvider() {
-
-      public Collection<String> getPropertyValues(PropertyDefinition<?> d)
-          throws IllegalArgumentException {
-        String attrID = context.getLDAPProfile().getAttributeName(mod, d);
-        Attribute attribute = attributes.get(attrID);
-        List<String> values = new LinkedList<String>();
-
-        if (attribute != null && attribute.size() != 0) {
-          try {
-            NamingEnumeration<?> ldapValues = attribute.getAll();
-            while (ldapValues.hasMore()) {
-              Object obj = ldapValues.next();
-              if (obj != null) {
-                values.add(obj.toString());
-              }
-            }
-          } catch (NamingException e) {
-            nelist.add(e);
-          }
-        }
-
-        return values;
-      }
-
-    };
-
-    // There can only be at most one exception.
-    if (!nelist.isEmpty()) {
-      try {
-        throw nelist.get(0);
-      } catch (NameNotFoundException e) {
-        throw new ManagedObjectNotFoundException();
-      } catch (NoPermissionException e) {
-        throw new AuthorizationException(e);
-      } catch (NamingException e) {
-        throw new CommunicationException(e);
-      }
-    }
-
-    // Now decode the properties using the provider.
-    List<PropertyException> exceptions = new LinkedList<PropertyException>();
-    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        context, p);
-    PropertySet properties = PropertySet.create(mod, provider, i, exceptions);
-    ManagedObject<? extends M> mo = createLDAPManagedObject(context, mod, p,
-        properties);
-
-    // If there were no decoding problems then return the object,
-    // otherwise throw an operations exception.
-    if (exceptions.isEmpty()) {
-      return mo;
-    } else {
-      throw new ManagedObjectDecodingException(mo, exceptions);
-    }
-  }
+  // The LDAP management context used for the ldap connection.
+  private final LDAPManagementContext context;
 
   // The managed object definition associated with this managed
   // object.
   private final ManagedObjectDefinition<C, ?> definition;
 
-  // The LDAP management context used for the ldap connection.
-  private final LDAPManagementContext context;
+  // Indicates whether or not this managed object exists on the server
+  // (false means the managed object is new and has not been
+  // committed).
+  private boolean existsOnServer;
 
   // The path associated with this managed object.
   private final ManagedObjectPath<?, ?> path;
@@ -357,11 +401,12 @@
   // Create an new LDAP managed object with the provided JNDI context.
   private LDAPManagedObject(LDAPManagementContext context,
       ManagedObjectDefinition<C, ?> d, ManagedObjectPath path,
-      PropertySet properties) {
+      PropertySet properties, boolean existsOnServer) {
     this.definition = d;
     this.context = context;
     this.path = path;
     this.properties = properties;
+    this.existsOnServer = existsOnServer;
   }
 
 
@@ -369,36 +414,30 @@
   /**
    * {@inheritDoc}
    */
-  public void commit() throws ConcurrentModificationException,
-      OperationRejectedException, AuthorizationException,
-      CommunicationException {
-    // Build the list of modified attributes.
-    ManagedObjectDefinition<C, ?> d = getManagedObjectDefinition();
-    Attributes mods = new BasicAttributes();
-    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+  public void commit() throws MissingMandatoryPropertiesException,
+      ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException,
+      ManagedObjectAlreadyExistsException {
+    // First make sure all mandatory properties are defined.
+    List<PropertyIsMandatoryException> exceptions =
+      new LinkedList<PropertyIsMandatoryException>();
+
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
       Property<?> p = properties.getProperty(pd);
-      if (p.isModified()) {
-        String attrID = context.getLDAPProfile().getAttributeName(d, pd);
-        Attribute attribute = new BasicAttribute(attrID);
-        encodeProperty(attribute, pd, properties);
-        mods.put(attribute);
+      if (pd.hasOption(PropertyOption.MANDATORY) && p.isEmpty()) {
+        exceptions.add(new PropertyIsMandatoryException(pd));
       }
     }
 
-    // Perform the LDAP modification if something has changed.
-    if (mods.size() > 0) {
-      try {
-        LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
-        context.getLDAPConnection().modifyEntry(dn, mods);
-      } catch (NoPermissionException e) {
-        throw new AuthorizationException(e);
-      } catch (OperationNotSupportedException e) {
-        // Unwilling to perform.
-        throw new OperationRejectedException(e);
-      } catch (NamingException e) {
-        // Just treat it as a communication problem.
-        throw new CommunicationException(e);
-      }
+    if (!exceptions.isEmpty()) {
+      throw new MissingMandatoryPropertiesException(exceptions);
+    }
+
+    // Commit the managed object.
+    if (existsOnServer) {
+      commitExistingManagedObject();
+    } else {
+      commitNewManagedObject();
     }
   }
 
@@ -410,60 +449,11 @@
   public <M extends ConfigurationClient, N extends M>
       ManagedObject<N> createChild(
       InstantiableRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
-      String name, PropertyProvider p) throws IllegalArgumentException,
-      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
-      ConcurrentModificationException, OperationRejectedException,
-      AuthorizationException, CommunicationException {
+      String name, Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException {
     validateRelationDefinition(r);
-
     ManagedObjectPath childPath = path.child(r, name);
-
-    // First make sure all the properties are valid.
-    List<PropertyException> exceptions = new LinkedList<PropertyException>();
-    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        context, childPath);
-    PropertySet properties = PropertySet.create(d, p, i, exceptions);
-    if (!exceptions.isEmpty()) {
-      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
-          properties);
-      throw new ManagedObjectDecodingException(mo, exceptions);
-    }
-
-    ensureThisManagedObjectExists();
-
-    // TODO: this implementation does not handle relations which
-    // comprise of more than one RDN arc (this will probably never
-    // be required anyway).
-    LdapName dn = LDAPNameBuilder.create(path, r, context.getLDAPProfile());
-    if (!entryExists(dn)) {
-      // Need to create the child managed object's parent entry i.e.
-      // the entry representing the relation itself.
-      Attributes attributes = new BasicAttributes();
-
-      // Create the branch's object class attribute.
-      Attribute oc = new BasicAttribute("objectClass");
-      for (String objectClass : context.getLDAPProfile()
-          .getInstantiableRelationObjectClasses(r)) {
-        oc.add(objectClass);
-      }
-      attributes.put(oc);
-
-      // Create the branch's naming attribute.
-      Rdn rdn = dn.getRdn(dn.size() - 1);
-      attributes.put(rdn.getType(), rdn.getValue().toString());
-
-      // Create the entry.
-      try {
-        context.getLDAPConnection().createEntry(dn, attributes);
-      } catch (OperationNotSupportedException e) {
-        // Unwilling to perform.
-        throw new OperationRejectedException(e);
-      } catch (NamingException e) {
-        adaptNamingException(e);
-      }
-    }
-
-    return createManagedObject(childPath, d, properties);
+    return createNewManagedObject(d, childPath, exceptions);
   }
 
 
@@ -474,28 +464,11 @@
   public <M extends ConfigurationClient, N extends M>
       ManagedObject<N> createChild(
       OptionalRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
-      PropertyProvider p) throws IllegalArgumentException,
-      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
-      ConcurrentModificationException, OperationRejectedException,
-      AuthorizationException, CommunicationException {
+      Collection<DefaultBehaviorException> exceptions)
+      throws IllegalArgumentException {
     validateRelationDefinition(r);
-
     ManagedObjectPath childPath = path.child(r);
-
-    // First make sure all the properties are valid.
-    List<PropertyException> exceptions = new LinkedList<PropertyException>();
-    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        context, childPath);
-    PropertySet properties = PropertySet.create(d, p, i, exceptions);
-    if (!exceptions.isEmpty()) {
-      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
-          properties);
-      throw new ManagedObjectDecodingException(mo, exceptions);
-    }
-
-    ensureThisManagedObjectExists();
-
-    return createManagedObject(childPath, d, properties);
+    return createNewManagedObject(d, childPath, exceptions);
   }
 
 
@@ -511,7 +484,7 @@
       CommunicationException {
     validateRelationDefinition(d);
     ensureThisManagedObjectExists();
-    return readEntry(context, path.child(d, name), d.getChildDefinition());
+    return readManagedObject(d.getChildDefinition(), path.child(d, name));
   }
 
 
@@ -526,7 +499,7 @@
       AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
     ensureThisManagedObjectExists();
-    return readEntry(context, path.child(d), d.getChildDefinition());
+    return readManagedObject(d.getChildDefinition(), path.child(d));
   }
 
 
@@ -541,7 +514,7 @@
       AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
     ensureThisManagedObjectExists();
-    return readEntry(context, path.child(d), d.getChildDefinition());
+    return readManagedObject(d.getChildDefinition(), path.child(d));
   }
 
 
@@ -674,6 +647,14 @@
   public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
       throws IllegalPropertyValueException, PropertyIsReadOnlyException,
       PropertyIsMandatoryException, IllegalArgumentException {
+    if (d.hasOption(PropertyOption.MONITORING)) {
+      throw new PropertyIsReadOnlyException(d);
+    }
+
+    if (existsOnServer && d.hasOption(PropertyOption.READ_ONLY)) {
+      throw new PropertyIsReadOnlyException(d);
+    }
+
     properties.setPropertyValue(d, value);
   }
 
@@ -686,6 +667,14 @@
       Collection<T> values) throws IllegalPropertyValueException,
       PropertyIsSingleValuedException, PropertyIsReadOnlyException,
       PropertyIsMandatoryException, IllegalArgumentException {
+    if (d.hasOption(PropertyOption.MONITORING)) {
+      throw new PropertyIsReadOnlyException(d);
+    }
+
+    if (existsOnServer && d.hasOption(PropertyOption.READ_ONLY)) {
+      throw new PropertyIsReadOnlyException(d);
+    }
+
     properties.setPropertyValues(d, values);
   }
 
@@ -711,30 +700,118 @@
 
 
 
-  // Creates a new managed object. The parent LDAP entry is assumed to
-  // already exist.
-  private <N extends ConfigurationClient> ManagedObject<N> createManagedObject(
-      ManagedObjectPath path, ManagedObjectDefinition<N, ?> d,
-      PropertySet properties) throws ManagedObjectAlreadyExistsException,
-      OperationRejectedException, AuthorizationException,
-      CommunicationException {
+  // Commit modifications made to this managed object.
+  private void commitExistingManagedObject()
+      throws ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
+    // Build the list of modified attributes.
+    Attributes mods = new BasicAttributes();
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      Property<?> p = properties.getProperty(pd);
+      if (p.isModified()) {
+        String attrID = context.getLDAPProfile().getAttributeName(definition,
+            pd);
+        Attribute attribute = new BasicAttribute(attrID);
+        encodeProperty(attribute, pd, properties);
+        mods.put(attribute);
+      }
+    }
+
+    // Perform the LDAP modification if something has changed.
+    if (mods.size() > 0) {
+      try {
+        LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
+        context.getLDAPConnection().modifyEntry(dn, mods);
+      } catch (NoPermissionException e) {
+        throw new AuthorizationException(e);
+      } catch (OperationNotSupportedException e) {
+        // Unwilling to perform.
+        throw new OperationRejectedException(e);
+      } catch (NamingException e) {
+        // Just treat it as a communication problem.
+        throw new CommunicationException(e);
+      }
+    }
+
+    // The changes were committed successfully so update this managed
+    // object's state.
+    properties.commit();
+  }
+
+
+
+  // Commit this new managed object.
+  private void commitNewManagedObject() throws AuthorizationException,
+      CommunicationException, OperationRejectedException,
+      ConcurrentModificationException, ManagedObjectAlreadyExistsException {
+    // First make sure that the parent managed object still exists.
+    ManagedObjectPath<?, ?> parent = path.parent();
+    if (!parent.isEmpty()) {
+      LdapName dn = LDAPNameBuilder.create(parent, context.getLDAPProfile());
+      if (!entryExists(dn)) {
+        throw new ConcurrentModificationException();
+      }
+    }
+
+    // We may need to create the parent "relation" entry if this is a
+    // child of an instantiable relation.
+    RelationDefinition<?, ?> r = path.getRelationDefinition();
+    if (r instanceof InstantiableRelationDefinition) {
+      InstantiableRelationDefinition<?, ?> ir =
+        (InstantiableRelationDefinition<?, ?>) r;
+
+      // TODO: this implementation does not handle relations which
+      // comprise of more than one RDN arc (this will probably never
+      // be required anyway).
+      LdapName dn = LDAPNameBuilder
+          .create(parent, ir, context.getLDAPProfile());
+      if (!entryExists(dn)) {
+        // We need to create the entry.
+        Attributes attributes = new BasicAttributes();
+
+        // Create the branch's object class attribute.
+        Attribute oc = new BasicAttribute("objectClass");
+        for (String objectClass : context.getLDAPProfile()
+            .getInstantiableRelationObjectClasses(ir)) {
+          oc.add(objectClass);
+        }
+        attributes.put(oc);
+
+        // Create the branch's naming attribute.
+        Rdn rdn = dn.getRdn(dn.size() - 1);
+        attributes.put(rdn.getType(), rdn.getValue().toString());
+
+        // Create the entry.
+        try {
+          context.getLDAPConnection().createEntry(dn, attributes);
+        } catch (OperationNotSupportedException e) {
+          // Unwilling to perform.
+          throw new OperationRejectedException(e);
+        } catch (NamingException e) {
+          adaptNamingException(e);
+        }
+      }
+    }
+
+    // Now add the entry representing this new managed object.
     LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
     Attributes attributes = new BasicAttributes(true);
 
-    // Create the child's object class attribute.
+    // Create the object class attribute.
     Attribute oc = new BasicAttribute("objectclass");
-    for (String objectClass : context.getLDAPProfile().getObjectClasses(d)) {
+    for (String objectClass : context.getLDAPProfile().getObjectClasses(
+        definition)) {
       oc.add(objectClass);
     }
     attributes.put(oc);
 
-    // Create the child's naming attribute.
+    // Create the naming attribute.
     Rdn rdn = dn.getRdn(dn.size() - 1);
     attributes.put(rdn.getType(), rdn.getValue().toString());
 
     // Create the remaining attributes.
-    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
-      String attrID = context.getLDAPProfile().getAttributeName(d, pd);
+    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
+      String attrID = context.getLDAPProfile().getAttributeName(definition, pd);
       Attribute attribute = new BasicAttribute(attrID);
       encodeProperty(attribute, pd, properties);
       if (attribute.size() != 0) {
@@ -754,7 +831,108 @@
       adaptNamingException(e);
     }
 
-    return new LDAPManagedObject<N>(context, d, path, properties);
+    // The entry was created successfully so update this managed
+    // object's state.
+    properties.commit();
+    existsOnServer = true;
+  }
+
+
+
+  // Create a managed object which already exists on the server.
+  private <M extends ConfigurationClient>
+      ManagedObject<M> createExistingManagedObject(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath p,
+      PropertySet properties) {
+    return new LDAPManagedObject<M>(context, d, p, properties, true);
+  }
+
+
+
+  // Creates a new managed object with no active values, just default
+  // values.
+  private <M extends ConfigurationClient>
+      ManagedObject<M> createNewManagedObject(
+      ManagedObjectDefinition<M, ?> d, ManagedObjectPath p,
+      Collection<DefaultBehaviorException> exceptions) {
+    PropertySet childProperties = new PropertySet();
+    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
+      try {
+        createProperty(childProperties, p, pd);
+      } catch (DefaultBehaviorException e) {
+        // Add the exception if requested.
+        if (exceptions != null) {
+          exceptions.add(e);
+        }
+      }
+    }
+
+    return new LDAPManagedObject<M>(context, d, p, childProperties, false);
+  }
+
+
+
+  // Create an empty property.
+  private <T> void createProperty(PropertySet properties, ManagedObjectPath p,
+      PropertyDefinition<T> pd) throws DefaultBehaviorException {
+    try {
+      Collection<T> defaultValues = DefaultValueFinder.getDefaultValues(
+          context, p, pd);
+      properties.addProperty(pd, defaultValues, Collections.<T> emptySet());
+    } catch (DefaultBehaviorException e) {
+      // Make sure that we have still created the property.
+      properties.addProperty(pd, Collections.<T> emptySet(), Collections
+          .<T> emptySet());
+      throw e;
+    }
+  }
+
+
+
+  // Create a property using the provided string values.
+  private <T> void decodeProperty(PropertySet newProperties,
+      ManagedObjectPath p, PropertyDefinition<T> pd, List<String> values)
+      throws PropertyException {
+    PropertyException exception = null;
+
+    // Get the property's active values.
+    Collection<T> activeValues = new ArrayList<T>(values.size());
+    for (String value : values) {
+      try {
+        activeValues.add(pd.decodeValue(value));
+      } catch (IllegalPropertyValueStringException e) {
+        exception = e;
+      }
+    }
+
+    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+      // This exception takes precedence over previous exceptions.
+      exception = new PropertyIsSingleValuedException(pd);
+      T value = activeValues.iterator().next();
+      activeValues.clear();
+      activeValues.add(value);
+    }
+
+    if (activeValues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+      // The active values maybe empty because of a previous exception.
+      if (exception == null) {
+        exception = new PropertyIsMandatoryException(pd);
+      }
+    }
+
+    // Get the property's default values.
+    Collection<T> defaultValues;
+    try {
+      defaultValues = DefaultValueFinder.getDefaultValues(context, p, pd);
+    } catch (DefaultBehaviorException e) {
+      defaultValues = Collections.emptySet();
+      exception = e;
+    }
+
+    newProperties.addProperty(pd, defaultValues, activeValues);
+    if (exception != null) {
+      throw exception;
+    }
   }
 
 
@@ -775,9 +953,11 @@
   private void ensureThisManagedObjectExists()
       throws ConcurrentModificationException, CommunicationException,
       AuthorizationException {
-    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
-    if (!entryExists(dn)) {
-      throw new ConcurrentModificationException();
+    if (!path.isEmpty()) {
+      LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
+      if (!entryExists(dn)) {
+        throw new ConcurrentModificationException();
+      }
     }
   }
 
@@ -796,6 +976,75 @@
 
 
 
+  // Read the entry identified by the path and which is a sub-type of
+  // the specified definition.
+  private <M extends ConfigurationClient>
+      ManagedObject<? extends M> readManagedObject(
+      AbstractManagedObjectDefinition<M, ?> d, ManagedObjectPath p)
+      throws DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    try {
+      // Read the entry associated with the managed object.
+      LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
+      ManagedObjectDefinition<? extends M, ?> mod = getEntryDefinition(context,
+          d, dn);
+
+      ArrayList<String> attrIds = new ArrayList<String>();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrId = context.getLDAPProfile().getAttributeName(mod, pd);
+        attrIds.add(attrId);
+      }
+
+      Attributes attributes = context.getLDAPConnection()
+          .readEntry(dn, attrIds);
+
+      // Build the managed object's properties.
+      List<PropertyException> exceptions = new LinkedList<PropertyException>();
+      PropertySet newProperties = new PropertySet();
+      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+        String attrID = context.getLDAPProfile().getAttributeName(mod, pd);
+        Attribute attribute = attributes.get(attrID);
+        List<String> values = new LinkedList<String>();
+
+        if (attribute != null && attribute.size() != 0) {
+          NamingEnumeration<?> ldapValues = attribute.getAll();
+          while (ldapValues.hasMore()) {
+            Object obj = ldapValues.next();
+            if (obj != null) {
+              values.add(obj.toString());
+            }
+          }
+        }
+
+        try {
+          decodeProperty(newProperties, p, pd, values);
+        } catch (PropertyException e) {
+          exceptions.add(e);
+        }
+      }
+
+      // If there were no decoding problems then return the object,
+      // otherwise throw an operations exception.
+      ManagedObject<? extends M> mo = createExistingManagedObject(mod, p,
+          newProperties);
+
+      if (exceptions.isEmpty()) {
+        return mo;
+      } else {
+        throw new ManagedObjectDecodingException(mo, exceptions);
+      }
+    } catch (NameNotFoundException e) {
+      throw new ManagedObjectNotFoundException();
+    } catch (NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
   // Remove the named managed object.
   private void removeManagedObject(ManagedObjectPath p)
       throws CommunicationException, AuthorizationException,

--
Gitblit v1.10.0