From e1b0862e7251d4f8ec159d704e8db0d5cf1ba212 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Mon, 02 Dec 2013 11:13:30 +0000
Subject: [PATCH] OpenDJ 3 : config framework

---
 opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java | 1065 +++++++++++++++++++++++++++-------------------------------
 1 files changed, 490 insertions(+), 575 deletions(-)

diff --git a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java
index 4adebb5..48d5ca7 100644
--- a/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java
+++ b/opendj-admin/src/main/java/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -27,8 +27,6 @@
  */
 package org.opends.server.admin.client.ldap;
 
-
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -48,7 +46,9 @@
 import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
 
-import org.opends.messages.Message;
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.admin.client.RootCfgClient;
+import org.forgerock.opendj.admin.meta.RootCfgDefn;
 import org.opends.server.admin.AbstractManagedObjectDefinition;
 import org.opends.server.admin.AggregationPropertyDefinition;
 import org.opends.server.admin.Configuration;
@@ -81,626 +81,541 @@
 import org.opends.server.admin.client.OperationRejectedException.OperationType;
 import org.opends.server.admin.client.spi.Driver;
 import org.opends.server.admin.client.spi.PropertySet;
-import org.opends.server.admin.std.client.RootCfgClient;
-import org.opends.server.admin.std.meta.RootCfgDefn;
-
-
 
 /**
  * The LDAP management context driver implementation.
  */
 final class LDAPDriver extends Driver {
 
-  /**
-   * A visitor which is used to decode property LDAP values.
-   */
-  private static final class ValueDecoder extends
-      PropertyDefinitionVisitor<Object, String> {
+    /**
+     * 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, Object value)
+                throws IllegalPropertyValueStringException {
+            String s = String.valueOf(value);
+            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);
+        }
+    }
+
+    // The LDAP connection.
+    private final LDAPConnection connection;
+
+    // The LDAP management context.
+    private final LDAPManagementContext context;
+
+    // The LDAP profile which should be used to construct LDAP
+    // requests and decode LDAP responses.
+    private final LDAPProfile profile;
 
     /**
-     * Decodes the provided property LDAP value.
+     * Creates a new LDAP driver using the specified LDAP connection and
+     * profile.
      *
-     * @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.
+     * @param context
+     *            The LDAP management context.
+     * @param connection
+     *            The LDAP connection.
+     * @param profile
+     *            The LDAP profile.
      */
-    public static <PD> PD decode(PropertyDefinition<PD> pd, Object value)
-        throws IllegalPropertyValueStringException {
-      String s = String.valueOf(value);
-      return pd.castValue(pd.accept(new ValueDecoder(), s));
+    public LDAPDriver(LDAPManagementContext context, LDAPConnection connection, LDAPProfile profile) {
+        this.context = context;
+        this.connection = connection;
+        this.profile = profile;
     }
 
-
-
-    // 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);
-      }
+    public void close() {
+        connection.unbind();
     }
 
-
-
     /**
      * {@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);
-    }
-  }
+    public <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getManagedObject(
+            ManagedObjectPath<C, S> path) throws DefinitionDecodingException, ManagedObjectDecodingException,
+            ManagedObjectNotFoundException, AuthorizationException, CommunicationException {
+        if (!managedObjectExists(path)) {
+            throw new ManagedObjectNotFoundException();
+        }
 
-
-
-  // The LDAP connection.
-  private final LDAPConnection connection;
-
-  // The LDAP management context.
-  private final LDAPManagementContext context;
-
-  // The LDAP profile which should be used to construct LDAP
-  // requests and decode LDAP responses.
-  private final LDAPProfile profile;
-
-
-
-  /**
-   * Creates a new LDAP driver using the specified LDAP connection and
-   * profile.
-   *
-   * @param context
-   *          The LDAP management context.
-   * @param connection
-   *          The LDAP connection.
-   * @param profile
-   *          The LDAP profile.
-   */
-  public LDAPDriver(LDAPManagementContext context, LDAPConnection connection,
-      LDAPProfile profile) {
-    this.context = context;
-    this.connection = connection;
-    this.profile = profile;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public void close() {
-    connection.unbind();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration>
-  ManagedObject<? extends C> getManagedObject(
-      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
-      ManagedObjectDecodingException, ManagedObjectNotFoundException,
-      AuthorizationException, CommunicationException {
-    if (!managedObjectExists(path)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    try {
-      // Read the entry associated with the managed object.
-      LdapName dn = LDAPNameBuilder.create(path, profile);
-      AbstractManagedObjectDefinition<C, S> d = path
-          .getManagedObjectDefinition();
-      ManagedObjectDefinition<? extends C, ? extends S> mod =
-        getEntryDefinition(d, dn);
-
-      ArrayList<String> attrIds = new ArrayList<String>();
-      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
-        String attrId = profile.getAttributeName(mod, pd);
-        attrIds.add(attrId);
-      }
-
-      Attributes attributes = connection.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 = profile.getAttributeName(mod, pd);
-        Attribute attribute = attributes.get(attrID);
         try {
-          decodeProperty(newProperties, path, pd, attribute);
-        } catch (PropertyException e) {
-          exceptions.add(e);
+            // Read the entry associated with the managed object.
+            LdapName dn = LDAPNameBuilder.create(path, profile);
+            AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
+            ManagedObjectDefinition<? extends C, ? extends S> mod = getEntryDefinition(d, dn);
+
+            ArrayList<String> attrIds = new ArrayList<String>();
+            for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
+                String attrId = profile.getAttributeName(mod, pd);
+                attrIds.add(attrId);
+            }
+
+            Attributes attributes = connection.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 = profile.getAttributeName(mod, pd);
+                Attribute attribute = attributes.get(attrID);
+                try {
+                    decodeProperty(newProperties, path, pd, attribute);
+                } catch (PropertyException e) {
+                    exceptions.add(e);
+                }
+            }
+
+            // If there were no decoding problems then return the object,
+            // otherwise throw an operations exception.
+            ManagedObject<? extends C> mo = createExistingManagedObject(mod, path, 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);
         }
-      }
-
-      // If there were no decoding problems then return the object,
-      // otherwise throw an operations exception.
-      ManagedObject<? extends C> mo = createExistingManagedObject(mod, path,
-          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);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @SuppressWarnings("unchecked")
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration, PD>
-  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
-      PropertyDefinition<PD> pd) throws IllegalArgumentException,
-      DefinitionDecodingException, AuthorizationException,
-      ManagedObjectNotFoundException, CommunicationException,
-      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());
     }
 
-    if (!managedObjectExists(path)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    try {
-      // Read the entry associated with the managed object.
-      LdapName dn = LDAPNameBuilder.create(path, profile);
-      ManagedObjectDefinition<? extends C, ? extends S> mod;
-      mod = getEntryDefinition(d, dn);
-
-      // 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());
-
-      String attrID = profile.getAttributeName(mod, pd);
-      Attributes attributes = connection.readEntry(dn, Collections
-          .singleton(attrID));
-      Attribute attribute = attributes.get(attrID);
-
-      // Decode the values.
-      SortedSet<PD> values = new TreeSet<PD>(pd);
-      if (attribute != null) {
-        NamingEnumeration<?> ldapValues = attribute.getAll();
-        while (ldapValues.hasMore()) {
-          Object obj = ldapValues.next();
-          if (obj != null) {
-            PD value = ValueDecoder.decode(pd, obj);
-            values.add(value);
-          }
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration, PD> SortedSet<PD> getPropertyValues(
+            ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd) throws IllegalArgumentException,
+            DefinitionDecodingException, AuthorizationException, ManagedObjectNotFoundException,
+            CommunicationException, 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());
         }
-      }
 
-      // Sanity check the returned values.
-      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-        throw new PropertyIsSingleValuedException(pd);
-      }
-
-      if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
-        throw new PropertyIsMandatoryException(pd);
-      }
-
-      if (values.isEmpty()) {
-        // Use the property's default values.
-        values.addAll(findDefaultValues(path.asSubType(mod), pd, false));
-      }
-
-      return values;
-    } catch (NameNotFoundException e) {
-      throw new ManagedObjectNotFoundException();
-    } catch (NoPermissionException e) {
-      throw new AuthorizationException(e);
-    } catch (NamingException e) {
-      throw new CommunicationException(e);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
-    return new LDAPManagedObject<RootCfgClient>(this,
-        RootCfgDefn.getInstance(), ManagedObjectPath.emptyPath(),
-        new PropertySet(), true, null);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listManagedObjects(
-      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
-      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
-      throws IllegalArgumentException, ManagedObjectNotFoundException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(parent, rd);
-
-    if (!managedObjectExists(parent)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    // Get the search base DN.
-    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
-
-    // Retrieve only those entries which are sub-types of the
-    // specified definition.
-    StringBuilder builder = new StringBuilder();
-    builder.append("(objectclass=");
-    builder.append(profile.getObjectClass(d));
-    builder.append(')');
-    String filter = builder.toString();
-
-    List<String> children = new ArrayList<String>();
-    try {
-      for (LdapName child : connection.listEntries(dn, filter)) {
-        children.add(child.getRdn(child.size() - 1).getValue().toString());
-      }
-    } 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) {
-      adaptNamingException(e);
-    }
-
-    return children.toArray(new String[children.size()]);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public <C extends ConfigurationClient, S extends Configuration>
-  String[] listManagedObjects(
-      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
-      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
-      throws IllegalArgumentException, ManagedObjectNotFoundException,
-      AuthorizationException, CommunicationException {
-    validateRelationDefinition(parent, rd);
-
-    if (!managedObjectExists(parent)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    // Get the search base DN.
-    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
-
-    // Retrieve only those entries which are sub-types of the
-    // specified definition.
-    StringBuilder builder = new StringBuilder();
-    builder.append("(objectclass=");
-    builder.append(profile.getObjectClass(d));
-    builder.append(')');
-    String filter = builder.toString();
-
-    List<String> children = new ArrayList<String>();
-    try {
-      for (LdapName child : connection.listEntries(dn, filter)) {
-        children.add(child.getRdn(child.size() - 1).getValue().toString());
-      }
-    } 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) {
-      adaptNamingException(e);
-    }
-
-    return children.toArray(new String[children.size()]);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public boolean managedObjectExists(ManagedObjectPath<?, ?> path)
-      throws ManagedObjectNotFoundException, AuthorizationException,
-      CommunicationException {
-    if (path.isEmpty()) {
-      return true;
-    }
-
-    ManagedObjectPath<?, ?> parent = path.parent();
-    LdapName dn = LDAPNameBuilder.create(parent, profile);
-    if (!entryExists(dn)) {
-      throw new ManagedObjectNotFoundException();
-    }
-
-    dn = LDAPNameBuilder.create(path, profile);
-    return entryExists(dn);
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  protected <C extends ConfigurationClient, S extends Configuration>
-  void deleteManagedObject(
-      ManagedObjectPath<C, S> path) throws OperationRejectedException,
-      AuthorizationException, CommunicationException {
-    // Delete the entry and any subordinate entries.
-    LdapName dn = LDAPNameBuilder.create(path, profile);
-    try {
-      connection.deleteSubtree(dn);
-    } catch (OperationNotSupportedException e) {
-      // Unwilling to perform.
-      AbstractManagedObjectDefinition<?, ?> d =
-        path.getManagedObjectDefinition();
-      if (e.getMessage() == null) {
-        throw new OperationRejectedException(OperationType.DELETE, d
-            .getUserFriendlyName());
-      } else {
-        Message m = Message.raw("%s", e.getMessage());
-        throw new OperationRejectedException(OperationType.DELETE, d
-            .getUserFriendlyName(), m);
-      }
-    } catch (NamingException e) {
-      adaptNamingException(e);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  protected LDAPManagementContext getManagementContext() {
-    return context;
-  }
-
-
-
-  /**
-   * Adapts a naming exception to an appropriate admin client
-   * exception.
-   *
-   * @param ne
-   *          The naming exception.
-   * @throws CommunicationException
-   *           If the naming exception mapped to a communication
-   *           exception.
-   * @throws AuthorizationException
-   *           If the naming exception mapped to an authorization
-   *           exception.
-   */
-  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);
-    }
-  }
-
-
-
-  /**
-   * Determines whether the named LDAP entry exists.
-   *
-   * @param dn
-   *          The LDAP entry name.
-   * @return Returns <code>true</code> if the named LDAP entry
-   *         exists.
-   * @throws AuthorizationException
-   *           If the server refuses to make the determination because
-   *           the client does not have the correct privileges.
-   * @throws CommunicationException
-   *           If the client cannot contact the server due to an
-   *           underlying communication problem.
-   */
-  boolean entryExists(LdapName dn) throws CommunicationException,
-      AuthorizationException {
-    try {
-      return connection.entryExists(dn);
-    } catch (NamingException e) {
-      adaptNamingException(e);
-    }
-    return false;
-  }
-
-
-
-  /**
-   * Gets the LDAP connection used for interacting with the server.
-   *
-   * @return Returns the LDAP connection used for interacting with the
-   *         server.
-   */
-  LDAPConnection getLDAPConnection() {
-    return connection;
-  }
-
-
-
-  /**
-   * Gets the LDAP profile which should be used to construct LDAP
-   * requests and decode LDAP responses.
-   *
-   * @return Returns the LDAP profile which should be used to
-   *         construct LDAP requests and decode LDAP responses.
-   */
-  LDAPProfile getLDAPProfile() {
-    return profile;
-  }
-
-
-
-  // Create a managed object which already exists on the server.
-  private <M extends ConfigurationClient, N extends Configuration>
-  ManagedObject<M> createExistingManagedObject(
-      ManagedObjectDefinition<M, N> d,
-      ManagedObjectPath<? super M, ? super N> p, PropertySet properties) {
-    RelationDefinition<?, ?> rd = p.getRelationDefinition();
-    PropertyDefinition<?> pd = null;
-    if (rd instanceof InstantiableRelationDefinition) {
-      InstantiableRelationDefinition<?, ?> ird =
-        (InstantiableRelationDefinition<?, ?>) rd;
-      pd = ird.getNamingPropertyDefinition();
-    }
-    return new LDAPManagedObject<M>(this, d, p.asSubType(d), properties, true,
-        pd);
-  }
-
-
-
-  // Create a property using the provided string values.
-  private <PD> void decodeProperty(PropertySet newProperties,
-      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd,
-      Attribute attribute) throws PropertyException,
-      NamingException {
-    PropertyException exception = null;
-
-    // Get the property's active values.
-    SortedSet<PD> activeValues = new TreeSet<PD>(pd);
-    if (attribute != null) {
-      NamingEnumeration<?> ldapValues = attribute.getAll();
-      while (ldapValues.hasMore()) {
-        Object obj = ldapValues.next();
-        if (obj != null) {
-          PD value = ValueDecoder.decode(pd, obj);
-          activeValues.add(value);
+        if (!managedObjectExists(path)) {
+            throw new ManagedObjectNotFoundException();
         }
-      }
+
+        try {
+            // Read the entry associated with the managed object.
+            LdapName dn = LDAPNameBuilder.create(path, profile);
+            ManagedObjectDefinition<? extends C, ? extends S> mod;
+            mod = getEntryDefinition(d, dn);
+
+            // 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());
+
+            String attrID = profile.getAttributeName(mod, pd);
+            Attributes attributes = connection.readEntry(dn, Collections.singleton(attrID));
+            Attribute attribute = attributes.get(attrID);
+
+            // Decode the values.
+            SortedSet<PD> values = new TreeSet<PD>(pd);
+            if (attribute != null) {
+                NamingEnumeration<?> ldapValues = attribute.getAll();
+                while (ldapValues.hasMore()) {
+                    Object obj = ldapValues.next();
+                    if (obj != null) {
+                        PD value = ValueDecoder.decode(pd, obj);
+                        values.add(value);
+                    }
+                }
+            }
+
+            // Sanity check the returned values.
+            if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+                throw new PropertyIsSingleValuedException(pd);
+            }
+
+            if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+                throw new PropertyIsMandatoryException(pd);
+            }
+
+            if (values.isEmpty()) {
+                // Use the property's default values.
+                values.addAll(findDefaultValues(path.asSubType(mod), pd, false));
+            }
+
+            return values;
+        } catch (NameNotFoundException e) {
+            throw new ManagedObjectNotFoundException();
+        } catch (NoPermissionException e) {
+            throw new AuthorizationException(e);
+        } catch (NamingException e) {
+            throw new CommunicationException(e);
+        }
     }
 
-    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
-      // This exception takes precedence over previous exceptions.
-      exception = new PropertyIsSingleValuedException(pd);
-      PD value = activeValues.first();
-      activeValues.clear();
-      activeValues.add(value);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
+        return new LDAPManagedObject<RootCfgClient>(this, RootCfgDefn.getInstance(), ManagedObjectPath.emptyPath(),
+                new PropertySet(), true, null);
     }
 
-    // Get the property's default values.
-    Collection<PD> defaultValues;
-    try {
-      defaultValues = findDefaultValues(p, pd, false);
-    } catch (DefaultBehaviorException e) {
-      defaultValues = Collections.emptySet();
-      exception = e;
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
+            ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
+            AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws IllegalArgumentException,
+            ManagedObjectNotFoundException, AuthorizationException, CommunicationException {
+        validateRelationDefinition(parent, rd);
+
+        if (!managedObjectExists(parent)) {
+            throw new ManagedObjectNotFoundException();
+        }
+
+        // Get the search base DN.
+        LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
+
+        // Retrieve only those entries which are sub-types of the
+        // specified definition.
+        StringBuilder builder = new StringBuilder();
+        builder.append("(objectclass=");
+        builder.append(profile.getObjectClass(d));
+        builder.append(')');
+        String filter = builder.toString();
+
+        List<String> children = new ArrayList<String>();
+        try {
+            for (LdapName child : connection.listEntries(dn, filter)) {
+                children.add(child.getRdn(child.size() - 1).getValue().toString());
+            }
+        } 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) {
+            adaptNamingException(e);
+        }
+
+        return children.toArray(new String[children.size()]);
     }
 
-    newProperties.addProperty(pd, defaultValues, activeValues);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
+            ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
+            AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws IllegalArgumentException,
+            ManagedObjectNotFoundException, AuthorizationException, CommunicationException {
+        validateRelationDefinition(parent, rd);
 
-    if (activeValues.isEmpty() && defaultValues.isEmpty()
-        && pd.hasOption(PropertyOption.MANDATORY)) {
-      // The active values maybe empty because of a previous
-      // exception.
-      if (exception == null) {
-        exception = new PropertyIsMandatoryException(pd);
-      }
+        if (!managedObjectExists(parent)) {
+            throw new ManagedObjectNotFoundException();
+        }
+
+        // Get the search base DN.
+        LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
+
+        // Retrieve only those entries which are sub-types of the
+        // specified definition.
+        StringBuilder builder = new StringBuilder();
+        builder.append("(objectclass=");
+        builder.append(profile.getObjectClass(d));
+        builder.append(')');
+        String filter = builder.toString();
+
+        List<String> children = new ArrayList<String>();
+        try {
+            for (LdapName child : connection.listEntries(dn, filter)) {
+                children.add(child.getRdn(child.size() - 1).getValue().toString());
+            }
+        } 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) {
+            adaptNamingException(e);
+        }
+
+        return children.toArray(new String[children.size()]);
     }
 
-    if (exception != null) {
-      throw exception;
-    }
-  }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean managedObjectExists(ManagedObjectPath<?, ?> path) throws ManagedObjectNotFoundException,
+            AuthorizationException, CommunicationException {
+        if (path.isEmpty()) {
+            return true;
+        }
 
+        ManagedObjectPath<?, ?> parent = path.parent();
+        LdapName dn = LDAPNameBuilder.create(parent, profile);
+        if (!entryExists(dn)) {
+            throw new ManagedObjectNotFoundException();
+        }
 
-
-  // Determine the type of managed object associated with the named
-  // entry.
-  private <C extends ConfigurationClient, S extends Configuration>
-  ManagedObjectDefinition<? extends C, ? extends S> getEntryDefinition(
-      AbstractManagedObjectDefinition<C, S> d, LdapName dn)
-      throws NamingException, DefinitionDecodingException {
-    Attributes attributes = connection.readEntry(dn, Collections
-        .singleton("objectclass"));
-    Attribute oc = attributes.get("objectclass");
-
-    if (oc == null) {
-      // No object classes.
-      throw new DefinitionDecodingException(d, Reason.NO_TYPE_INFORMATION);
+        dn = LDAPNameBuilder.create(path, profile);
+        return entryExists(dn);
     }
 
-    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());
-      }
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected <C extends ConfigurationClient, S extends Configuration> void deleteManagedObject(
+            ManagedObjectPath<C, S> path) throws OperationRejectedException, AuthorizationException,
+            CommunicationException {
+        // Delete the entry and any subordinate entries.
+        LdapName dn = LDAPNameBuilder.create(path, profile);
+        try {
+            connection.deleteSubtree(dn);
+        } catch (OperationNotSupportedException e) {
+            // Unwilling to perform.
+            AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
+            if (e.getMessage() == null) {
+                throw new OperationRejectedException(OperationType.DELETE, d.getUserFriendlyName());
+            } else {
+                LocalizableMessage m = LocalizableMessage.raw("%s", e.getMessage());
+                throw new OperationRejectedException(OperationType.DELETE, d.getUserFriendlyName(), m);
+            }
+        } catch (NamingException e) {
+            adaptNamingException(e);
+        }
     }
 
-    if (objectClasses.isEmpty()) {
-      // No object classes.
-      throw new DefinitionDecodingException(d, Reason.NO_TYPE_INFORMATION);
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected LDAPManagementContext getManagementContext() {
+        return context;
     }
 
-    // Resolve the appropriate sub-type based on the object classes.
-    DefinitionResolver resolver = new DefinitionResolver() {
+    /**
+     * Adapts a naming exception to an appropriate admin client exception.
+     *
+     * @param ne
+     *            The naming exception.
+     * @throws CommunicationException
+     *             If the naming exception mapped to a communication exception.
+     * @throws AuthorizationException
+     *             If the naming exception mapped to an authorization exception.
+     */
+    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);
+        }
+    }
 
-      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
-        String objectClass = profile.getObjectClass(d);
-        return objectClasses.contains(objectClass);
-      }
+    /**
+     * Determines whether the named LDAP entry exists.
+     *
+     * @param dn
+     *            The LDAP entry name.
+     * @return Returns <code>true</code> if the named LDAP entry exists.
+     * @throws AuthorizationException
+     *             If the server refuses to make the determination because the
+     *             client does not have the correct privileges.
+     * @throws CommunicationException
+     *             If the client cannot contact the server due to an underlying
+     *             communication problem.
+     */
+    boolean entryExists(LdapName dn) throws CommunicationException, AuthorizationException {
+        try {
+            return connection.entryExists(dn);
+        } catch (NamingException e) {
+            adaptNamingException(e);
+        }
+        return false;
+    }
 
-    };
+    /**
+     * Gets the LDAP connection used for interacting with the server.
+     *
+     * @return Returns the LDAP connection used for interacting with the server.
+     */
+    LDAPConnection getLDAPConnection() {
+        return connection;
+    }
 
-    return d.resolveManagedObjectDefinition(resolver);
-  }
+    /**
+     * Gets the LDAP profile which should be used to construct LDAP requests and
+     * decode LDAP responses.
+     *
+     * @return Returns the LDAP profile which should be used to construct LDAP
+     *         requests and decode LDAP responses.
+     */
+    LDAPProfile getLDAPProfile() {
+        return profile;
+    }
+
+    // Create a managed object which already exists on the server.
+    private <M extends ConfigurationClient, N extends Configuration> ManagedObject<M> createExistingManagedObject(
+            ManagedObjectDefinition<M, N> d, ManagedObjectPath<? super M, ? super N> p, PropertySet properties) {
+        RelationDefinition<?, ?> rd = p.getRelationDefinition();
+        PropertyDefinition<?> pd = null;
+        if (rd instanceof InstantiableRelationDefinition) {
+            InstantiableRelationDefinition<?, ?> ird = (InstantiableRelationDefinition<?, ?>) rd;
+            pd = ird.getNamingPropertyDefinition();
+        }
+        return new LDAPManagedObject<M>(this, d, p.asSubType(d), properties, true, pd);
+    }
+
+    // Create a property using the provided string values.
+    private <PD> void decodeProperty(PropertySet newProperties, ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd,
+            Attribute attribute) throws PropertyException, NamingException {
+        PropertyException exception = null;
+
+        // Get the property's active values.
+        SortedSet<PD> activeValues = new TreeSet<PD>(pd);
+        if (attribute != null) {
+            NamingEnumeration<?> ldapValues = attribute.getAll();
+            while (ldapValues.hasMore()) {
+                Object obj = ldapValues.next();
+                if (obj != null) {
+                    PD value = ValueDecoder.decode(pd, obj);
+                    activeValues.add(value);
+                }
+            }
+        }
+
+        if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
+            // This exception takes precedence over previous exceptions.
+            exception = new PropertyIsSingleValuedException(pd);
+            PD value = activeValues.first();
+            activeValues.clear();
+            activeValues.add(value);
+        }
+
+        // Get the property's default values.
+        Collection<PD> defaultValues;
+        try {
+            defaultValues = findDefaultValues(p, pd, false);
+        } catch (DefaultBehaviorException e) {
+            defaultValues = Collections.emptySet();
+            exception = e;
+        }
+
+        newProperties.addProperty(pd, defaultValues, activeValues);
+
+        if (activeValues.isEmpty() && defaultValues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
+            // The active values maybe empty because of a previous
+            // exception.
+            if (exception == null) {
+                exception = new PropertyIsMandatoryException(pd);
+            }
+        }
+
+        if (exception != null) {
+            throw exception;
+        }
+    }
+
+    // Determine the type of managed object associated with the named
+    // entry.
+    private <C extends ConfigurationClient, S extends Configuration> ManagedObjectDefinition<? extends C, ? extends S> getEntryDefinition(
+            AbstractManagedObjectDefinition<C, S> d, LdapName dn) throws NamingException, DefinitionDecodingException {
+        Attributes attributes = connection.readEntry(dn, Collections.singleton("objectclass"));
+        Attribute oc = attributes.get("objectclass");
+
+        if (oc == null) {
+            // No object classes.
+            throw new DefinitionDecodingException(d, 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(d, 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 = profile.getObjectClass(d);
+                return objectClasses.contains(objectClass);
+            }
+
+        };
+
+        return d.resolveManagedObjectDefinition(resolver);
+    }
 }

--
Gitblit v1.10.0