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

matthew_swift
01.06.2007 506eee12c5fe5d42d3f90662289426e6cc8aed76
opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -216,7 +216,7 @@
  /**
   * Retrieve an instantiable child managed object.
   * Retrieves an instantiable child managed object.
   *
   * @param <C>
   *          The requested type of the child managed object
@@ -262,7 +262,7 @@
  /**
   * Retrieve an optional child managed object.
   * Retrieves an optional child managed object.
   *
   * @param <C>
   *          The requested type of the child managed object
@@ -306,7 +306,7 @@
  /**
   * Retrieve a singleton child managed object.
   * Retrieves a singleton child managed object.
   *
   * @param <C>
   *          The requested type of the child managed object
@@ -362,7 +362,7 @@
  /**
   * Get the definition associated with this managed object.
   * Gets the definition associated with this managed object.
   *
   * @return Returns the definition associated with this managed
   *         object.
@@ -373,7 +373,7 @@
  /**
   * Get the path of this managed object.
   * Gets the path of this managed object.
   *
   * @return Returns the path of this managed object.
   */
@@ -382,30 +382,31 @@
  /**
   * Get the default values of the specified property.
   * Gets a mutable copy of the set of default values for the
   * specified property.
   *
   * @param <P>
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's default values, or an empty set
   *         if there are no default values defined.
   * @return Returns the property's default values, or an empty set if
   *         there are no default values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd)
  <PD> SortedSet<PD> getPropertyDefaultValues(PropertyDefinition<PD> pd)
      throws IllegalArgumentException;
  /**
   * Get the effective value of the specified property.
   * Gets the effective value of the specified property.
   * <p>
   * See the class description for more information about how the
   * effective property value is derived.
   *
   * @param <P>
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param pd
   *          The property to be retrieved.
@@ -416,18 +417,19 @@
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  <P> P getPropertyValue(PropertyDefinition<P> pd)
  <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
      throws IllegalArgumentException;
  /**
   * Get the effective values of the specified property.
   * Gets a mutable copy of the set of effective values for the
   * specified property.
   * <p>
   * See the class description for more information about how the
   * effective property values are derived.
   *
   * @param <P>
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param pd
   *          The property to be retrieved.
@@ -437,7 +439,7 @@
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd)
  <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
      throws IllegalArgumentException;
@@ -653,12 +655,12 @@
  /**
   * Set a new pending value for the specified property.
   * Sets a new pending value for the specified property.
   * <p>
   * See the class description for more information regarding pending
   * values.
   *
   * @param <P>
   * @param <PD>
   *          The type of the property to be modified.
   * @param pd
   *          The property to be modified.
@@ -678,19 +680,19 @@
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  <P> void setPropertyValue(PropertyDefinition<P> pd, P value)
  <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException;
  /**
   * Set a new pending values for the specified property.
   * Sets a new pending values for the specified property.
   * <p>
   * See the class description for more information regarding pending
   * values.
   *
   * @param <P>
   * @param <PD>
   *          The type of the property to be modified.
   * @param pd
   *          The property to be modified.
@@ -714,7 +716,7 @@
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  <P> void setPropertyValues(PropertyDefinition<P> pd, Collection<P> values)
  <PD> void setPropertyValues(PropertyDefinition<PD> pd, Collection<PD> values)
      throws IllegalPropertyValueException, PropertyIsSingleValuedException,
      PropertyIsReadOnlyException, PropertyIsMandatoryException,
      IllegalArgumentException;
opendj-sdk/opends/src/server/org/opends/server/admin/client/ManagementContext.java
@@ -34,7 +34,7 @@
/**
 * Management connection context.
 * Client management connection context.
 */
public abstract class ManagementContext {
@@ -48,21 +48,9 @@
  /**
   * Get the root configuration managed object associated with this
   * Gets the root configuration client associated with this
   * management context.
   *
   * @return Returns the root configuration managed object associated
   *         with this management context.
   */
  public abstract ManagedObject<RootCfgClient>
      getRootConfigurationManagedObject();
  /**
   * Get the root configuration client associated with this management
   * context.
   *
   * @return Returns the root configuration client associated with
   *         this management context.
   */
@@ -70,4 +58,16 @@
    return getRootConfigurationManagedObject().getConfiguration();
  }
  /**
   * Gets the root configuration managed object associated with this
   * management context.
   *
   * @return Returns the root configuration managed object associated
   *         with this management context.
   */
  public abstract
  ManagedObject<RootCfgClient> getRootConfigurationManagedObject();
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
New file
@@ -0,0 +1,595 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.ldap;
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 java.util.TreeSet;
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.ldap.LdapName;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.DefinitionResolver;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
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.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.client.spi.Driver;
import org.opends.server.admin.client.spi.PropertySet;
/**
 * The LDAP management context driver implementation.
 */
final class LDAPDriver extends Driver {
  // The LDAP connection.
  private final LDAPConnection connection;
  // 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 connection
   *          The LDAP connection.
   * @param profile
   *          The LDAP profile.
   */
  public LDAPDriver(LDAPConnection connection, LDAPProfile profile) {
    this.connection = connection;
    this.profile = profile;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
      String name) throws IllegalArgumentException,
      ManagedObjectNotFoundException, OperationRejectedException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(parent, rd);
    if (!entryExists(parent)) {
      throw new ManagedObjectNotFoundException();
    }
    return removeManagedObject(parent.child(rd, name));
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(parent, rd);
    if (!entryExists(parent)) {
      throw new ManagedObjectNotFoundException();
    }
    return removeManagedObject(parent.child(rd));
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getManagedObject(
      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    if (!entryExists(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);
        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, path, 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 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}
   */
  @Override
  public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
      PropertyException {
    if (!entryExists(path)) {
      throw new ManagedObjectNotFoundException();
    }
    try {
      // Read the entry associated with the managed object.
      LdapName dn = LDAPNameBuilder.create(path, profile);
      AbstractManagedObjectDefinition<?, ?> d = path
          .getManagedObjectDefinition();
      ManagedObjectDefinition<?, ?> mod = getEntryDefinition(d, dn);
      String attrID = profile.getAttributeName(mod, pd);
      Attributes attributes = connection.readEntry(dn, Collections
          .singleton(attrID));
      Attribute attribute = attributes.get(attrID);
      SortedSet<PD> values = new TreeSet<PD>(pd);
      if (attribute == null || attribute.size() == 0) {
        // Use the property's default values.
        values.addAll(findDefaultValues(path, pd, false));
      } else {
        // Decode the values.
        NamingEnumeration<?> ldapValues = attribute.getAll();
        while (ldapValues.hasMore()) {
          Object obj = ldapValues.next();
          if (obj != null) {
            PD value = pd.decodeValue(obj.toString());
            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);
      }
      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 <C extends ConfigurationClient, S extends Configuration>
  boolean hasManagedObject(
      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(parent, rd);
    if (!entryExists(parent)) {
      throw new ManagedObjectNotFoundException();
    }
    return entryExists(parent.child(rd));
  }
  /**
   * {@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 (!entryExists(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()]);
  }
  /**
   * 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, List<String> values)
      throws PropertyException {
    PropertyException exception = null;
    // Get the property's active values.
    Collection<PD> activeValues = new ArrayList<PD>(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);
      PD 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<PD> defaultValues;
    try {
      defaultValues = findDefaultValues(p, pd, false);
    } catch (DefaultBehaviorException e) {
      defaultValues = Collections.emptySet();
      exception = e;
    }
    newProperties.addProperty(pd, defaultValues, activeValues);
    if (exception != null) {
      throw exception;
    }
  }
  // Determines whether the LDAP entry associated with the managed
  // object path exists.
  private boolean entryExists(ManagedObjectPath<?, ?> path)
      throws CommunicationException, AuthorizationException {
    if (path.isEmpty()) {
      return true;
    }
    LdapName dn = LDAPNameBuilder.create(path, profile);
    return entryExists(dn);
  }
  // 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(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 = profile.getObjectClass(d);
        return objectClasses.contains(objectClass);
      }
    };
    return d.resolveManagedObjectDefinition(resolver);
  }
  // Remove the named managed object.
  private boolean removeManagedObject(ManagedObjectPath<?, ?> path)
      throws CommunicationException, AuthorizationException,
      OperationRejectedException, ManagedObjectNotFoundException {
    if (!entryExists(path)) {
      return false;
    }
    // Delete the entry and any subordinate entries.
    LdapName dn = LDAPNameBuilder.create(path, profile);
    try {
      connection.deleteSubtree(dn);
    } catch (OperationNotSupportedException e) {
      // Unwilling to perform.
      throw new OperationRejectedException(e);
    } catch (NamingException e) {
      adaptNamingException(e);
    }
    return true;
  }
  // Validate that a relation definition belongs to this managed
  // object.
  private void validateRelationDefinition(ManagedObjectPath<?, ?> path,
      RelationDefinition<?, ?> rd) throws IllegalArgumentException {
    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
    if (tmp != rd) {
      throw new IllegalArgumentException("The relation " + rd.getName()
          + " is not associated with a " + d.getName());
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -29,18 +29,7 @@
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;
@@ -51,46 +40,24 @@
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.Configuration;
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.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.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyNotFoundException;
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.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.IllegalManagedObjectNameException;
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;
import org.opends.server.admin.client.spi.AbstractManagedObject;
import org.opends.server.admin.client.spi.Driver;
import org.opends.server.admin.client.spi.Property;
import org.opends.server.admin.client.spi.PropertySet;
import org.opends.server.admin.std.client.RootCfgClient;
import org.opends.server.admin.std.meta.RootCfgDefn;
@@ -103,384 +70,55 @@
 *          The type of client configuration represented by the client
 *          managed object.
 */
final class LDAPManagedObject<T extends ConfigurationClient> implements
    ManagedObject<T> {
final class LDAPManagedObject<T extends ConfigurationClient> extends
    AbstractManagedObject<T> {
  /**
   * A default behavior visitor used for retrieving the default values
   * of a property.
   * Constructs a root LDAP managed object associated with the
   * provided LDAP driver.
   *
   * @param <T>
   *          The type of the property.
   */
  private static class DefaultValueFinder<T> implements
      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
    /**
     * 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.
     * @param isCreate
     *          Indicates whether the managed object has been created
     *          yet.
     * @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, boolean isCreate)
        throws DefaultBehaviorException {
      DefaultValueFinder<T> v = new DefaultValueFinder<T>(context, p, isCreate);
      return v.find(p, pd);
    }
    // The LDAP management context.
    private final LDAPManagementContext context;
    // Any exception that occurred whilst retrieving inherited default
    // values.
    private DefaultBehaviorException exception = null;
    // Indicates whether the managed object has been created yet.
    private final boolean isCreate;
    // The path of the managed object containing the first property.
    private final ManagedObjectPath<?, ?> firstPath;
    // The path of the managed object containing the next property.
    private ManagedObjectPath<?, ?> nextPath = null;
    // The next property whose default values were required.
    private PropertyDefinition<T> nextProperty = null;
    // Private constructor.
    private DefaultValueFinder(LDAPManagementContext context,
        ManagedObjectPath<?, ?> p, boolean isCreate) {
      this.context = context;
      this.firstPath = p;
      this.isCreate = isCreate;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
        Void p) {
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        Void p) {
      Collection<String> stringValues = d.getDefaultValues();
      List<T> values = new ArrayList<T>(stringValues.size());
      for (String stringValue : stringValues) {
        try {
          values.add(nextProperty.decodeValue(stringValue));
        } catch (IllegalPropertyValueStringException e) {
          exception = new DefaultBehaviorException(nextProperty, e);
          break;
        }
      }
      return values;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        Void p) {
      return Collections.emptySet();
    }
    // Find the default values for the next path/property.
    private Collection<T> find(ManagedObjectPath<?, ?> p,
        PropertyDefinition<T> pd)
        throws DefaultBehaviorException {
      this.nextPath = p;
      this.nextProperty = pd;
      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
          this, null);
      if (exception != null) {
        throw exception;
      }
      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
        throw new DefaultBehaviorException(pd,
            new PropertyIsSingleValuedException(pd));
      }
      return values;
    }
    // Get an inherited property value.
    @SuppressWarnings("unchecked")
    private Collection<T> getInheritedProperty(ManagedObjectPath target,
        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
        throws DefaultBehaviorException {
      // First check that the requested type of managed object
      // corresponds to the path.
      AbstractManagedObjectDefinition<?, ?> supr = target
          .getManagedObjectDefinition();
      if (!supr.isParentOf(d)) {
        throw new DefaultBehaviorException(nextProperty,
            new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION));
      }
      // Save the current property in case of recursion.
      PropertyDefinition<T> pd1 = nextProperty;
      try {
        // If the path relates to the current managed object and the
        // managed object is in the process of being created it won't
        // exist, so we should just use the default values of the
        // referenced property.
        if (isCreate && firstPath.equals(target)) {
          PropertyDefinition<T> pd2;
          try {
            // FIXME: we use the definition taken from the default
            // behavior here when we should really use the exact
            // definition of the component being created.
            PropertyDefinition<?> pdTmp = d.getPropertyDefinition(propertyName);
            pd2 = pd1.getClass().cast(pdTmp);
          } catch (IllegalArgumentException e) {
            throw new PropertyNotFoundException(propertyName);
          } catch (ClassCastException e) {
            // FIXME: would be nice to throw a better exception here.
            throw new PropertyNotFoundException(propertyName);
          }
          // Recursively retrieve this property's default values.
          Collection<T> tmp = find(target, pd2);
          Collection<T> values = new ArrayList<T>(tmp.size());
          for (T value : tmp) {
            pd1.validateValue(value);
            values.add(value);
          }
          return values;
        } else {
          // Get the actual managed object definition.
          LdapName dn = LDAPNameBuilder
              .create(target, context.getLDAPProfile());
          ManagedObjectDefinition<?, ?> mod =
            getEntryDefinition(context, d, dn);
          PropertyDefinition<T> pd2;
          try {
            PropertyDefinition<?> pdTmp = mod
                .getPropertyDefinition(propertyName);
            pd2 = pd1.getClass().cast(pdTmp);
          } catch (IllegalArgumentException e) {
            throw new PropertyNotFoundException(propertyName);
          } catch (ClassCastException e) {
            // FIXME: would be nice to throw a better exception here.
            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<T> tmp = find(target, pd2);
            Collection<T> values = new ArrayList<T>(tmp.size());
            for (T value : tmp) {
              pd1.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(pd1.decodeValue(value.toString()));
              }
            }
            return values;
          }
        }
      } catch (DefaultBehaviorException e) {
        // Wrap any errors due to recursion.
        throw new DefaultBehaviorException(pd1, e);
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (PropertyNotFoundException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (IllegalPropertyValueException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (IllegalPropertyValueStringException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (NameNotFoundException e) {
        throw new DefaultBehaviorException(pd1,
            new ManagedObjectNotFoundException());
      } catch (NoPermissionException e) {
        throw new DefaultBehaviorException(pd1, new AuthorizationException(e));
      } catch (NamingException e) {
        throw new DefaultBehaviorException(pd1, new CommunicationException(e));
      }
    }
  };
  /**
   * Construct a root LDAP managed object associated with the provided
   * LDAP context.
   *
   * @param context
   *          The LDAP management context.
   * @param driver
   *          The LDAP management driver.
   * @return Returns a root LDAP managed object associated with the
   *         provided LDAP context.
   *         provided LDAP driver.
   */
  static ManagedObject<RootCfgClient> getRootManagedObject(
      LDAPManagementContext context) {
    return new LDAPManagedObject<RootCfgClient>(context, RootCfgDefn
      LDAPDriver driver) {
    return new LDAPManagedObject<RootCfgClient>(driver, RootCfgDefn
        .getInstance(), ManagedObjectPath.emptyPath(), new PropertySet(), true,
        null);
  }
  // Determine the type of managed object associated with the named
  // entry.
  private static <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectDefinition<? extends M, ? extends N> getEntryDefinition(
      final LDAPManagementContext context,
      AbstractManagedObjectDefinition<M, N> 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 = context.getLDAPProfile().getObjectClass(d);
        return objectClasses.contains(objectClass);
      }
    };
    return d.resolveManagedObjectDefinition(resolver);
  }
  // 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<T, ? extends Configuration> definition;
  // 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;
  // Optional naming property definition.
  private final PropertyDefinition<?> namingPropertyDefinition;
  // The path associated with this managed object.
  private ManagedObjectPath<T, ? extends Configuration> path;
  // The managed object's properties.
  private final PropertySet properties;
  // The LDAP management driver associated with this managed object.
  private final LDAPDriver driver;
  // Create an new LDAP managed object with the provided JNDI context.
  private LDAPManagedObject(LDAPManagementContext context,
  /**
   * Creates a new LDAP managed object instance.
   *
   * @param driver
   *          The underlying LDAP management driver.
   * @param d
   *          The managed object's definition.
   * @param path
   *          The managed object's path.
   * @param properties
   *          The managed object's properties.
   * @param existsOnServer
   *          Indicates whether or not the managed object already
   *          exists.
   * @param namingPropertyDefinition
   *          The managed object's naming property definition if there
   *          is one.
   */
  LDAPManagedObject(LDAPDriver driver,
      ManagedObjectDefinition<T, ? extends Configuration> d,
      ManagedObjectPath<T, ? extends Configuration> path,
      PropertySet properties, boolean existsOnServer,
      PropertyDefinition<?> namingPropertyDefinition) {
    this.definition = d;
    this.context = context;
    this.path = path;
    this.properties = properties;
    this.existsOnServer = existsOnServer;
    this.namingPropertyDefinition = namingPropertyDefinition;
    super(d, path, properties, existsOnServer, namingPropertyDefinition);
    this.driver = driver;
  }
@@ -488,411 +126,16 @@
  /**
   * {@inheritDoc}
   */
  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 (pd.hasOption(PropertyOption.MANDATORY)
          && p.getEffectiveValues().isEmpty()) {
        exceptions.add(new PropertyIsMandatoryException(pd));
      }
    }
    if (!exceptions.isEmpty()) {
      throw new MissingMandatoryPropertiesException(exceptions);
    }
    // Commit the managed object.
    if (existsOnServer) {
      commitExistingManagedObject();
    } else {
      commitNewManagedObject();
    }
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration, CC extends C>
  ManagedObject<CC> createChild(InstantiableRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d, String name,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalManagedObjectNameException, IllegalArgumentException {
    validateRelationDefinition(r);
    // Empty names are not allowed.
    if (name.trim().length() == 0) {
      throw new IllegalManagedObjectNameException(name);
    }
    // If the relation uses a naming property definition then it must
    // be a valid value.
    PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
    if (pd != null) {
      try {
        pd.decodeValue(name);
      } catch (IllegalPropertyValueStringException e) {
        throw new IllegalManagedObjectNameException(name, pd);
      }
    }
    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d, name);
    return createNewManagedObject(d, childPath, pd, name, exceptions);
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration, CC extends C>
  ManagedObject<CC> createChild(OptionalRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException {
    validateRelationDefinition(r);
    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
    return createNewManagedObject(d, childPath, null, null, exceptions);
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(InstantiableRelationDefinition<C, S> r,
      String name) throws IllegalArgumentException, DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    return readManagedObject(r.getChildDefinition(), path.child(r, name));
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(OptionalRelationDefinition<C, S> r)
      throws IllegalArgumentException, DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    return readManagedObject(r.getChildDefinition(), path.child(r));
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      SingletonRelationDefinition<C, S> r) throws IllegalArgumentException,
      DefinitionDecodingException, ManagedObjectDecodingException,
      ManagedObjectNotFoundException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    return readManagedObject(r.getChildDefinition(), path.child(r));
  }
  /**
   * {@inheritDoc}
   */
  public T getConfiguration() {
    return definition.createClientConfiguration(this);
  }
  /**
   * {@inheritDoc}
   */
  public ManagedObjectDefinition<T, ? extends Configuration>
  getManagedObjectDefinition() {
    return definition;
  }
  /**
   * {@inheritDoc}
   */
  public ManagedObjectPath<T, ? extends Configuration> getManagedObjectPath() {
    return path;
  }
  /**
   * {@inheritDoc}
   */
  public <P> SortedSet<P> getPropertyDefaultValues(PropertyDefinition<P> pd)
      throws IllegalArgumentException {
    Property<P> p = properties.getProperty(pd);
    return p.getDefaultValues();
  }
  /**
   * {@inheritDoc}
   */
  public <P> P getPropertyValue(PropertyDefinition<P> pd)
      throws IllegalArgumentException {
    return properties.getPropertyValue(pd);
  }
  /**
   * {@inheritDoc}
   */
  public <P> SortedSet<P> getPropertyValues(PropertyDefinition<P> pd)
      throws IllegalArgumentException {
    return properties.getPropertyValues(pd);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isPropertyPresent(PropertyDefinition<?> pd)
      throws IllegalArgumentException {
    Property<?> p = properties.getProperty(pd);
    return !p.isEmpty();
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  boolean hasChild(OptionalRelationDefinition<C, S> r)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    ManagedObjectPath<C, S> p = path.child(r);
    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
    return entryExists(dn);
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(InstantiableRelationDefinition<C, S> r)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    return listChildren(r, r.getChildDefinition());
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(InstantiableRelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    // Get the search base DN.
    LdapName dn = LDAPNameBuilder.create(path, r, context.getLDAPProfile());
    // Retrieve only those entries which are sub-types of the
    // specified definition.
    StringBuilder builder = new StringBuilder();
    builder.append("(objectclass=");
    builder.append(context.getLDAPProfile().getObjectClass(d));
    builder.append(')');
    String filter = builder.toString();
    List<String> children = new ArrayList<String>();
    try {
      for (LdapName child :
          context.getLDAPConnection().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}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  void removeChild(InstantiableRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    ManagedObjectPath<C, S> p = path.child(r, name);
    removeManagedObject(p);
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
  void removeChild(OptionalRelationDefinition<C, S> r)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
        OperationRejectedException, ConcurrentModificationException,
        AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    ManagedObjectPath<C, S> p = path.child(r);
    removeManagedObject(p);
  }
  /**
   * {@inheritDoc}
   */
  public <P> void setPropertyValue(PropertyDefinition<P> pd, P value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (value == null) {
      setPropertyValues(pd, Collections.<P> emptySet());
    } else {
      setPropertyValues(pd, Collections.singleton(value));
    }
  }
  /**
   * {@inheritDoc}
   */
  public <P> void setPropertyValues(PropertyDefinition<P> pd,
      Collection<P> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (pd.hasOption(PropertyOption.MONITORING)) {
      throw new PropertyIsReadOnlyException(pd);
    }
    if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(pd);
    }
    properties.setPropertyValues(pd, values);
    // If this is a naming property then update the name.
    if (pd.equals(namingPropertyDefinition)) {
      // The property must be single-valued and mandatory.
      String newName = pd.encodeValue(values.iterator().next());
      path = path.rename(newName);
    }
  }
  // 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);
    }
  }
  // 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,
  @Override
  protected void addNewManagedObject() throws AuthorizationException,
      CommunicationException, OperationRejectedException,
      ConcurrentModificationException, ManagedObjectAlreadyExistsException {
    // First make sure that the parent managed object still exists.
    ManagedObjectPath<?, ?> path = getManagedObjectPath();
    ManagedObjectPath<?, ?> parent = path.parent();
    if (!parent.isEmpty()) {
      LdapName dn = LDAPNameBuilder.create(parent, context.getLDAPProfile());
      if (!entryExists(dn)) {
      LdapName dn = LDAPNameBuilder.create(parent, driver.getLDAPProfile());
      if (!driver.entryExists(dn)) {
        throw new ConcurrentModificationException();
      }
    }
@@ -908,14 +151,14 @@
      // 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)) {
          .create(parent, ir, driver.getLDAPProfile());
      if (!driver.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()
        for (String objectClass : driver.getLDAPProfile()
            .getInstantiableRelationObjectClasses(ir)) {
          oc.add(objectClass);
        }
@@ -927,39 +170,41 @@
        // Create the entry.
        try {
          context.getLDAPConnection().createEntry(dn, attributes);
          driver.getLDAPConnection().createEntry(dn, attributes);
        } catch (OperationNotSupportedException e) {
          // Unwilling to perform.
          throw new OperationRejectedException(e);
        } catch (NamingException e) {
          adaptNamingException(e);
          driver.adaptNamingException(e);
        }
      }
    }
    // Now add the entry representing this new managed object.
    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
    LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
    Attributes attributes = new BasicAttributes(true);
    // Create the object class attribute.
    Attribute oc = new BasicAttribute("objectclass");
    for (String objectClass : context.getLDAPProfile().getObjectClasses(
    ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
    for (String objectClass : driver.getLDAPProfile().getObjectClasses(
        definition)) {
      oc.add(objectClass);
    }
    attributes.put(oc);
    // Create the naming attribute if there is not naming property.
    if (namingPropertyDefinition == null) {
    PropertyDefinition<?> npd = getNamingPropertyDefinition();
    if (npd == null) {
      Rdn rdn = dn.getRdn(dn.size() - 1);
      attributes.put(rdn.getType(), rdn.getValue().toString());
    }
    // Create the remaining attributes.
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      String attrID = context.getLDAPProfile().getAttributeName(definition, pd);
      String attrID = driver.getLDAPProfile().getAttributeName(definition, pd);
      Attribute attribute = new BasicAttribute(attrID);
      encodeProperty(attribute, pd, properties);
      encodeProperty(attribute, pd);
      if (attribute.size() != 0) {
        attributes.put(attribute);
      }
@@ -967,287 +212,100 @@
    try {
      // Create the entry.
      context.getLDAPConnection().createEntry(dn, attributes);
      driver.getLDAPConnection().createEntry(dn, attributes);
    } catch (NameAlreadyBoundException e) {
      throw new ManagedObjectAlreadyExistsException();
    } catch (OperationNotSupportedException e) {
      // Unwilling to perform.
      throw new OperationRejectedException(e);
    } catch (NamingException e) {
      adaptNamingException(e);
      driver.adaptNamingException(e);
    }
    // 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, 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>(context, d, p.asSubType(d), properties,
        true, pd);
  /**
   * {@inheritDoc}
   */
  @Override
  protected Driver getDriver() {
    return driver;
  }
  // Creates a new managed object with no active values, just default
  // values.
  private <M extends ConfigurationClient, P>
      ManagedObject<M> createNewManagedObject(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p,
      PropertyDefinition<P> namingPropertyDefinition, String name,
      Collection<DefaultBehaviorException> exceptions) {
    PropertySet childProperties = new PropertySet();
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
  /**
   * {@inheritDoc}
   */
  @Override
  protected void modifyExistingManagedObject()
      throws ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException {
    // Build the list of modified attributes.
    Attributes mods = new BasicAttributes();
    ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      Property<?> p = getProperty(pd);
      if (p.isModified()) {
        String attrID = driver.getLDAPProfile().getAttributeName(definition,
            pd);
        Attribute attribute = new BasicAttribute(attrID);
        encodeProperty(attribute, pd);
        mods.put(attribute);
      }
    }
    // Perform the LDAP modification if something has changed.
    if (mods.size() > 0) {
      try {
        createProperty(childProperties, p, pd);
      } catch (DefaultBehaviorException e) {
        // Add the exception if requested.
        if (exceptions != null) {
          exceptions.add(e);
        }
        ManagedObjectPath<?, ?> path = getManagedObjectPath();
        LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
        driver.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);
      }
    }
    // Set the naming property if there is one.
    if (namingPropertyDefinition != null) {
      P value = namingPropertyDefinition.decodeValue(name);
      childProperties.setPropertyValue(namingPropertyDefinition, value);
    }
    return new LDAPManagedObject<M>(context, d, p, childProperties, false,
        namingPropertyDefinition);
  }
  // Create an empty property.
  private <P> void createProperty(PropertySet properties,
      ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd)
      throws DefaultBehaviorException {
    try {
      Collection<P> defaultValues = DefaultValueFinder.getDefaultValues(
          context, p, pd, true);
      properties.addProperty(pd, defaultValues, Collections.<P> emptySet());
    } catch (DefaultBehaviorException e) {
      // Make sure that we have still created the property.
      properties.addProperty(pd, Collections.<P> emptySet(), Collections
          .<P> emptySet());
      throw e;
    }
  }
  // Create a property using the provided string values.
  private <P> void decodeProperty(PropertySet newProperties,
      ManagedObjectPath<?, ?> p, PropertyDefinition<P> pd, List<String> values)
      throws PropertyException {
    PropertyException exception = null;
    // Get the property's active values.
    Collection<P> activeValues = new ArrayList<P>(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);
      P 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<P> defaultValues;
    try {
      defaultValues = DefaultValueFinder.getDefaultValues(context, p, pd,
          false);
    } catch (DefaultBehaviorException e) {
      defaultValues = Collections.emptySet();
      exception = e;
    }
    newProperties.addProperty(pd, defaultValues, activeValues);
    if (exception != null) {
      throw exception;
    }
  /**
   * {@inheritDoc}
   */
  @Override
  protected <M extends ConfigurationClient> ManagedObject<M> newInstance(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
      PropertySet properties, boolean existsOnServer,
      PropertyDefinition<?> namingPropertyDefinition) {
    return new LDAPManagedObject<M>(driver, d, path, properties,
        existsOnServer, namingPropertyDefinition);
  }
  // Encode a property into LDAP string values.
  private <P> void encodeProperty(Attribute attribute,
      PropertyDefinition<P> pd, PropertySet properties) {
    Property<P> p = properties.getProperty(pd);
  private <PD> void encodeProperty(Attribute attribute,
      PropertyDefinition<PD> pd) {
    Property<PD> p = getProperty(pd);
    if (pd.hasOption(PropertyOption.MANDATORY)) {
      // For mandatory properties we fall-back to the default values
      // if defined which can sometimes be the case e.g when a
      // mandatory property is overridden.
      for (P value : p.getEffectiveValues()) {
      for (PD value : p.getEffectiveValues()) {
        attribute.add(pd.encodeValue(value));
      }
    } else {
      for (P value : p.getPendingValues()) {
      for (PD value : p.getPendingValues()) {
        attribute.add(pd.encodeValue(value));
      }
    }
  }
  // Makes sure that the entry corresponding to this managed object
  // exists.
  private void ensureThisManagedObjectExists()
      throws ConcurrentModificationException, CommunicationException,
      AuthorizationException {
    if (!path.isEmpty()) {
      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;
  }
  // Read the entry identified by the path and which is a sub-type of
  // the specified definition.
  private <C extends ConfigurationClient, S extends Configuration>
      ManagedObject<? extends C> readManagedObject(
      AbstractManagedObjectDefinition<C, S> d, ManagedObjectPath<C, S> 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 C, ? extends S> 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 C> 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,
      OperationRejectedException, ManagedObjectNotFoundException {
    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);
      }
    } else {
      throw new ManagedObjectNotFoundException();
    }
  }
  // Validate that a relation definition belongs to this managed
  // object.
  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
      throws IllegalArgumentException {
    ManagedObjectDefinition<T, ?> 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());
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java
@@ -55,20 +55,15 @@
    return new LDAPManagementContext(connection, LDAPProfile.getInstance());
  }
  // The LDAP connection.
  private final LDAPConnection connection;
  // The LDAP profile which should be used to construct LDAP requests
  // and decode LDAP responses.
  private final LDAPProfile profile;
  // The LDAP management context driver.
  private final LDAPDriver driver;
  // Private constructor.
  private LDAPManagementContext(LDAPConnection connection,
      LDAPProfile profile) {
    this.connection = connection;
    this.profile = profile;
    this.driver = new LDAPDriver(connection, profile);
  }
@@ -77,31 +72,6 @@
   * {@inheritDoc}
   */
  public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
    return LDAPManagedObject.getRootManagedObject(this);
  }
  /**
   * 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;
    return LDAPManagedObject.getRootManagedObject(driver);
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/package-info.java
@@ -30,12 +30,8 @@
/**
 * Common client-side administration classes.
 * <p>
 * This package contains classes which client applications and client-side
 * driver implementations are expected to use.
 * <p>
 * In addition, there are also two example client
 * applications, <code>ExampleClient</code> and
 * <code>ExampleIntrospection</code>.
 * This package contains classes which client applications are
 * expected to use.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.PRIVATE)
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
New file
@@ -0,0 +1,696 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.spi;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.IllegalPropertyValueException;
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.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
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.IllegalManagedObjectNameException;
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;
/**
 * An abstract managed object implementation.
 *
 * @param <T>
 *          The type of client configuration represented by the client
 *          managed object.
 */
public abstract class AbstractManagedObject<T extends ConfigurationClient>
    implements ManagedObject<T> {
  // The managed object definition associated with this managed
  // object.
  private final ManagedObjectDefinition<T, ? extends Configuration> definition;
  // 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;
  // Optional naming property definition.
  private final PropertyDefinition<?> namingPropertyDefinition;
  // The path associated with this managed object.
  private ManagedObjectPath<T, ? extends Configuration> path;
  // The managed object's properties.
  private final PropertySet properties;
  /**
   * Creates a new abstract managed object.
   *
   * @param d
   *          The managed object's definition.
   * @param path
   *          The managed object's path.
   * @param properties
   *          The managed object's properties.
   * @param existsOnServer
   *          Indicates whether or not the managed object exists on
   *          the server (false means the managed object is new and
   *          has not been committed).
   * @param namingPropertyDefinition
   *          Optional naming property definition.
   */
  protected AbstractManagedObject(
      ManagedObjectDefinition<T, ? extends Configuration> d,
      ManagedObjectPath<T, ? extends Configuration> path,
      PropertySet properties, boolean existsOnServer,
      PropertyDefinition<?> namingPropertyDefinition) {
    this.definition = d;
    this.path = path;
    this.properties = properties;
    this.existsOnServer = existsOnServer;
    this.namingPropertyDefinition = namingPropertyDefinition;
  }
  /**
   * {@inheritDoc}
   */
  public final void commit() throws ManagedObjectAlreadyExistsException,
      MissingMandatoryPropertiesException, ConcurrentModificationException,
      OperationRejectedException, AuthorizationException,
      CommunicationException {
    // First make sure all mandatory properties are defined.
    List<PropertyIsMandatoryException> exceptions =
      new LinkedList<PropertyIsMandatoryException>();
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      Property<?> p = getProperty(pd);
      if (pd.hasOption(PropertyOption.MANDATORY)
          && p.getEffectiveValues().isEmpty()) {
        exceptions.add(new PropertyIsMandatoryException(pd));
      }
    }
    if (!exceptions.isEmpty()) {
      throw new MissingMandatoryPropertiesException(exceptions);
    }
    // Commit the managed object.
    if (existsOnServer) {
      modifyExistingManagedObject();
    } else {
      addNewManagedObject();
      existsOnServer = true;
    }
    // Make all pending property values active.
    properties.commit();
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration,
                CC extends C>
  ManagedObject<CC> createChild(
      InstantiableRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d, String name,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalManagedObjectNameException, IllegalArgumentException {
    validateRelationDefinition(r);
    // Empty names are not allowed.
    if (name.trim().length() == 0) {
      throw new IllegalManagedObjectNameException(name);
    }
    // If the relation uses a naming property definition then it must
    // be a valid value.
    PropertyDefinition<?> pd = r.getNamingPropertyDefinition();
    if (pd != null) {
      try {
        pd.decodeValue(name);
      } catch (IllegalPropertyValueStringException e) {
        throw new IllegalManagedObjectNameException(name, pd);
      }
    }
    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d, name);
    return createNewManagedObject(d, childPath, pd, name, exceptions);
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient,
                S extends Configuration, CC extends C>
  ManagedObject<CC> createChild(
      OptionalRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException {
    validateRelationDefinition(r);
    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
    return createNewManagedObject(d, childPath, null, null, exceptions);
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      InstantiableRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    Driver ctx = getDriver();
    return ctx.getManagedObject(path.child(r, name));
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
      DefinitionDecodingException, ManagedObjectDecodingException,
      ManagedObjectNotFoundException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    Driver ctx = getDriver();
    return ctx.getManagedObject(path.child(r));
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      SingletonRelationDefinition<C, S> r) throws IllegalArgumentException,
      DefinitionDecodingException, ManagedObjectDecodingException,
      ManagedObjectNotFoundException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    Driver ctx = getDriver();
    return ctx.getManagedObject(path.child(r));
  }
  /**
   * {@inheritDoc}
   */
  public final T getConfiguration() {
    return definition.createClientConfiguration(this);
  }
  /**
   * {@inheritDoc}
   */
  public final ManagedObjectDefinition<T, ? extends Configuration>
  getManagedObjectDefinition() {
    return definition;
  }
  /**
   * {@inheritDoc}
   */
  public final ManagedObjectPath<T, ? extends Configuration>
  getManagedObjectPath() {
    return path;
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> SortedSet<PD> getPropertyDefaultValues(
      PropertyDefinition<PD> pd) throws IllegalArgumentException {
    return new TreeSet<PD>(getProperty(pd).getDefaultValues());
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> PD getPropertyValue(PropertyDefinition<PD> pd)
      throws IllegalArgumentException {
    Set<PD> values = getProperty(pd).getEffectiveValues();
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> SortedSet<PD> getPropertyValues(PropertyDefinition<PD> pd)
      throws IllegalArgumentException {
    return new TreeSet<PD>(getProperty(pd).getEffectiveValues());
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  boolean hasChild(
      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    try {
      return ctx.hasManagedObject(path, r);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final boolean isPropertyPresent(PropertyDefinition<?> pd)
      throws IllegalArgumentException {
    return !getProperty(pd).isEmpty();
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(
      InstantiableRelationDefinition<C, S> r) throws IllegalArgumentException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    return listChildren(r, r.getChildDefinition());
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(
      InstantiableRelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    try {
      return ctx.listManagedObjects(path, r, d);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  void removeChild(
      InstantiableRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    boolean found;
    try {
      found = ctx.deleteManagedObject(path, r, name);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
    if (!found) {
      throw new ManagedObjectNotFoundException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  void removeChild(
      OptionalRelationDefinition<C, S> r) throws IllegalArgumentException,
      ManagedObjectNotFoundException, OperationRejectedException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    boolean found;
    try {
      found = ctx.deleteManagedObject(path, r);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
    if (!found) {
      throw new ManagedObjectNotFoundException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (value == null) {
      setPropertyValues(pd, Collections.<PD> emptySet());
    } else {
      setPropertyValues(pd, Collections.singleton(value));
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> void setPropertyValues(PropertyDefinition<PD> pd,
      Collection<PD> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (pd.hasOption(PropertyOption.MONITORING)) {
      throw new PropertyIsReadOnlyException(pd);
    }
    if (existsOnServer && pd.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(pd);
    }
    properties.setPropertyValues(pd, values);
    // If this is a naming property then update the name.
    if (pd.equals(namingPropertyDefinition)) {
      // The property must be single-valued and mandatory.
      String newName = pd.encodeValue(values.iterator().next());
      path = path.rename(newName);
    }
  }
  /**
   * Adds this new managed object.
   *
   * @throws ManagedObjectAlreadyExistsException
   *           If the managed object cannot be added to the server
   *           because it already exists.
   * @throws ConcurrentModificationException
   *           If the managed object's parent has been removed by
   *           another client.
   * @throws OperationRejectedException
   *           If the server refuses to add this managed object due to
   *           some server-side constraint which cannot be satisfied.
   * @throws AuthorizationException
   *           If the server refuses to add this managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  protected abstract void addNewManagedObject() throws AuthorizationException,
      CommunicationException, OperationRejectedException,
      ConcurrentModificationException, ManagedObjectAlreadyExistsException;
  /**
   * Gets the management context driver associated with this managed
   * object.
   *
   * @return Returns the management context driver associated with
   *         this managed object.
   */
  protected abstract Driver getDriver();
  /**
   * Gets the naming property definition associated with this managed
   * object.
   *
   * @return Returns the naming property definition associated with
   *         this managed object, or <code>null</code> if this
   *         managed object does not have a naming property.
   */
  protected final PropertyDefinition<?> getNamingPropertyDefinition() {
    return namingPropertyDefinition;
  }
  /**
   * Gets the property associated with the specified property
   * definition.
   *
   * @param <PD>
   *          The underlying type of the property.
   * @param pd
   *          The Property definition.
   * @return Returns the property associated with the specified
   *         property definition.
   * @throws IllegalArgumentException
   *           If this property provider does not recognize the
   *           requested property definition.
   */
  protected final <PD> Property<PD> getProperty(PropertyDefinition<PD> pd)
      throws IllegalArgumentException {
    return properties.getProperty(pd);
  }
  /**
   * Applies changes made to this managed object.
   *
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws OperationRejectedException
   *           If the server refuses to modify this managed object due
   *           to some server-side constraint which cannot be
   *           satisfied.
   * @throws AuthorizationException
   *           If the server refuses to modify this managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  protected abstract void modifyExistingManagedObject()
      throws ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException;
  /**
   * Creates a new managed object.
   *
   * @param <M>
   *          The type of client configuration represented by the
   *          client managed object.
   * @param d
   *          The managed object's definition.
   * @param path
   *          The managed object's path.
   * @param properties
   *          The managed object's properties.
   * @param existsOnServer
   *          Indicates whether or not the managed object exists on
   *          the server (false means the managed object is new and
   *          has not been committed).
   * @param namingPropertyDefinition
   *          Optional naming property definition.
   * @return Returns the new managed object.
   */
  protected abstract <M extends ConfigurationClient>
  ManagedObject<M> newInstance(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
      PropertySet properties, boolean existsOnServer,
      PropertyDefinition<?> namingPropertyDefinition);
  // Creates a new managed object with no active values, just default
  // values.
  private <M extends ConfigurationClient, PD> ManagedObject<M>
  createNewManagedObject(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> p,
      PropertyDefinition<PD> namingPropertyDefinition, String name,
      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);
        }
      }
    }
    // Set the naming property if there is one.
    if (namingPropertyDefinition != null) {
      PD value = namingPropertyDefinition.decodeValue(name);
      childProperties.setPropertyValues(namingPropertyDefinition, Collections
          .singleton(value));
    }
    return newInstance(d, p, childProperties, false, namingPropertyDefinition);
  }
  // Create an empty property.
  private <PD> void createProperty(PropertySet properties,
      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd)
      throws DefaultBehaviorException {
    try {
      Driver context = getDriver();
      Collection<PD> defaultValues = context.findDefaultValues(p, pd, true);
      properties.addProperty(pd, defaultValues, Collections.<PD> emptySet());
    } catch (DefaultBehaviorException e) {
      // Make sure that we have still created the property.
      properties.addProperty(pd, Collections.<PD> emptySet(), Collections
          .<PD> emptySet());
      throw e;
    }
  }
  // Makes sure that this managed object exists.
  private void ensureThisManagedObjectExists()
      throws ConcurrentModificationException, CommunicationException,
      AuthorizationException {
    if (!path.isEmpty()) {
      Driver ctx = getDriver();
      try {
        ctx.getManagedObject(path);
      } catch (DefinitionDecodingException e) {
        // Ignore.
      } catch (ManagedObjectDecodingException e) {
        // Ignore.
      } catch (ManagedObjectNotFoundException e) {
        throw new ConcurrentModificationException();
      }
    }
  }
  // Validate that a relation definition belongs to this managed
  // object.
  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
      throws IllegalArgumentException {
    ManagedObjectDefinition<T, ?> 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());
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
New file
@@ -0,0 +1,701 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.spi;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.Configuration;
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.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyNotFoundException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
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.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.OperationRejectedException;
/**
 * An abstract management connection context driver which should form
 * the basis of driver implementations.
 */
public abstract class Driver {
  /**
   * A default behavior visitor used for retrieving the default values
   * of a property.
   *
   * @param <T>
   *          The type of the property.
   */
  private class DefaultValueFinder<T> implements
      DefaultBehaviorProviderVisitor<T, Collection<T>, Void> {
    // Any exception that occurred whilst retrieving inherited default
    // values.
    private DefaultBehaviorException exception = null;
    // The path of the managed object containing the first property.
    private final ManagedObjectPath<?, ?> firstPath;
    // Indicates whether the managed object has been created yet.
    private final boolean isCreate;
    // The path of the managed object containing the next property.
    private ManagedObjectPath<?, ?> nextPath = null;
    // The next property whose default values were required.
    private PropertyDefinition<T> nextProperty = null;
    // Private constructor.
    private DefaultValueFinder(ManagedObjectPath<?, ?> p, boolean isCreate) {
      this.firstPath = p;
      this.isCreate = isCreate;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d, Void p) {
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        Void p) {
      Collection<String> stringValues = d.getDefaultValues();
      List<T> values = new ArrayList<T>(stringValues.size());
      for (String stringValue : stringValues) {
        try {
          values.add(nextProperty.decodeValue(stringValue));
        } catch (IllegalPropertyValueStringException e) {
          exception = new DefaultBehaviorException(nextProperty, e);
          break;
        }
      }
      return values;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d, Void p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(nextPath), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = e;
        return Collections.emptySet();
      }
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        Void p) {
      return Collections.emptySet();
    }
    // Find the default values for the next path/property.
    private Collection<T> find(ManagedObjectPath<?, ?> p,
        PropertyDefinition<T> pd) throws DefaultBehaviorException {
      this.nextPath = p;
      this.nextProperty = pd;
      Collection<T> values = nextProperty.getDefaultBehaviorProvider().accept(
          this, null);
      if (exception != null) {
        throw exception;
      }
      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
        throw new DefaultBehaviorException(pd,
            new PropertyIsSingleValuedException(pd));
      }
      return values;
    }
    // Get an inherited property value.
    @SuppressWarnings("unchecked")
    private Collection<T> getInheritedProperty(ManagedObjectPath target,
        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
        throws DefaultBehaviorException {
      // First check that the requested type of managed object
      // corresponds to the path.
      AbstractManagedObjectDefinition<?, ?> supr = target
          .getManagedObjectDefinition();
      if (!supr.isParentOf(d)) {
        throw new DefaultBehaviorException(nextProperty,
            new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION));
      }
      // Save the current property in case of recursion.
      PropertyDefinition<T> pd1 = nextProperty;
      try {
        // Determine the requested property definition.
        PropertyDefinition<T> pd2;
        try {
          // FIXME: we use the definition taken from the default
          // behavior here when we should really use the exact
          // definition of the component being created.
          PropertyDefinition<?> pdTmp = d.getPropertyDefinition(propertyName);
          pd2 = pd1.getClass().cast(pdTmp);
        } catch (IllegalArgumentException e) {
          throw new PropertyNotFoundException(propertyName);
        } catch (ClassCastException e) {
          // FIXME: would be nice to throw a better exception here.
          throw new PropertyNotFoundException(propertyName);
        }
        // If the path relates to the current managed object and the
        // managed object is in the process of being created it won't
        // exist, so we should just use the default values of the
        // referenced property.
        if (isCreate && firstPath.equals(target)) {
          // Recursively retrieve this property's default values.
          Collection<T> tmp = find(target, pd2);
          Collection<T> values = new ArrayList<T>(tmp.size());
          for (T value : tmp) {
            pd1.validateValue(value);
            values.add(value);
          }
          return values;
        } else {
          return getPropertyValues(target, pd2);
        }
      } catch (DefaultBehaviorException e) {
        // Wrap any errors due to recursion.
        throw new DefaultBehaviorException(pd1, e);
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (PropertyNotFoundException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (AuthorizationException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (ManagedObjectNotFoundException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (CommunicationException e) {
        throw new DefaultBehaviorException(pd1, e);
      } catch (PropertyException e) {
        throw new DefaultBehaviorException(pd1, e);
      }
    }
  };
  /**
   * Creates a new abstract management context.
   */
  protected Driver() {
    // No implementation required.
  }
  /**
   * Deletes the named instantiable child managed object from the
   * named parent managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @param name
   *          The name of the child managed object to be removed.
   * @return Returns <code>true</code> if the named instantiable
   *         child managed object was found, or <code>false</code>
   *         if it was not found.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws OperationRejectedException
   *           If the server refuses to remove the child managed
   *           object due to some server-side constraint which cannot
   *           be satisfied (for example, if it is referenced by
   *           another managed object).
   * @throws AuthorizationException
   *           If the server refuses to make the list the managed
   *           objects because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
      String name) throws IllegalArgumentException,
      ManagedObjectNotFoundException, OperationRejectedException,
      AuthorizationException, CommunicationException;
  /**
   * Deletes the optional child managed object from the named parent
   * managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The optional relation definition.
   * @return Returns <code>true</code> if the optional child managed
   *         object was found, or <code>false</code> if it was not
   *         found.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws OperationRejectedException
   *           If the server refuses to remove the child managed
   *           object due to some server-side constraint which cannot
   *           be satisfied (for example, if it is referenced by
   *           another managed object).
   * @throws AuthorizationException
   *           If the server refuses to make the list the managed
   *           objects because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, AuthorizationException,
      CommunicationException;
  /**
   * Gets the named managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param path
   *          The path of the managed object.
   * @return Returns the named managed object.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
   * @throws ManagedObjectDecodingException
   *           If the managed object was found but one or more of its
   *           properties could not be decoded.
   * @throws ManagedObjectNotFoundException
   *           If the requested managed object could not be found on
   *           the server.
   * @throws AuthorizationException
   *           If the server refuses to retrieve the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getManagedObject(
      ManagedObjectPath<C, S> path) throws DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException;
  /**
   * Gets the effective value of a property in the named managed
   * object.
   *
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param path
   *          The path of the managed object containing the property.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there are no values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with the
   *           referenced managed object's definition.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
   * @throws PropertyException
   *           If the managed object was found but the requested
   *           property could not be decoded.
   * @throws ManagedObjectNotFoundException
   *           If the requested managed object could not be found on
   *           the server.
   * @throws AuthorizationException
   *           If the server refuses to retrieve the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
      PropertyException {
    Set<PD> values = getPropertyValues(path, pd);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
  /**
   * Gets the effective values of a property in the named managed
   * object.
   * <p>
   * Implementations MUST NOT not use
   * {@link #getManagedObject(ManagedObjectPath)} to read the
   * referenced managed object in its entirety. Specifically,
   * implementations MUST only attempt to resolve the default values
   * for the requested property and its dependencies (if it uses
   * inherited defaults). This is to avoid infinite recursion where a
   * managed object contains a property which inherits default values
   * from another property in the same managed object.
   *
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param path
   *          The path of the managed object containing the property.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's effective values, or an empty set
   *         if there are no values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with the
   *           referenced managed object's definition.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
   * @throws PropertyException
   *           If the managed object was found but the requested
   *           property could not be decoded.
   * @throws ManagedObjectNotFoundException
   *           If the requested managed object could not be found on
   *           the server.
   * @throws AuthorizationException
   *           If the server refuses to retrieve the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <PD> SortedSet<PD> getPropertyValues(
      ManagedObjectPath<?, ?> path, PropertyDefinition<PD> pd)
      throws IllegalArgumentException, DefinitionDecodingException,
      AuthorizationException, ManagedObjectNotFoundException,
      CommunicationException, PropertyException;
  /**
   * Determines whether or not the named parent managed object has the
   * named instantiable child managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @param name
   *          The name of the child managed object.
   * @return Returns <code>true</code> if the named instantiable
   *         child managed object exists, <code>false</code>
   *         otherwise.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @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.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  boolean hasManagedObject(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
      String name) throws IllegalArgumentException,
      ManagedObjectNotFoundException, AuthorizationException,
      CommunicationException {
    // FIXME: use naming properties for comparison where available.
    String[] children = listManagedObjects(parent, rd);
    String nname = toLowerCase(name.trim().replaceAll(" +", " "));
    for (String child : children) {
      if (nname.equals(toLowerCase(child.trim().replaceAll(" +", " ")))) {
        return true;
      }
    }
    return false;
  }
  /**
   * Determines whether or not the named parent managed object has an
   * optional child managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The optional relation definition.
   * @return Returns <code>true</code> if the optional child managed
   *         object exists, <code>false</code> otherwise.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @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.
   */
  public abstract <C extends ConfigurationClient, S extends Configuration>
  boolean hasManagedObject(
      ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException;
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    return listManagedObjects(parent, rd, rd.getChildDefinition());
  }
  /**
   * Lists the child managed objects of the named parent managed
   * object which are a sub-type of the specified managed object
   * definition.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @param d
   *          The managed object definition.
   * @return Returns the names of the child managed objects which are
   *         a sub-type of the specified managed object definition.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <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;
  /**
   * Gets the default values for the specified property.
   *
   * @param <PD>
   *          The type of the property.
   * @param p
   *          The managed object path of the current managed object.
   * @param pd
   *          The property definition.
   * @param isCreate
   *          Indicates whether the managed object has been created
   *          yet.
   * @return Returns the default values for the specified property.
   * @throws DefaultBehaviorException
   *           If the default values could not be retrieved or decoded
   *           properly.
   */
  protected final <PD> Collection<PD> findDefaultValues(
      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd, boolean isCreate)
      throws DefaultBehaviorException {
    DefaultValueFinder<PD> v = new DefaultValueFinder<PD>(p, isCreate);
    return v.find(p, pd);
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Property.java
File was renamed from opendj-sdk/opends/src/server/org/opends/server/admin/client/Property.java
@@ -25,7 +25,7 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
package org.opends.server.admin.client.spi;
@@ -36,28 +36,26 @@
/**
 * A managed object property comprising of the property's definition and its set
 * of values.
 * A managed object property comprising of the property's definition
 * and its set of values.
 * <p>
 * The property stores the values in a sorted set in which values are compared
 * using the comparator defined by the property definition.
 * The property stores the values in a sorted set in which values are
 * compared using the comparator defined by the property definition.
 * <p>
 * The property keeps track of whether or not its pending set of values differs
 * from its active values.
 * The property keeps track of whether or not its pending set of
 * values differs from its active values.
 *
 * @param <T>
 *          The type of the property.
 * @see ManagedObject The <code>ManagedObject</code> documentation describes
 *      the different types of property values in more detail.
 */
public interface Property<T> {
  /**
   * Get an immutable set view of this property's active values.
   *
   * @return Returns an immutable set view of this property's active values. An
   *         empty set indicates that there are no active values, and any
   *         default values are applicable.
   * @return Returns an immutable set view of this property's active
   *         values. An empty set indicates that there are no active
   *         values, and any default values are applicable.
   */
  SortedSet<T> getActiveValues();
@@ -66,8 +64,9 @@
  /**
   * Get an immutable set view of this property's default values.
   *
   * @return Returns an immutable set view of this property's default values. An
   *         empty set indicates that there are no default values.
   * @return Returns an immutable set view of this property's default
   *         values. An empty set indicates that there are no default
   *         values.
   */
  SortedSet<T> getDefaultValues();
@@ -76,7 +75,8 @@
  /**
   * Get an immutable set view of this property's effective values.
   *
   * @return Returns an immutable set view of this property's effective values.
   * @return Returns an immutable set view of this property's
   *         effective values.
   */
  SortedSet<T> getEffectiveValues();
@@ -85,12 +85,12 @@
  /**
   * Get an immutable set view of this property's pending values.
   * <p>
   * Immediately after construction, the pending values matches the active
   * values.
   * Immediately after construction, the pending values matches the
   * active values.
   *
   * @return Returns an immutable set view of this property's pending values. An
   *         empty set indicates that there are no pending values, and any
   *         default values are applicable.
   * @return Returns an immutable set view of this property's pending
   *         values. An empty set indicates that there are no pending
   *         values, and any default values are applicable.
   */
  SortedSet<T> getPendingValues();
@@ -99,39 +99,42 @@
  /**
   * Get the property definition associated with this property.
   *
   * @return Returns the property definition associated with this property.
   * @return Returns the property definition associated with this
   *         property.
   */
  PropertyDefinition<T> getPropertyDefinition();
  /**
   * Determines whether or not this property contains any pending values.
   * Determines whether or not this property contains any pending
   * values.
   *
   * @return Returns <code>true</code> if this property does not contain any
   *         pending values.
   * @return Returns <code>true</code> if this property does not
   *         contain any pending values.
   */
  boolean isEmpty();
  /**
   * Determines whether or not this property has been modified since it was
   * constructed. In other words, whether or not the set of pending values
   * differs from the set of active values.
   * Determines whether or not this property has been modified since
   * it was constructed. In other words, whether or not the set of
   * pending values differs from the set of active values.
   *
   * @return Returns <code>true</code> if this property has been modified
   *         since it was constructed.
   * @return Returns <code>true</code> if this property has been
   *         modified since it was constructed.
   */
  boolean isModified();
  /**
   * Determines whether or not this property contains any active values.
   * Determines whether or not this property contains any active
   * values.
   *
   * @return Returns <code>true</code> if this property does not contain any
   *         active values.
   * @return Returns <code>true</code> if this property does not
   *         contain any active values.
   */
  boolean wasEmpty();
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/PropertySet.java
File was renamed from opendj-sdk/opends/src/server/org/opends/server/admin/client/PropertySet.java
@@ -25,7 +25,7 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
package org.opends.server.admin.client.spi;
@@ -33,7 +33,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -42,7 +41,6 @@
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
@@ -50,7 +48,7 @@
 * A set of properties. Instances of this class can be used as the
 * core of a managed object implementation.
 */
public final class PropertySet implements PropertyProvider {
public final class PropertySet {
  /**
   * Internal property implementation.
@@ -255,17 +253,6 @@
  /**
   * Makes all pending values active.
   */
  public void commit() {
    for (MyProperty<?> p : properties.values()) {
      p.commit();
    }
  }
  /**
   * Get the property associated with the specified property
   * definition.
   *
@@ -292,88 +279,34 @@
  /**
   * Get the effective value of the specified property.
   * <p>
   * See the class description for more information about how the
   * effective property value is derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there is no effective value
   *         defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
   * {@inheritDoc}
   */
  public <T> T getPropertyValue(PropertyDefinition<T> d)
      throws IllegalArgumentException {
    Set<T> values = getPropertyValues(d);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append('{');
    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties
        .entrySet()) {
      builder.append(entry.getKey().getName());
      builder.append('=');
      builder.append(entry.getValue().toString());
      builder.append(' ');
    }
    builder.append('}');
    return builder.toString();
  }
  /**
   * Get the effective values of the specified property.
   * <p>
   * See the class description for more information about how the
   * effective property values are derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective values, or an empty set
   *         if there are no effective values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
      throws IllegalArgumentException {
    Property<T> property = getProperty(d);
    return new TreeSet<T>(property.getEffectiveValues());
  }
  /**
   * Set a new pending value for the specified property.
   * <p>
   * See the class description for more information regarding pending
   * values.
   *
   * @param <T>
   *          The type of the property to be modified.
   * @param d
   *          The property to be modified.
   * @param value
   *          The new pending value for the property, or
   *          <code>null</code> if the property should be reset to
   *          its default behavior.
   * @throws IllegalPropertyValueException
   *           If the new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
   *           If the specified property definition is not associated
   *           with this managed object.
   * Makes all pending values active.
   */
  public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
      throws IllegalPropertyValueException, PropertyIsMandatoryException,
      IllegalArgumentException {
    if (value == null) {
      setPropertyValues(d, Collections.<T> emptySet());
    } else {
      setPropertyValues(d, Collections.singleton(value));
  void commit() {
    for (MyProperty<?> p : properties.values()) {
      p.commit();
    }
  }
@@ -406,7 +339,7 @@
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  public <T> void setPropertyValues(PropertyDefinition<T> d,
  <T> void setPropertyValues(PropertyDefinition<T> d,
      Collection<T> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsMandatoryException,
      IllegalArgumentException {
@@ -435,24 +368,4 @@
    // Update the property.
    property.setPendingValues(values);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append('{');
    for (Map.Entry<PropertyDefinition<?>, MyProperty<?>> entry : properties
        .entrySet()) {
      builder.append(entry.getKey().getName());
      builder.append('=');
      builder.append(entry.getValue().toString());
      builder.append(' ');
    }
    builder.append('}');
    return builder.toString();
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/package-info.java
New file
@@ -0,0 +1,39 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
/**
 * Client side driver implementation interfaces.
 * <p>
 * This package contains classes which client-side driver
 * implementations are expected to use.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.PRIVATE)
package org.opends.server.admin.client.spi;
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java
File was renamed from opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java
@@ -25,12 +25,15 @@
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
package org.opends.server.admin.client.spi;
import static org.testng.Assert.*;
import org.testng.annotations.*;
import org.opends.server.admin.*;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.spi.Property;
import org.opends.server.admin.client.spi.PropertySet;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.std.meta.RootCfgDefn;