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

matthew_swift
01.06.2007 70298b0c8e01f5df3a5f766f3624bed0ac8c2695
Fix issue 1734: Admin framework: refactor client APIs

This change refactors the client APIs so that it is now possible to easily implement features that require direct access to properties and managed objects (e.g. dependency/constraint enforcement call-backs - issue 1451). Previous to this change an application would have to drill down from the root managed object in order to find the required managed object and its properties.

This change is required by issue 1451 (dependency support), which in turn is required by issue 1449 (aggregation support).


Description:
------------

This change splits the client API into two:

* org.opends.server.admin.client: this contains APIs which client applications such as dsconfig should use to interact with the admin framework

* org.opends.server.admin.client.spi: (new package) this contains the APIs which driver implementations (e.g. JNDI driver) should use as a basis for their implementation. This package includes a Driver class which is intended for use by ManagedObject implementations as well as dependency call-backs.

In addition, I have refactored the LDAPManagedObject implementation so that code that is likely to be used by other driver implementations is pushed up into an AbstractManagedObject and the Driver base class.


Testing:
--------

All unit tests pass and basic walk-through of dsconfig interactive mode also works fine.

4 files added
3 files renamed
5 files modified
3518 ■■■■■ changed files
opends/src/server/org/opends/server/admin/client/ManagedObject.java 46 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ManagementContext.java 28 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java 595 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java 1162 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagementContext.java 38 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/package-info.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java 696 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/spi/Driver.java 701 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/spi/Property.java 67 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/spi/PropertySet.java 133 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/spi/package-info.java 39 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java 5 ●●●● patch | view | raw | blame | history
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;
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();
}
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());
    }
  }
}
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());
    }
  }
}
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);
  }
}
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)
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());
    }
  }
}
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);
  }
}
opends/src/server/org/opends/server/admin/client/spi/Property.java
File was renamed from 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();
}
opends/src/server/org/opends/server/admin/client/spi/PropertySet.java
File was renamed from 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.
   * Makes all pending values active.
   */
  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.
   */
  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();
  }
}
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;
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/spi/PropertySetTest.java
File was renamed from 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;