mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
28.31.2007 45359adc09ac1d9e48206c549e667ed6965c7cd3
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,