From 89debdc4f4f38e0d973c9f4f37e1c34002deeeb4 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Tue, 22 May 2007 14:35:47 +0000
Subject: [PATCH] Various improvements and refactorings of the admin framework client API, including:

---
 opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java |  610 ++++++++++++++++++++++++++++--------------------------
 1 files changed, 316 insertions(+), 294 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index d95a896..d2f3d56 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -31,22 +31,23 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.SortedSet;
 
+import javax.naming.NameAlreadyBoundException;
 import javax.naming.NameNotFoundException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
+import javax.naming.NoPermissionException;
+import javax.naming.OperationNotSupportedException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
 import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.BasicAttributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.Rdn;
 
@@ -57,8 +58,9 @@
 import org.opends.server.admin.IllegalPropertyValueException;
 import org.opends.server.admin.InheritedDefaultValueProvider;
 import org.opends.server.admin.InstantiableRelationDefinition;
-import org.opends.server.admin.LDAPProfile;
+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;
@@ -73,8 +75,12 @@
 import org.opends.server.admin.SingletonRelationDefinition;
 import org.opends.server.admin.StringPropertyProvider;
 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.OperationRejectedException;
 import org.opends.server.admin.client.Property;
 import org.opends.server.admin.client.PropertySet;
 import org.opends.server.admin.std.client.RootCfgClient;
@@ -89,8 +95,8 @@
  *          The type of client configuration represented by the client
  *          managed object.
  */
-final class LDAPManagedObject<C extends ConfigurationClient>
-    implements ManagedObject<C> {
+final class LDAPManagedObject<C extends ConfigurationClient> implements
+    ManagedObject<C> {
 
   /**
    * Internal inherited default value provider implementation.
@@ -98,25 +104,25 @@
   private static class MyInheritedDefaultValueProvider implements
       InheritedDefaultValueProvider {
 
+    // The LDAP management context.
+    private final LDAPManagementContext context;
+
     // The base path.
     private final ManagedObjectPath path;
 
-    // The LDAP connection context.
-    private final DirContext dirContext;
-
 
 
     /**
      * Create a new inherited default value provider.
      *
-     * @param dirContext
-     *          The LDAP connection context.
+     * @param context
+     *          The LDAP management context.
      * @param path
      *          The base path.
      */
-    public MyInheritedDefaultValueProvider(DirContext dirContext,
+    public MyInheritedDefaultValueProvider(LDAPManagementContext context,
         ManagedObjectPath path) {
-      this.dirContext = dirContext;
+      this.context = context;
       this.path = path;
     }
 
@@ -125,18 +131,22 @@
     /**
      * {@inheritDoc}
      */
-    public Collection<?> getDefaultPropertyValues(
-        ManagedObjectPath path, String propertyName)
-        throws OperationsException, PropertyNotFoundException {
+    public Collection<?> getDefaultPropertyValues(ManagedObjectPath path,
+        String propertyName) throws OperationsException,
+        PropertyNotFoundException {
       ManagedObjectPath<?, ?> tmp = path;
-      ManagedObject<?> mo = readEntry(dirContext, tmp, tmp
-          .getManagedObjectDefinition());
-      ManagedObjectDefinition<?, ?> mod = mo
-          .getManagedObjectDefinition();
+      ManagedObject<?> mo;
+      try {
+        mo = readEntry(context, tmp, tmp.getManagedObjectDefinition());
+      } catch (AuthorizationException e) {
+        throw new OperationsException(e);
+      } catch (CommunicationException e) {
+        throw new OperationsException(e);
+      }
+      ManagedObjectDefinition<?, ?> mod = mo.getManagedObjectDefinition();
 
       try {
-        PropertyDefinition<?> dpd = mod
-            .getPropertyDefinition(propertyName);
+        PropertyDefinition<?> dpd = mod.getPropertyDefinition(propertyName);
         return mo.getPropertyValues(dpd);
       } catch (IllegalArgumentException e) {
         throw new PropertyNotFoundException(propertyName);
@@ -159,19 +169,18 @@
    * Construct a root LDAP managed object associated with the provided
    * LDAP context.
    *
-   * @param dirContext
-   *          The LDAP directory context.
+   * @param context
+   *          The LDAP management context.
    * @return Returns a root LDAP managed object associated with the
    *         provided LDAP context.
    */
   static ManagedObject<RootCfgClient> getRootManagedObject(
-      DirContext dirContext) {
+      LDAPManagementContext context) {
     List<PropertyException> exceptions = new LinkedList<PropertyException>();
     InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        dirContext, ManagedObjectPath.emptyPath());
-    PropertySet properties = PropertySet.create(RootCfgDefn
-        .getInstance(), PropertyProvider.DEFAULT_PROVIDER, i,
-        exceptions);
+        context, ManagedObjectPath.emptyPath());
+    PropertySet properties = PropertySet.create(RootCfgDefn.getInstance(),
+        PropertyProvider.DEFAULT_PROVIDER, i, exceptions);
 
     // Should never get any exceptions.
     if (!exceptions.isEmpty()) {
@@ -179,37 +188,18 @@
           "Got exceptions when creating root managed object");
     }
 
-    return new LDAPManagedObject<RootCfgClient>(dirContext,
-        RootCfgDefn.getInstance(), ManagedObjectPath.emptyPath(),
-        properties);
+    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(
-      DirContext dirContext, ManagedObjectDefinition<M, ?> d,
+      ManagedObject<M> createLDAPManagedObject(
+      LDAPManagementContext context, ManagedObjectDefinition<M, ?> d,
       ManagedObjectPath p, PropertySet properties) {
-    return new LDAPManagedObject<M>(dirContext, d, p, properties);
-  }
-
-
-
-  // Get an array containing the list of LDAP attributes names
-  // associated with a
-  // managed object definition.
-  private static String[] getAttributeNames(
-      ManagedObjectDefinition<?, ?> d) {
-    ArrayList<String> attrIds = new ArrayList<String>();
-
-    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
-      String attrId = LDAPProfile.getInstance().getAttributeName(d,
-          pd);
-      attrIds.add(attrId);
-    }
-
-    return attrIds.toArray(new String[attrIds.size()]);
+    return new LDAPManagedObject<M>(context, d, p, properties);
   }
 
 
@@ -218,86 +208,85 @@
   // entry.
   private static <M extends ConfigurationClient>
       ManagedObjectDefinition<? extends M, ?> getEntryDefinition(
-      DirContext dirContext, AbstractManagedObjectDefinition<M, ?> d,
-      LdapName dn) throws OperationsException {
-    try {
-      String[] attrIds = new String[] { "objectclass" };
-      Attributes attributes = dirContext.getAttributes(dn, attrIds);
-      Attribute oc = attributes.get("objectclass");
+      final LDAPManagementContext context,
+      AbstractManagedObjectDefinition<M, ?> d, LdapName dn)
+      throws NamingException, DefinitionDecodingException {
+    Attributes attributes = context.getLDAPConnection().readEntry(dn,
+        Collections.singleton("objectclass"));
+    Attribute oc = attributes.get("objectclass");
 
-      if (oc == null) {
-        // No object classes.
-        throw new DefinitionDecodingException(
-            Reason.NO_TYPE_INFORMATION);
-      }
-
-      final Set<String> objectClasses = new HashSet<String>();
-      NamingEnumeration<?> values = oc.getAll();
-      while (values.hasMore()) {
-        Object value = values.next();
-        if (value != null) {
-          objectClasses.add(value.toString().toLowerCase().trim());
-        }
-      }
-
-      if (objectClasses.isEmpty()) {
-        // No object classes.
-        throw new DefinitionDecodingException(
-            Reason.NO_TYPE_INFORMATION);
-      }
-
-      // Resolve the appropriate sub-type based on the object classes.
-      DefinitionResolver resolver = new DefinitionResolver() {
-
-        public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
-          String objectClass = LDAPProfile.getInstance()
-              .getObjectClass(d);
-          return objectClasses.contains(objectClass);
-        }
-
-      };
-
-      return d.resolveManagedObjectDefinition(resolver);
-    } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
+    if (oc == null) {
+      // No object classes.
+      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
     }
+
+    final Set<String> objectClasses = new HashSet<String>();
+    NamingEnumeration<?> values = oc.getAll();
+    while (values.hasMore()) {
+      Object value = values.next();
+      if (value != null) {
+        objectClasses.add(value.toString().toLowerCase().trim());
+      }
+    }
+
+    if (objectClasses.isEmpty()) {
+      // No object classes.
+      throw new DefinitionDecodingException(Reason.NO_TYPE_INFORMATION);
+    }
+
+    // Resolve the appropriate sub-type based on the object classes.
+    DefinitionResolver resolver = new DefinitionResolver() {
+
+      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
+        String objectClass = context.getLDAPProfile().getObjectClass(d);
+        return objectClasses.contains(objectClass);
+      }
+
+    };
+
+    return d.resolveManagedObjectDefinition(resolver);
   }
 
 
 
   // Read the entry identified by the path and which is a sub-type of
-  // the
-  // specified definition.
+  // the specified definition.
   private static <M extends ConfigurationClient>
       ManagedObject<? extends M> readEntry(
-      DirContext dirContext, ManagedObjectPath p,
+      final LDAPManagementContext context, ManagedObjectPath p,
       AbstractManagedObjectDefinition<M, ?> d)
-      throws OperationsException {
-    LdapName dn = LDAPNameBuilder.create(p);
-    final ManagedObjectDefinition<? extends M, ?> mod = getEntryDefinition(
-        dirContext, d, dn);
-    String[] attrIds = getAttributeNames(mod);
+      throws DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, AuthorizationException,
+      CommunicationException {
+    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
 
+    final ManagedObjectDefinition<? extends M, ?> mod;
     final Attributes attributes;
     try {
-      attributes = dirContext.getAttributes(dn, attrIds);
+      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) {
-      OperationsExceptionFactory factory = new OperationsExceptionFactory();
-      throw factory.createException(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<OperationsException> oelist =
-      new LinkedList<OperationsException>();
+    final List<NamingException> nelist = new LinkedList<NamingException>();
     StringPropertyProvider provider = new StringPropertyProvider() {
 
-      public Collection<String> getPropertyValues(
-          PropertyDefinition<?> d) throws IllegalArgumentException {
-        String attrID = LDAPProfile.getInstance().getAttributeName(
-            mod, d);
+      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>();
 
@@ -311,8 +300,7 @@
               }
             }
           } catch (NamingException e) {
-            OperationsExceptionFactory oef = new OperationsExceptionFactory();
-            oelist.add(oef.createException(e));
+            nelist.add(e);
           }
         }
 
@@ -321,23 +309,29 @@
 
     };
 
-    // There can only be at most one operations exception.
-    if (!oelist.isEmpty()) {
-      throw oelist.get(0);
+    // 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(
-        dirContext, p);
-    PropertySet properties = PropertySet.create(mod, provider, i,
-        exceptions);
-    ManagedObject<? extends M> mo = createLDAPManagedObject(
-        dirContext, mod, p, properties);
+        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.
+    // otherwise throw an operations exception.
     if (exceptions.isEmpty()) {
       return mo;
     } else {
@@ -345,32 +339,28 @@
     }
   }
 
-  // The JNDI context used for the ldap connection.
-  private final DirContext dirContext;
-
-  // The path associated with this managed object.
-  private final ManagedObjectPath<?, ?> path;
-
-  // LDAP profile associated with this connection.
-  private final LDAPProfile profile;
-
   // 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;
+
+  // The path associated with this managed object.
+  private final ManagedObjectPath<?, ?> path;
+
   // The managed object's properties.
   private final PropertySet properties;
 
 
 
   // Create an new LDAP managed object with the provided JNDI context.
-  private LDAPManagedObject(DirContext dirContext,
+  private LDAPManagedObject(LDAPManagementContext context,
       ManagedObjectDefinition<C, ?> d, ManagedObjectPath path,
       PropertySet properties) {
     this.definition = d;
-    this.dirContext = dirContext;
+    this.context = context;
     this.path = path;
-    this.profile = LDAPProfile.getInstance();
     this.properties = properties;
   }
 
@@ -379,19 +369,37 @@
   /**
    * {@inheritDoc}
    */
-  public void commit() throws OperationsException {
+  public void commit() throws ConcurrentModificationException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    // Build the list of modified attributes.
     ManagedObjectDefinition<C, ?> d = getManagedObjectDefinition();
-    LDAPChangeBuilder builder = new LDAPChangeBuilder(dirContext,
-        path, d);
+    Attributes mods = new BasicAttributes();
     for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
-      // FIXME: should throw an error when there are missing mandatory
-      // properties.
       Property<?> p = properties.getProperty(pd);
       if (p.isModified()) {
-        builder.addChange(p);
+        String attrID = context.getLDAPProfile().getAttributeName(d, pd);
+        Attribute attribute = new BasicAttribute(attrID);
+        encodeProperty(attribute, pd, properties);
+        mods.put(attribute);
       }
     }
-    builder.commit();
+
+    // 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);
+      }
+    }
   }
 
 
@@ -401,9 +409,11 @@
    */
   public <M extends ConfigurationClient, N extends M>
       ManagedObject<N> createChild(
-      InstantiableRelationDefinition<M, ?> r,
-      ManagedObjectDefinition<N, ?> d, String name, PropertyProvider p)
-      throws IllegalArgumentException, OperationsException {
+      InstantiableRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
+      String name, PropertyProvider p) throws IllegalArgumentException,
+      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
+      ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(r);
 
     ManagedObjectPath childPath = path.child(r, name);
@@ -411,43 +421,46 @@
     // First make sure all the properties are valid.
     List<PropertyException> exceptions = new LinkedList<PropertyException>();
     InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        dirContext, childPath);
+        context, childPath);
     PropertySet properties = PropertySet.create(d, p, i, exceptions);
     if (!exceptions.isEmpty()) {
-      ManagedObject<N> mo = new LDAPManagedObject<N>(dirContext, d,
-          childPath, properties);
+      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
+          properties);
       throw new ManagedObjectDecodingException(mo, exceptions);
     }
 
-    try {
-      // 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);
+    ensureThisManagedObjectExists();
 
-      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();
+    // 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 : profile
-            .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.
-        dirContext.createSubcontext(dn, attributes);
+      // Create the branch's object class attribute.
+      Attribute oc = new BasicAttribute("objectClass");
+      for (String objectClass : context.getLDAPProfile()
+          .getInstantiableRelationObjectClasses(r)) {
+        oc.add(objectClass);
       }
-    } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
+      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);
@@ -460,9 +473,11 @@
    */
   public <M extends ConfigurationClient, N extends M>
       ManagedObject<N> createChild(
-      OptionalRelationDefinition<M, ?> r,
-      ManagedObjectDefinition<N, ?> d, PropertyProvider p)
-      throws IllegalArgumentException, OperationsException {
+      OptionalRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
+      PropertyProvider p) throws IllegalArgumentException,
+      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
+      ConcurrentModificationException, OperationRejectedException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(r);
 
     ManagedObjectPath childPath = path.child(r);
@@ -470,14 +485,16 @@
     // First make sure all the properties are valid.
     List<PropertyException> exceptions = new LinkedList<PropertyException>();
     InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
-        dirContext, childPath);
+        context, childPath);
     PropertySet properties = PropertySet.create(d, p, i, exceptions);
     if (!exceptions.isEmpty()) {
-      ManagedObject<N> mo = new LDAPManagedObject<N>(dirContext, d,
-          childPath, properties);
+      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
+          properties);
       throw new ManagedObjectDecodingException(mo, exceptions);
     }
 
+    ensureThisManagedObjectExists();
+
     return createManagedObject(childPath, d, properties);
   }
 
@@ -488,10 +505,13 @@
    */
   public <M extends ConfigurationClient> ManagedObject<? extends M> getChild(
       InstantiableRelationDefinition<M, ?> d, String name)
-      throws IllegalArgumentException, OperationsException {
+      throws IllegalArgumentException, DefinitionDecodingException,
+      ManagedObjectDecodingException, ManagedObjectNotFoundException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
     validateRelationDefinition(d);
-    return readEntry(dirContext, path.child(d, name), d
-        .getChildDefinition());
+    ensureThisManagedObjectExists();
+    return readEntry(context, path.child(d, name), d.getChildDefinition());
   }
 
 
@@ -500,11 +520,13 @@
    * {@inheritDoc}
    */
   public <M extends ConfigurationClient> ManagedObject<? extends M> getChild(
-      OptionalRelationDefinition<M, ?> d)
-      throws IllegalArgumentException, OperationsException {
+      OptionalRelationDefinition<M, ?> d) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
-    return readEntry(dirContext, path.child(d), d
-        .getChildDefinition());
+    ensureThisManagedObjectExists();
+    return readEntry(context, path.child(d), d.getChildDefinition());
   }
 
 
@@ -513,11 +535,13 @@
    * {@inheritDoc}
    */
   public <M extends ConfigurationClient> ManagedObject<? extends M> getChild(
-      SingletonRelationDefinition<M, ?> d)
-      throws IllegalArgumentException, OperationsException {
+      SingletonRelationDefinition<M, ?> d) throws IllegalArgumentException,
+      DefinitionDecodingException, ManagedObjectDecodingException,
+      ManagedObjectNotFoundException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
-    return readEntry(dirContext, path.child(d), d
-        .getChildDefinition());
+    ensureThisManagedObjectExists();
+    return readEntry(context, path.child(d), d.getChildDefinition());
   }
 
 
@@ -573,17 +597,14 @@
    * {@inheritDoc}
    */
   public boolean hasChild(OptionalRelationDefinition<?, ?> d)
-      throws IllegalArgumentException, OperationsException {
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
+    ensureThisManagedObjectExists();
 
     ManagedObjectPath p = path.child(d);
-    LdapName dn = LDAPNameBuilder.create(p);
-    try {
-      return entryExists(dn);
-    } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
-    }
+    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
+    return entryExists(dn);
   }
 
 
@@ -592,35 +613,25 @@
    * {@inheritDoc}
    */
   public String[] listChildren(InstantiableRelationDefinition<?, ?> d)
-      throws IllegalArgumentException, OperationsException {
+      throws IllegalArgumentException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
+    ensureThisManagedObjectExists();
 
-    LdapName dn = LDAPNameBuilder.create(path, d);
-
-    String filter = profile.getFilter(d.getChildDefinition());
-    SearchControls controls = new SearchControls();
-    controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
-
+    List<String> children = new ArrayList<String>();
+    LdapName dn = LDAPNameBuilder.create(path, d, context.getLDAPProfile());
     try {
-      NamingEnumeration<SearchResult> results;
-      results = dirContext.search(dn, filter, controls);
-
-      String rtype = profile.getInstantiableRelationChildRDNType(d);
-      List<String> names = new ArrayList<String>();
-      while (results.hasMore()) {
-        SearchResult sr = results.next();
-        Attributes attributes = sr.getAttributes();
-        Attribute cn = attributes.get(rtype);
-        if (cn != null && cn.get() != null) {
-          names.add(cn.get().toString());
-        }
+      for (LdapName child : context.getLDAPConnection().listEntries(dn)) {
+        children.add(child.getRdn(child.size() - 1).getValue().toString());
       }
-
-      return names.toArray(new String[names.size()]);
+    } catch (NameNotFoundException e) {
+      // Ignore this - it means that the base entry does not exist
+      // (which it might not if this managed object has just been
+      // created.
     } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
+      adaptNamingException(e);
     }
+    return children.toArray(new String[children.size()]);
   }
 
 
@@ -630,9 +641,11 @@
    */
   public <M extends ConfigurationClient> void removeChild(
       InstantiableRelationDefinition<M, ?> d, String name)
-      throws IllegalArgumentException, OperationsException {
+      throws IllegalArgumentException, ManagedObjectNotFoundException,
+      OperationRejectedException, ConcurrentModificationException,
+      AuthorizationException, CommunicationException {
     validateRelationDefinition(d);
-
+    ensureThisManagedObjectExists();
     ManagedObjectPath p = path.child(d, name);
     removeManagedObject(p);
   }
@@ -643,10 +656,12 @@
    * {@inheritDoc}
    */
   public <M extends ConfigurationClient> void removeChild(
-      OptionalRelationDefinition<M, ?> d)
-      throws IllegalArgumentException, OperationsException {
+      OptionalRelationDefinition<M, ?> d) throws IllegalArgumentException,
+      ManagedObjectNotFoundException, OperationRejectedException,
+      ConcurrentModificationException, AuthorizationException,
+      CommunicationException {
     validateRelationDefinition(d);
-
+    ensureThisManagedObjectExists();
     ManagedObjectPath p = path.child(d);
     removeManagedObject(p);
   }
@@ -657,9 +672,8 @@
    * {@inheritDoc}
    */
   public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
-      throws IllegalPropertyValueException,
-      PropertyIsReadOnlyException, PropertyIsMandatoryException,
-      IllegalArgumentException {
+      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
+      PropertyIsMandatoryException, IllegalArgumentException {
     properties.setPropertyValue(d, value);
   }
 
@@ -677,17 +691,39 @@
 
 
 
+  // Adapts a naming exception to an appropriate admin client
+  // exception.
+  private void adaptNamingException(NamingException ne)
+      throws CommunicationException, AuthorizationException {
+    try {
+      throw ne;
+    } catch (javax.naming.CommunicationException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.ServiceUnavailableException e) {
+      throw new CommunicationException(e);
+    } catch (javax.naming.NoPermissionException e) {
+      throw new AuthorizationException(e);
+    } catch (NamingException e) {
+      // Just treat it as a communication problem.
+      throw new CommunicationException(e);
+    }
+  }
+
+
+
   // 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 OperationsException {
-    LdapName dn = LDAPNameBuilder.create(path);
-    Attributes attributes = new BasicAttributes();
+      PropertySet properties) throws ManagedObjectAlreadyExistsException,
+      OperationRejectedException, AuthorizationException,
+      CommunicationException {
+    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
+    Attributes attributes = new BasicAttributes(true);
 
     // Create the child's object class attribute.
-    Attribute oc = new BasicAttribute("objectClass");
-    for (String objectClass : profile.getObjectClasses(d)) {
+    Attribute oc = new BasicAttribute("objectclass");
+    for (String objectClass : context.getLDAPProfile().getObjectClasses(d)) {
       oc.add(objectClass);
     }
     attributes.put(oc);
@@ -698,7 +734,7 @@
 
     // Create the remaining attributes.
     for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
-      String attrID = profile.getAttributeName(d, pd);
+      String attrID = context.getLDAPProfile().getAttributeName(d, pd);
       Attribute attribute = new BasicAttribute(attrID);
       encodeProperty(attribute, pd, properties);
       if (attribute.size() != 0) {
@@ -708,37 +744,17 @@
 
     try {
       // Create the entry.
-      dirContext.createSubcontext(dn, attributes);
+      context.getLDAPConnection().createEntry(dn, attributes);
+    } catch (NameAlreadyBoundException e) {
+      throw new ManagedObjectAlreadyExistsException();
+    } catch (OperationNotSupportedException e) {
+      // Unwilling to perform.
+      throw new OperationRejectedException(e);
     } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
+      adaptNamingException(e);
     }
 
-    return new LDAPManagedObject<N>(dirContext, d, path, properties);
-  }
-
-
-
-  // Recursively delete a subtree of entries.
-  private void destroySubtree(LdapName dn) throws NamingException {
-    // List the child entries.
-    String filter = "(objectClass=*)";
-    SearchControls controls = new SearchControls();
-    controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
-
-    NamingEnumeration<SearchResult> results;
-    results = dirContext.search(dn, filter, controls);
-
-    // Recursively delete child entries.
-    while (results.hasMore()) {
-      SearchResult sr = results.next();
-      LdapName child = new LdapName(dn.getRdns());
-      child.add(new Rdn(sr.getName()));
-      destroySubtree(child);
-    }
-
-    // Delete the specified entry.
-    dirContext.destroySubcontext(dn);
+    return new LDAPManagedObject<N>(context, d, path, properties);
   }
 
 
@@ -746,32 +762,35 @@
   // Encode a property into LDAP string values.
   private <T> void encodeProperty(Attribute attribute,
       PropertyDefinition<T> pd, PropertySet properties) {
-    for (T value : properties.getPropertyValues(pd)) {
+    Property<T> p = properties.getProperty(pd);
+    for (T value : p.getPendingValues()) {
       attribute.add(pd.encodeValue(value));
     }
   }
 
 
 
-  // Determine whether the named entry exists or not.
-  private boolean entryExists(LdapName dn) throws NamingException {
-    String filter = "(objectClass=*)";
-    SearchControls controls = new SearchControls();
-    controls.setSearchScope(SearchControls.OBJECT_SCOPE);
-
-    try {
-      NamingEnumeration<SearchResult> results = dirContext.search(dn,
-          filter, controls);
-      if (results.hasMore()) {
-        // TODO: should really verify that the relation entry has
-        // the correct object classes, etc. It definitely has the
-        // correct name, which is ok.
-        return true;
-      }
-    } catch (NameNotFoundException e) {
-      // Fall through - parent not found.
+  // Makes sure that the entry corresponding to this managed object
+  // exists.
+  private void ensureThisManagedObjectExists()
+      throws ConcurrentModificationException, CommunicationException,
+      AuthorizationException {
+    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
+    if (!entryExists(dn)) {
+      throw new ConcurrentModificationException();
     }
+  }
 
+
+
+  // Determine whether the named entry exists or not.
+  private boolean entryExists(LdapName dn) throws CommunicationException,
+      AuthorizationException {
+    try {
+      return context.getLDAPConnection().entryExists(dn);
+    } catch (NamingException e) {
+      adaptNamingException(e);
+    }
     return false;
   }
 
@@ -779,16 +798,19 @@
 
   // Remove the named managed object.
   private void removeManagedObject(ManagedObjectPath p)
-      throws OperationsException {
-    try {
-      LdapName dn = LDAPNameBuilder.create(p);
-      if (entryExists(dn)) {
-        // Delete the entry and any subordinate entries.
-        destroySubtree(dn);
+      throws CommunicationException, AuthorizationException,
+      OperationRejectedException {
+    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
+    if (entryExists(dn)) {
+      // Delete the entry and any subordinate entries.
+      try {
+        context.getLDAPConnection().deleteSubtree(dn);
+      } catch (OperationNotSupportedException e) {
+        // Unwilling to perform.
+        throw new OperationRejectedException(e);
+      } catch (NamingException e) {
+        adaptNamingException(e);
       }
-    } catch (NamingException e) {
-      OperationsExceptionFactory oef = new OperationsExceptionFactory();
-      throw oef.createException(e);
     }
   }
 
@@ -801,8 +823,8 @@
     ManagedObjectDefinition d = getManagedObjectDefinition();
     RelationDefinition tmp = d.getRelationDefinition(rd.getName());
     if (tmp != rd) {
-      throw new IllegalArgumentException("The relation "
-          + rd.getName() + " is not associated with a " + d.getName());
+      throw new IllegalArgumentException("The relation " + rd.getName()
+          + " is not associated with a " + d.getName());
     }
   }
 }

--
Gitblit v1.10.0