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

matthew_swift
05.12.2007 22117901143acf6b4d8c449ee88fab4fe1e4baf9
Partial fix for issue 1449: administration framework aggregation support

This change adds support for "aggregation" properties, which are properties which reference other managed objects (see issue 1449 for more details). Subsequent changes will add server-side and client-side referential integrity support, as well as migrating components over to using them (this will be post MS1.0).

8 files added
23 files modified
2930 ■■■■■ changed files
opendj-sdk/opends/build.xml 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/admin/admin.xsd 95 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/admin/property-types.xsl 1 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/admin/property-types/aggregation.xsl 153 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java 551 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java 85 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java 11 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java 22 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java 29 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/Reference.java 245 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java 144 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java 50 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java 20 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java 174 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/AggregationPropertyDefinitionTest.java 132 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java 6 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java 155 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfg.java 12 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgClient.java 25 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java 108 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.properties 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildConfiguration.xml 24 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgDefn.properties 12 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentConfiguration.xml 6 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/AggregationTest.java 357 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java 58 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java 377 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java 34 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DNBuilderTest.java 4 ●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java 19 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ListenerTest.java 7 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/build.xml
@@ -1088,6 +1088,11 @@
        <path refid="quickSetup.classpath" />
      </classpath>
    </javac>
    <copy todir="${unittest.classes.dir}">
      <fileset dir="${unittest.testng.src.dir}"
               includes="**/*.properties" />
    </copy>
    <!-- Prep the TestNG XML file -->
opendj-sdk/opends/resource/admin/admin.xsd
@@ -61,9 +61,9 @@
        type="tns:description-type">
        <xsd:annotation>
          <xsd:documentation>
            The user friendly plural name of this managed object. This element
            is optional and by default the user friendly plural name is derived
            from the definition's plural-name attribute.
            The user friendly plural name of this managed object. This
            element is optional and by default the user friendly plural
            name is derived from the definition's plural-name attribute.
          </xsd:documentation>
        </xsd:annotation>
      </xsd:element>
@@ -678,17 +678,6 @@
        </xsd:documentation>
      </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="aggregation" type="tns:path-type"
      use="optional">
      <xsd:annotation>
        <xsd:documentation>
          For aggregation relations, the path locating the relation
          beneath which the referenced (aggregated) managed objects can
          be found. If this attribute is not specified, the relation is
          a composition by default.
        </xsd:documentation>
      </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="managed-object-name" type="tns:name-type"
      use="optional">
      <xsd:annotation>
@@ -1032,6 +1021,84 @@
      </xsd:documentation>
    </xsd:annotation>
    <xsd:choice>
      <xsd:element name="aggregation">
        <xsd:annotation>
          <xsd:documentation>
            An aggregation property names one or more managed objects
            which are required by the managed object associated with
            this property. An aggregation property definition takes care
            to perform referential integrity checks: referenced managed
            objects cannot be deleted. Nor can an aggregation reference
            non-existent managed objects. Referential integrity checks
            are not performed during value validation. Instead they are
            performed when changes to the managed object are committed.
          </xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
          <xsd:attribute name="parent-path" type="tns:path-type"
            use="required">
            <xsd:annotation>
              <xsd:documentation>
                The name of the managed object which is the parent of
                the aggregated managed objects.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
          <xsd:attribute name="relation-name" type="tns:name-type"
            use="required">
            <xsd:annotation>
              <xsd:documentation>
                The relation in the parent managed object which contains
                the aggregated managed objects.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
          <xsd:attribute name="managed-object-name" type="tns:name-type"
            use="optional">
            <xsd:annotation>
              <xsd:documentation>
                The type of managed object referenced by this
                aggregation if different from this relation's name.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
          <xsd:attribute name="managed-object-package"
            type="tns:package-type" use="optional">
            <xsd:annotation>
              <xsd:documentation>
                The package containing the referenced managed object
                definition if it is not the same as this managed
                object's package.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
          <xsd:attribute name="source-enabled-property-name"
            type="tns:name-type" use="optional">
            <xsd:annotation>
              <xsd:documentation>
                The optional boolean "enabled" property in the
                aggregating managed object. When this property is true,
                the "target-enabled-property-name" in the aggregated
                managed objects must also be true. The
                "target-enabled-property-name" attribute must be
                specified when this attribute is defined.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
          <xsd:attribute name="target-enabled-property-name"
            type="tns:name-type" use="optional">
            <xsd:annotation>
              <xsd:documentation>
                The optional boolean "enabled" property in the
                aggregated managed objects. This property must not be
                false while the aggregated managed object is referenced.
                This attribute must be defined when the
                "source-enabled-property-name" attribute is specified.
              </xsd:documentation>
            </xsd:annotation>
          </xsd:attribute>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="attribute-type">
        <xsd:annotation>
          <xsd:documentation>
opendj-sdk/opends/resource/admin/property-types.xsl
@@ -39,6 +39,7 @@
    
    
  -->
  <xsl:include href="property-types/aggregation.xsl" />
  <xsl:include href="property-types/attribute-type.xsl" />
  <xsl:include href="property-types/boolean.xsl" />
  <xsl:include href="property-types/dn.xsl" />
opendj-sdk/opends/resource/admin/property-types/aggregation.xsl
New file
@@ -0,0 +1,153 @@
<!--
  ! 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.
  ! -->
<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!--
    Templates for processing aggregation properties.
  -->
  <xsl:template match="adm:aggregation" mode="java-definition-imports">
    <xsl:element name="import">
      <xsl:call-template name="get-definition-package" />
      <xsl:value-of select="'.client.'" />
      <xsl:call-template name="get-client-type" />
    </xsl:element>
    <xsl:element name="import">
      <xsl:call-template name="get-definition-package" />
      <xsl:value-of select="'.server.'" />
      <xsl:call-template name="get-server-type" />
    </xsl:element>
    <xsl:element name="import">
      <xsl:call-template name="get-definition-package" />
      <xsl:value-of select="'.meta.'" />
      <xsl:call-template name="get-definition-type" />
    </xsl:element>
    <import>org.opends.server.admin.ManagedObjectPath</import>
  </xsl:template>
  <xsl:template match="adm:aggregation" mode="java-value-type">
    <xsl:value-of select="'String'" />
  </xsl:template>
  <xsl:template match="adm:aggregation" mode="java-definition-type">
    <xsl:value-of select="'AggregationPropertyDefinition'" />
  </xsl:template>
  <xsl:template match="adm:aggregation"
    mode="java-definition-generic-type">
    <xsl:call-template name="get-client-type" />
    <xsl:value-of select="', '" />
    <xsl:call-template name="get-server-type" />
  </xsl:template>
  <xsl:template match="adm:aggregation" mode="java-definition-ctor">
    <xsl:if test="not(@parent-path)">
      <xsl:message terminate="yes">
        <xsl:value-of
          select="concat('No parent-path defined for aggregation property ', ../../@name)" />
      </xsl:message>
    </xsl:if>
    <xsl:if test="not(@relation-name)">
      <xsl:message terminate="yes">
        <xsl:value-of
          select="concat('No relation-name defined for aggregation property ', ../../@name)" />
      </xsl:message>
    </xsl:if>
    <xsl:if
      test="@source-enabled-property-name and not(@target-enabled-property-name)">
      <xsl:message terminate="yes">
        <xsl:value-of
          select="concat('source-enabled-property-name defined but target-enabled-property-name undefined in aggregation property ', ../../@name)" />
      </xsl:message>
    </xsl:if>
    <xsl:value-of
      select="concat('      builder.setParentPath(ManagedObjectPath.valueOf(&quot;',
                     normalize-space(@parent-path), '&quot;));&#xa;')" />
    <xsl:value-of
      select="concat('      builder.setRelationDefinition(&quot;',
                     normalize-space(@relation-name), '&quot;);&#xa;')" />
    <xsl:value-of select="'      builder.setManagedObjectDefinition('" />
    <xsl:call-template name="get-definition-type" />
    <xsl:value-of select="'.getInstance());&#xa;'" />
    <xsl:if test="@source-enabled-property-name">
      <xsl:value-of
        select="concat('      builder.setSourceEnabledPropertyName(&quot;',
                       normalize-space(@source-enabled-property-name), '&quot;);&#xa;')" />
    </xsl:if>
    <xsl:if test="@target-enabled-property-name">
      <xsl:value-of
        select="concat('      builder.setTargetEnabledPropertyName(&quot;',
                       normalize-space(@target-enabled-property-name), '&quot;);&#xa;')" />
    </xsl:if>
  </xsl:template>
  <!--
    Gets the Java client configuration interface for the referenced type.
  -->
  <xsl:template name="get-client-type">
    <xsl:call-template name="get-reference-type" />
    <xsl:value-of select="'CfgClient'" />
  </xsl:template>
  <!--
    Gets the Java server configuration interface for the referenced type.
  -->
  <xsl:template name="get-server-type">
    <xsl:call-template name="get-reference-type" />
    <xsl:value-of select="'Cfg'" />
  </xsl:template>
  <!--
    Gets the Java definition configuration interface for the referenced type.
  -->
  <xsl:template name="get-definition-type">
    <xsl:call-template name="get-reference-type" />
    <xsl:value-of select="'CfgDefn'" />
  </xsl:template>
  <!--
    Gets the Java definition configuration package.
  -->
  <xsl:template name="get-definition-package">
    <xsl:choose>
      <xsl:when test="@managed-object-package">
        <xsl:value-of select="@managed-object-package" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$this-package" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <!--
    Gets the Java name for the referenced type.
  -->
  <xsl:template name="get-reference-type">
    <xsl:choose>
      <xsl:when test="@managed-object-name">
        <xsl:call-template name="name-to-java">
          <xsl:with-param name="value" select="@managed-object-name" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="name-to-java">
          <xsl:with-param name="value" select="@relation-name" />
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
opendj-sdk/opends/src/server/org/opends/server/admin/AggregationPropertyDefinition.java
New file
@@ -0,0 +1,551 @@
/*
 * 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;
import static org.opends.server.util.Validator.*;
import java.util.EnumSet;
import org.opends.server.types.DN;
/**
 * Aggregation property definition.
 * <p>
 * An aggregation property names one or more managed objects which are
 * required by the managed object associated with this property. An
 * aggregation property definition takes care to perform referential
 * integrity checks: referenced managed objects cannot be deleted. Nor
 * can an aggregation reference non-existent managed objects.
 * Referential integrity checks are <b>not</b> performed during value
 * validation. Instead they are performed when changes to the managed
 * object are committed.
 * <p>
 * An aggregation property definition can optionally identify two
 * properties:
 * <ul>
 * <li>an <code>enabled</code> property in the aggregated managed
 * object - the property must be a {@link BooleanPropertyDefinition}
 * and indicate whether the aggregated managed object is enabled or
 * not. If specified, the administration framework will prevent the
 * aggregated managed object from being disabled while it is
 * referenced
 * <li>an <code>enabled</code> property in this property's managed
 * object - the property must be a {@link BooleanPropertyDefinition}
 * and indicate whether this property's managed object is enabled or
 * not. If specified, and as long as there is an equivalent
 * <code>enabled</code> property defined for the aggregated managed
 * object, the <code>enabled</code> property in the aggregated
 * managed object will only be checked when this property is true.
 * </ul>
 * In other words, these properties can be used to make sure that
 * referenced managed objects are not disabled while they are
 * referenced.
 *
 * @param <C>
 *          The type of client managed object configuration that this
 *          aggregation property definition refers to.
 * @param <S>
 *          The type of server managed object configuration that this
 *          aggregation property definition refers to.
 */
public final class AggregationPropertyDefinition
    <C extends ConfigurationClient, S extends Configuration>
    extends PropertyDefinition<String> {
  /**
   * An interface for incrementally constructing aggregation property
   * definitions.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this aggregation property definition refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this aggregation property definition refers to.
   */
  public static class Builder
      <C extends ConfigurationClient, S extends Configuration>
      extends AbstractBuilder<String, AggregationPropertyDefinition<C, S>> {
    // The name of the managed object which is the parent of the
    // aggregated managed objects.
    private ManagedObjectPath<?, ?> p = null;
    // The name of a relation in the parent managed object which
    // contains the aggregated managed objects.
    private String rdName = null;
    // The type of referenced managed objects.
    private AbstractManagedObjectDefinition<?, ?> cd = null;
    // The optional name of a boolean "enabled" property in this
    // managed object. When this property is true, the enabled
    // property in the aggregated managed object must also be true.
    private String sourceEnabledPropertyName = null;
    // The optional name of a boolean "enabled" property in the
    // aggregated managed object. This property must not be false
    // while the aggregated managed object is referenced.
    private String targetEnabledPropertyName = null;
    // Private constructor
    private Builder(AbstractManagedObjectDefinition<?, ?> d,
        String propertyName) {
      super(d, propertyName);
    }
    /**
     * Sets the name of the managed object which is the parent of the
     * aggregated managed objects.
     * <p>
     * This must be defined before the property definition can be
     * built.
     *
     * @param p
     *          The name of the managed object which is the parent of
     *          the aggregated managed objects.
     */
    public final void setParentPath(ManagedObjectPath<?, ?> p) {
      this.p = p;
    }
    /**
     * Sets the relation in the parent managed object which contains
     * the aggregated managed objects.
     * <p>
     * This must be defined before the property definition can be
     * built.
     *
     * @param rdName
     *          The name of a relation in the parent managed object
     *          which contains the aggregated managed objects.
     */
    public final void setRelationDefinition(String rdName) {
      this.rdName = rdName;
    }
    /**
     * Sets the definition of the type of referenced managed objects.
     * <p>
     * This must be defined before the property definition can be
     * built.
     *
     * @param d
     *          The definition of the type of referenced managed
     *          objects.
     */
    public final void setManagedObjectDefinition(
        AbstractManagedObjectDefinition<C, S> d) {
      this.cd = d;
    }
    /**
     * Sets the optional boolean "enabled" property in this managed
     * object. When this property is true, the enabled property in the
     * aggregated managed object must also be true.
     * <p>
     * By default no source property is defined. When it is defined,
     * the target property must also be defined.
     *
     * @param sourceEnabledPropertyName
     *          The optional boolean "enabled" property in this
     *          managed object.
     */
    public final void setSourceEnabledPropertyName(
        String sourceEnabledPropertyName) {
      this.sourceEnabledPropertyName = sourceEnabledPropertyName;
    }
    /**
     * Sets the optional boolean "enabled" property in the aggregated
     * managed object. This property must not be false while the
     * aggregated managed object is referenced.
     * <p>
     * By default no target property is defined. It must be defined,
     * if the source property is defined.
     *
     * @param targetEnabledPropertyName
     *          The optional boolean "enabled" property in the
     *          aggregated managed object.
     */
    public final void setTargetEnabledPropertyName(
        String targetEnabledPropertyName) {
      this.targetEnabledPropertyName = targetEnabledPropertyName;
    }
    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    @Override
    protected AggregationPropertyDefinition<C, S> buildInstance(
        AbstractManagedObjectDefinition<?, ?> d, String propertyName,
        EnumSet<PropertyOption> options, AdministratorAction adminAction,
        DefaultBehaviorProvider<String> defaultBehavior) {
      // Make sure that the parent path has been defined.
      if (p == null) {
        throw new IllegalStateException("Parent path undefined");
      }
      // Make sure that the relation definition has been defined.
      if (rdName == null) {
        throw new IllegalStateException("Relation definition undefined");
      }
      // Make sure that the managed object definition has been
      // defined.
      if (cd == null) {
        throw new IllegalStateException("Managed object definition undefined");
      }
      // Make sure that the relation definition is a member of the
      // parent path's definition.
      AbstractManagedObjectDefinition<?, ?> parent = p
          .getManagedObjectDefinition();
      RelationDefinition<?, ?> rd = parent.getRelationDefinition(rdName);
      // Make sure the relation refers to the child type.
      AbstractManagedObjectDefinition<?, ?> dTmp = rd.getChildDefinition();
      if (dTmp != cd) {
        throw new IllegalStateException("Relation definition \"" + rd.getName()
            + "\" does not refer to definition " + d.getName());
      }
      // Force the relation to the correct type.
      InstantiableRelationDefinition<C, S> relation =
        (InstantiableRelationDefinition<C, S>) rd;
      // Make sure that if a source property is specified then a
      // target property is also specified.
      if (sourceEnabledPropertyName != null
          && targetEnabledPropertyName == null) {
        throw new IllegalStateException(
            "Source property defined but target property is undefined");
      }
      return new AggregationPropertyDefinition<C, S>(d, propertyName, options,
          adminAction, defaultBehavior, p, relation, sourceEnabledPropertyName,
          targetEnabledPropertyName);
    }
  }
  /**
   * Creates an aggregation property definition builder.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this aggregation property definition refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this aggregation property definition refers to.
   * @param d
   *          The managed object definition associated with this
   *          property definition.
   * @param propertyName
   *          The property name.
   * @return Returns the new aggregation property definition builder.
   */
  public static <C extends ConfigurationClient, S extends Configuration>
  Builder<C, S> createBuilder(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName) {
    return new Builder<C, S>(d, propertyName);
  }
  // The name of the managed object which is the parent of the
  // aggregated managed objects.
  private final ManagedObjectPath<?, ?> parentPath;
  // The relation in the parent managed object which contains the
  // aggregated managed objects.
  private final InstantiableRelationDefinition<C, S> relationDefinition;
  // The optional name of a boolean "enabled" property in this managed
  // object. When this property is true, the enabled property in the
  // aggregated managed object must also be true.
  private final String sourceEnabledPropertyName;
  // The optional name of a boolean "enabled" property in the
  // aggregated managed object. This property must not be false while
  // the aggregated managed object is referenced.
  private final String targetEnabledPropertyName;
  // Private constructor.
  private AggregationPropertyDefinition(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName,
      EnumSet<PropertyOption> options, AdministratorAction adminAction,
      DefaultBehaviorProvider<String> defaultBehavior,
      ManagedObjectPath<?, ?> parentPath,
      InstantiableRelationDefinition<C, S> relationDefinition,
      String sourceEnabledPropertyName, String targetEnabledPropertyName) {
    super(d, String.class, propertyName, options, adminAction, defaultBehavior);
    this.parentPath = parentPath;
    this.relationDefinition = relationDefinition;
    this.sourceEnabledPropertyName = sourceEnabledPropertyName;
    this.targetEnabledPropertyName = targetEnabledPropertyName;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyDefinitionVisitor<R, P> v, P p) {
    return v.visitAggregation(this, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(PropertyValueVisitor<R, P> v, String value, P p) {
    return v.visitAggregation(this, value, p);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String decodeValue(String value)
      throws IllegalPropertyValueStringException {
    ensureNotNull(value);
    try {
      validateValue(value);
      return value;
    } catch (IllegalPropertyValueException e) {
      throw new IllegalPropertyValueStringException(this, value);
    }
  }
  /**
   * Constructs a DN for a referenced managed object having the
   * provided name. This method is implemented by first calling
   * {@link #getChildPath(String)} and then invoking
   * {@link ManagedObjectPath#toDN()} on the returned path.
   *
   * @param name
   *          The name of the child managed object.
   * @return Returns a DN for a referenced managed object having the
   *         provided name.
   */
  public final DN getChildDN(String name) {
    return getChildPath(name).toDN();
  }
  /**
   * Constructs a managed object path for a referenced managed object
   * having the provided name.
   *
   * @param name
   *          The name of the child managed object.
   * @return Returns a managed object path for a referenced managed
   *         object having the provided name.
   */
  public final ManagedObjectPath<C, S> getChildPath(String name) {
    return parentPath.child(relationDefinition, name);
  }
  /**
   * Gets the name of the managed object which is the parent of the
   * aggregated managed objects.
   *
   * @return Returns the name of the managed object which is the
   *         parent of the aggregated managed objects.
   */
  public final ManagedObjectPath<?, ?> getParentPath() {
    return parentPath;
  }
  /**
   * Gets the relation in the parent managed object which contains the
   * aggregated managed objects.
   *
   * @return Returns the relation in the parent managed object which
   *         contains the aggregated managed objects.
   */
  public final InstantiableRelationDefinition<C, S> getRelationDefinition() {
    return relationDefinition;
  }
  /**
   * Gets the optional boolean "enabled" property in this managed
   * object. When this property is true, the enabled property in the
   * aggregated managed object must also be true.
   *
   * @return Returns the optional boolean "enabled" property in this
   *         managed object, or <code>null</code> if none is
   *         defined.
   * @throws IllegalArgumentException
   *           If the named property does not exist in this property's
   *           associated managed object definition.
   * @throws ClassCastException
   *           If the named property does exist but is not a
   *           {@link BooleanPropertyDefinition}.
   */
  public final BooleanPropertyDefinition getSourceEnabledPropertyDefinition()
      throws IllegalArgumentException, ClassCastException {
    if (sourceEnabledPropertyName == null) {
      return null;
    }
    AbstractManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
    PropertyDefinition<?> pd;
    pd = d.getPropertyDefinition(sourceEnabledPropertyName);
    return (BooleanPropertyDefinition) pd;
  }
  /**
   * Gets the optional boolean "enabled" property in the aggregated
   * managed object. This property must not be false while the
   * aggregated managed object is referenced.
   *
   * @return Returns the optional boolean "enabled" property in the
   *         aggregated managed object, or <code>null</code> if none
   *         is defined.
   * @throws IllegalArgumentException
   *           If the named property does not exist in the aggregated
   *           managed object's definition.
   * @throws ClassCastException
   *           If the named property does exist but is not a
   *           {@link BooleanPropertyDefinition}.
   */
  public final BooleanPropertyDefinition getTargetEnabledPropertyDefinition()
      throws IllegalArgumentException, ClassCastException {
    if (targetEnabledPropertyName == null) {
      return null;
    }
    AbstractManagedObjectDefinition<?, ?> d;
    PropertyDefinition<?> pd;
    d = relationDefinition.getChildDefinition();
    pd = d.getPropertyDefinition(targetEnabledPropertyName);
    return (BooleanPropertyDefinition) pd;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String normalizeValue(String value)
      throws IllegalPropertyValueException {
    try {
      Reference<C, S> reference = Reference.parseName(parentPath,
          relationDefinition, value);
      return reference.getNormalizedName();
    } catch (IllegalArgumentException e) {
      throw new IllegalPropertyValueException(this, value);
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void toString(StringBuilder builder) {
    super.toString(builder);
    builder.append(" parentPath=");
    builder.append(parentPath);
    builder.append(" relationDefinition=");
    builder.append(relationDefinition.getName());
    if (sourceEnabledPropertyName != null) {
      builder.append(" sourceEnabledPropertyName=");
      builder.append(sourceEnabledPropertyName);
    }
    if (targetEnabledPropertyName != null) {
      builder.append(" targetEnabledPropertyName=");
      builder.append(targetEnabledPropertyName);
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void validateValue(String value) throws IllegalPropertyValueException {
    try {
      Reference.parseName(parentPath, relationDefinition, value);
    } catch (IllegalArgumentException e) {
      throw new IllegalPropertyValueException(this, value);
    }
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/ManagedObjectDefinitionI18NResource.java
@@ -214,6 +214,91 @@
  /**
   * Forcefully removes any resource bundles associated with the
   * provided definition and using the default locale.
   * <p>
   * This method is intended for internal testing only.
   *
   * @param d
   *          The managed object definition.
   */
  synchronized void removeResourceBundle(
      AbstractManagedObjectDefinition<?, ?> d) {
    removeResourceBundle(d, Locale.getDefault());
  }
  /**
   * Forcefully removes any resource bundles associated with the
   * provided definition and locale.
   * <p>
   * This method is intended for internal testing only.
   *
   * @param d
   *          The managed object definition.
   * @param locale
   *          The locale.
   */
  synchronized void removeResourceBundle(
      AbstractManagedObjectDefinition<?, ?> d, Locale locale) {
    // Get the locale resource mapping.
    Map<Locale, ResourceBundle> map = resources.get(d);
    if (map != null) {
      map.remove(locale);
    }
  }
  /**
   * Forcefully adds the provided resource bundle to this I18N
   * resource for the default locale.
   * <p>
   * This method is intended for internal testing only.
   *
   * @param d
   *          The managed object definition.
   * @param resoureBundle
   *          The resource bundle to be used.
   */
  synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d,
      ResourceBundle resoureBundle) {
    setResourceBundle(d, Locale.getDefault(), resoureBundle);
  }
  /**
   * Forcefully adds the provided resource bundle to this I18N
   * resource.
   * <p>
   * This method is intended for internal testing only.
   *
   * @param d
   *          The managed object definition.
   * @param locale
   *          The locale.
   * @param resoureBundle
   *          The resource bundle to be used.
   */
  synchronized void setResourceBundle(AbstractManagedObjectDefinition<?, ?> d,
      Locale locale, ResourceBundle resoureBundle) {
    // First get the locale-resource mapping, creating it if
    // necessary.
    Map<Locale, ResourceBundle> map = resources.get(d);
    if (map == null) {
      map = new HashMap<Locale, ResourceBundle>();
      resources.put(d, map);
    }
    // Add the resource bundle.
    map.put(locale, resoureBundle);
  }
  // Retrieve the resource bundle associated with a managed object and
  // locale, lazily loading it if necessary.
  private synchronized ResourceBundle getResourceBundle(
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionUsageBuilder.java
@@ -72,6 +72,17 @@
     * {@inheritDoc}
     */
    @Override
    public <C extends ConfigurationClient, S extends Configuration>
    Message visitAggregation(AggregationPropertyDefinition<C, S> d, Void p) {
      return Message.raw("NAME");
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Message visitAttributeType(AttributeTypePropertyDefinition d,
        Void p) {
      return Message.raw("OID");
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyDefinitionVisitor.java
@@ -65,6 +65,28 @@
  /**
   * Visit an aggregation property definition.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this aggregation property definition refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this aggregation property definition refers to.
   * @param d
   *          The aggregation property definition to visit.
   * @param p
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  public <C extends ConfigurationClient, S extends Configuration>
  R visitAggregation(AggregationPropertyDefinition<C, S> d, P p) {
    return visitUnknown(d, p);
  }
  /**
   * Visit an attribute type property definition.
   *
   * @param d
opendj-sdk/opends/src/server/org/opends/server/admin/PropertyValueVisitor.java
@@ -73,6 +73,31 @@
  /**
   * Visit an aggregation property value.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this aggregation property definition refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this aggregation property definition refers to.
   * @param d
   *          The aggregation property definition to visit.
   * @param v
   *          The property value to visit.
   * @param p
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  public <C extends ConfigurationClient, S extends Configuration>
  R visitAggregation(
      AggregationPropertyDefinition<C, S> d, String v, P p) {
    return visitUnknown(d, v, p);
  }
  /**
   * Visit an attribute type.
   *
   * @param d
@@ -172,8 +197,8 @@
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  public <E extends Enum<E>> R visitEnum(
      EnumPropertyDefinition<E> d, E v, P p) {
  public <E extends Enum<E>>
  R visitEnum(EnumPropertyDefinition<E> d, E v, P p) {
    return visitUnknown(d, v, p);
  }
opendj-sdk/opends/src/server/org/opends/server/admin/Reference.java
New file
@@ -0,0 +1,245 @@
/*
 * 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;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.RDN;
import org.opends.server.util.StaticUtils;
/**
 * A reference to another managed object.
 *
 * @param <C>
 *          The type of client managed object configuration that this
 *          reference refers to.
 * @param <S>
 *          The type of server managed object configuration that this
 *          reference refers to.
 */
public final class Reference<C extends ConfigurationClient,
                             S extends Configuration> {
  /**
   * Parses a DN string value as a reference using the provided
   * managed object path and relation definition.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this reference refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this reference refers to.
   * @param p
   *          The path of the referenced managed object's parent.
   * @param rd
   *          The instantiable relation in the parent which contains
   *          the referenced managed object.
   * @param s
   *          The DN string value.
   * @return Returns the new reference based on the provided DN string
   *         value.
   * @throws IllegalArgumentException
   *           If the DN string value could not be decoded as a DN or
   *           if the provided DN did not correspond to the provided
   *           path and relation.
   */
  public static <C extends ConfigurationClient, S extends Configuration>
  Reference<C, S> parseDN(
      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
      String s) throws IllegalArgumentException {
    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
    if (tmp != rd) {
      throw new IllegalArgumentException("The relation \"" + rd.getName()
          + "\" is not associated with the definition \"" + d.getName() + "\"");
    }
    DN dn;
    try {
      dn = DN.decode(s);
    } catch (DirectoryException e) {
      throw new IllegalArgumentException("Unabled to decode the DN string: \""
          + s + "\"");
    }
    RDN rdn = dn.getRDN();
    if (rdn == null) {
      throw new IllegalArgumentException("Unabled to decode the DN string: \""
          + s + "\"");
    }
    AttributeValue av = rdn.getAttributeValue(0);
    if (av == null) {
      throw new IllegalArgumentException("Unabled to decode the DN string: \""
          + s + "\"");
    }
    String name = av.getStringValue();
    // Check that the DN was valid.
    DN expected = p.child(rd, name).toDN();
    if (!dn.equals(expected)) {
      throw new IllegalArgumentException("Unabled to decode the DN string: \""
          + s + "\"");
    }
    return new Reference<C, S>(p, rd, name);
  }
  /**
   * Parses a name as a reference using the provided managed object
   * path and relation definition.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this reference refers to.
   * @param <S>
   *          The type of server managed object configuration that
   *          this reference refers to.
   * @param p
   *          The path of the referenced managed object's parent.
   * @param rd
   *          The instantiable relation in the parent which contains
   *          the referenced managed object.
   * @param s
   *          The name of the referenced managed object.
   * @return Returns the new reference based on the provided name.
   * @throws IllegalArgumentException
   *           If the relation is not associated with the provided
   *           parent's definition, or if the provided name is empty.
   */
  public static <C extends ConfigurationClient, S extends Configuration>
  Reference<C, S> parseName(
      ManagedObjectPath<?, ?> p, InstantiableRelationDefinition<C, S> rd,
      String s) throws IllegalArgumentException {
    // Sanity checks.
    AbstractManagedObjectDefinition<?, ?> d = p.getManagedObjectDefinition();
    RelationDefinition<?, ?> tmp = d.getRelationDefinition(rd.getName());
    if (tmp != rd) {
      throw new IllegalArgumentException("The relation \"" + rd.getName()
          + "\" is not associated with the definition \"" + d.getName() + "\"");
    }
    if (s.trim().length() == 0) {
      throw new IllegalArgumentException("Empty names are not allowed");
    }
    return new Reference<C, S>(p, rd, s);
  }
  // The name of the referenced managed object.
  private final String name;
  // The path of the referenced managed object.
  private final ManagedObjectPath<C, S> path;
  // The instantiable relation in the parent which contains the
  // referenced managed object.
  private final InstantiableRelationDefinition<C, S> relation;
  // Private constructor.
  private Reference(ManagedObjectPath<?, ?> parent,
      InstantiableRelationDefinition<C, S> relation, String name)
      throws IllegalArgumentException {
    this.relation = relation;
    this.name = name;
    this.path = parent.child(relation, name);
  }
  /**
   * Gets the name of the referenced managed object.
   *
   * @return Returns the name of the referenced managed object.
   */
  public String getName() {
    return name;
  }
  /**
   * Gets the normalized name of the referenced managed object.
   *
   * @return Returns the normalized name of the referenced managed
   *         object.
   */
  public String getNormalizedName() {
    PropertyDefinition<?> pd = relation.getNamingPropertyDefinition();
    return normalizeName(pd);
  }
  /**
   * Gets the DN of the referenced managed object.
   *
   * @return Returns the DN of the referenced managed object.
   */
  public DN toDN() {
    return path.toDN();
  }
  /**
   * {@inheritDoc}
   */
  public String toString() {
    return name;
  }
  // Normalize a value using the specified naming property definition
  // if defined.
  private <T> String normalizeName(PropertyDefinition<T> pd) {
    if (pd != null) {
      try {
        T tvalue = pd.decodeValue(name);
        return pd.normalizeValue(tvalue);
      } catch (IllegalPropertyValueStringException e) {
        // Fall through to default normalization.
      }
    }
    // FIXME: should really use directory string normalizer.
    String s = name.trim().replaceAll(" +", " ");
    return StaticUtils.toLowerCase(s);
  }
}
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -49,6 +49,7 @@
import org.opends.messages.Message;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefaultBehaviorException;
@@ -61,11 +62,14 @@
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyDefinitionVisitor;
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.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
@@ -84,6 +88,73 @@
 */
final class LDAPDriver extends Driver {
  /**
   * A visitor which is used to decode property LDAP values.
   */
  private static final class ValueDecoder extends
      PropertyDefinitionVisitor<Object, String> {
    /**
     * Decodes the provided property LDAP value.
     *
     * @param <PD>
     *          The type of the property.
     * @param pd
     *          The property definition.
     * @param value
     *          The LDAP string representation.
     * @return Returns the decoded LDAP value.
     * @throws IllegalPropertyValueStringException
     *           If the property value could not be decoded because it
     *           was invalid.
     */
    public static <PD> PD decode(PropertyDefinition<PD> pd, Object value)
        throws IllegalPropertyValueStringException {
      String s = String.valueOf(value);
      return pd.castValue(pd.accept(new ValueDecoder(), s));
    }
    // Prevent instantiation.
    private ValueDecoder() {
      // No implementation required.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <C extends ConfigurationClient, S extends Configuration>
    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
      // Aggregations values are stored as full DNs in LDAP, but
      // just their common name is exposed in the admin framework.
      try {
        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
            .getRelationDefinition(), p);
        return reference.getName();
      } catch (IllegalArgumentException e) {
        throw new IllegalPropertyValueStringException(d, p);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
        throws UnknownPropertyDefinitionException {
      // By default the property definition's decoder will do.
      return d.decodeValue(p);
    }
  }
  // The LDAP connection.
  private final LDAPConnection connection;
@@ -151,20 +222,8 @@
      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);
          decodeProperty(newProperties, path, pd, attribute);
        } catch (PropertyException e) {
          exceptions.add(e);
        }
@@ -193,12 +252,23 @@
  /**
   * {@inheritDoc}
   */
  @SuppressWarnings("unchecked")
  @Override
  public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
  public <C extends ConfigurationClient, S extends Configuration, PD>
  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
      PropertyException {
    // Check that the requested property is from the definition
    // associated with the path.
    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
    if (tmp != pd) {
      throw new IllegalArgumentException("The property " + pd.getName()
          + " is not associated with a " + d.getName());
    }
    if (!managedObjectExists(path)) {
      throw new ManagedObjectNotFoundException();
    }
@@ -206,26 +276,27 @@
    try {
      // Read the entry associated with the managed object.
      LdapName dn = LDAPNameBuilder.create(path, profile);
      AbstractManagedObjectDefinition<?, ?> d = path
          .getManagedObjectDefinition();
      ManagedObjectDefinition<?, ?> mod = getEntryDefinition(d, dn);
      ManagedObjectDefinition<? extends C, ? extends S> mod;
      mod = getEntryDefinition(d, dn);
      // Make sure we use the correct property definition, the
      // provided one might have been overridden in the resolved
      // definition.
      pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
      String attrID = profile.getAttributeName(mod, pd);
      Attributes attributes = connection.readEntry(dn, Collections
          .singleton(attrID));
      Attribute attribute = attributes.get(attrID);
      // Decode the values.
      SortedSet<PD> values = new TreeSet<PD>(pd);
      if (attribute == null || attribute.size() == 0) {
        // Use the property's default values.
        values.addAll(findDefaultValues(path, pd, false));
      } else {
        // Decode the values.
      if (attribute != null) {
        NamingEnumeration<?> ldapValues = attribute.getAll();
        while (ldapValues.hasMore()) {
          Object obj = ldapValues.next();
          if (obj != null) {
            PD value = pd.decodeValue(obj.toString());
            PD value = ValueDecoder.decode(pd, obj);
            values.add(value);
          }
        }
@@ -240,6 +311,11 @@
        throw new PropertyIsMandatoryException(pd);
      }
      if (values.isEmpty()) {
        // Use the property's default values.
        values.addAll(findDefaultValues(path.asSubType(mod), pd, false));
      }
      return values;
    } catch (NameNotFoundException e) {
      throw new ManagedObjectNotFoundException();
@@ -471,24 +547,28 @@
  // Create a property using the provided string values.
  private <PD> void decodeProperty(PropertySet newProperties,
      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd, List<String> values)
      throws PropertyException {
      ManagedObjectPath<?, ?> p, PropertyDefinition<PD> pd,
      Attribute attribute) throws PropertyException,
      NamingException {
    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;
    SortedSet<PD> activeValues = new TreeSet<PD>(pd);
    if (attribute != null) {
      NamingEnumeration<?> ldapValues = attribute.getAll();
      while (ldapValues.hasMore()) {
        Object obj = ldapValues.next();
        if (obj != null) {
          PD value = ValueDecoder.decode(pd, obj);
          activeValues.add(value);
        }
      }
    }
    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
      // This exception takes precedence over previous exceptions.
      exception = new PropertyIsSingleValuedException(pd);
      PD value = activeValues.iterator().next();
      PD value = activeValues.first();
      activeValues.clear();
      activeValues.add(value);
    }
opendj-sdk/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -41,6 +41,7 @@
import javax.naming.ldap.Rdn;
import org.opends.messages.Message;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.InstantiableRelationDefinition;
@@ -50,7 +51,10 @@
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyValueVisitor;
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
@@ -73,6 +77,47 @@
final class LDAPManagedObject<T extends ConfigurationClient> extends
    AbstractManagedObject<T> {
  /**
   * A visitor which is used to encode property LDAP values.
   */
  private static final class ValueEncoder extends
      PropertyValueVisitor<Object, Void> {
    // Prevent instantiation.
    private ValueEncoder() {
      // No implementation required.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <C extends ConfigurationClient, S extends Configuration>
    Object visitAggregation(
        AggregationPropertyDefinition<C, S> d, String v, Void p) {
      // Aggregations values are stored as full DNs in LDAP, but
      // just their common name is exposed in the admin framework.
      Reference<C, S> reference = Reference.parseName(d.getParentPath(), d
          .getRelationDefinition(), v);
      return reference.toDN().toString();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <PD> Object visitUnknown(PropertyDefinition<PD> d, PD v, Void p)
        throws UnknownPropertyDefinitionException {
      return d.encodeValue(v);
    }
  }
  // The LDAP management driver associated with this managed object.
  private final LDAPDriver driver;
@@ -294,17 +339,18 @@
  // Encode a property into LDAP string values.
  private <PD> void encodeProperty(Attribute attribute,
      PropertyDefinition<PD> pd) {
    PropertyValueVisitor<Object, Void> visitor = new ValueEncoder();
    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 (PD value : p.getEffectiveValues()) {
        attribute.add(pd.encodeValue(value));
        attribute.add(pd.accept(visitor, value, null));
      }
    } else {
      for (PD value : p.getPendingValues()) {
        attribute.add(pd.encodeValue(value));
        attribute.add(pd.accept(visitor, value, null));
      }
    }
  }
opendj-sdk/opends/src/server/org/opends/server/admin/client/spi/Driver.java
@@ -430,6 +430,12 @@
   * Gets the effective value of a property in 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 <PD>
   *          The type of the property to be retrieved.
   * @param path
@@ -458,7 +464,8 @@
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
  public final <C extends ConfigurationClient, S extends Configuration, PD>
  PD getPropertyValue(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
@@ -486,6 +493,12 @@
   * managed object contains a property which inherits default values
   * from another property in the same 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 <PD>
   *          The type of the property to be retrieved.
   * @param path
@@ -514,8 +527,9 @@
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <PD> SortedSet<PD> getPropertyValues(
      ManagedObjectPath<?, ?> path, PropertyDefinition<PD> pd)
  public abstract <C extends ConfigurationClient, S extends Configuration, PD>
  SortedSet<PD> getPropertyValues(
      ManagedObjectPath<C, S> path, PropertyDefinition<PD> pd)
      throws IllegalArgumentException, DefinitionDecodingException,
      AuthorizationException, ManagedObjectNotFoundException,
      CommunicationException, PropertyException;
opendj-sdk/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -47,6 +47,7 @@
import org.opends.messages.Message;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
@@ -62,14 +63,17 @@
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyDefinitionVisitor;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyNotFoundException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.admin.std.server.RootCfg;
@@ -263,22 +267,22 @@
          throw new PropertyNotFoundException(propertyName);
        }
        List<String> stringValues = getAttribute(mod, pd2, configEntry);
        if (stringValues.isEmpty()) {
        List<AttributeValue> values = getAttribute(mod, pd2, configEntry);
        if (values.isEmpty()) {
          // Recursively retrieve this property's default values.
          Collection<T> tmp = find(target, pd2);
          Collection<T> values = new ArrayList<T>(tmp.size());
          Collection<T> pvalues = new ArrayList<T>(tmp.size());
          for (T value : tmp) {
            pd1.validateValue(value);
            values.add(value);
            pvalues.add(value);
          }
          return values;
          return pvalues;
        } else {
          Collection<T> values = new ArrayList<T>(stringValues.size());
          for (String s : stringValues) {
            values.add(pd1.decodeValue(s));
          Collection<T> pvalues = new ArrayList<T>(values.size());
          for (AttributeValue value : values) {
            pvalues.add(ValueDecoder.decode(pd1, value));
          }
          return values;
          return pvalues;
        }
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd1, e);
@@ -321,7 +325,76 @@
      String oc = LDAPProfile.getInstance().getObjectClass(d);
      return entry.hasObjectClass(oc);
    }
  };
  }
  /**
   * A visitor which is used to decode property LDAP values.
   */
  private static final class ValueDecoder extends
      PropertyDefinitionVisitor<Object, String> {
    /**
     * Decodes the provided property LDAP value.
     *
     * @param <PD>
     *          The type of the property.
     * @param pd
     *          The property definition.
     * @param value
     *          The LDAP string representation.
     * @return Returns the decoded LDAP value.
     * @throws IllegalPropertyValueStringException
     *           If the property value could not be decoded because it
     *           was invalid.
     */
    public static <PD> PD decode(PropertyDefinition<PD> pd,
        AttributeValue value) throws IllegalPropertyValueStringException {
      String s = value.getStringValue();
      return pd.castValue(pd.accept(new ValueDecoder(), s));
    }
    // Prevent instantiation.
    private ValueDecoder() {
      // No implementation required.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <C extends ConfigurationClient, S extends Configuration>
    Object visitAggregation(AggregationPropertyDefinition<C, S> d, String p) {
      // Aggregations values are stored as full DNs in LDAP, but
      // just their common name is exposed in the admin framework.
      try {
        Reference<C, S> reference = Reference.parseDN(d.getParentPath(), d
            .getRelationDefinition(), p);
        return reference.getName();
      } catch (IllegalArgumentException e) {
        throw new IllegalPropertyValueStringException(d, p);
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <T> Object visitUnknown(PropertyDefinition<T> d, String p)
        throws UnknownPropertyDefinitionException {
      // By default the property definition's decoder will do.
      return d.decodeValue(p);
    }
  }
  // Singleton instance.
  private final static ServerManagementContext INSTANCE =
@@ -399,6 +472,12 @@
   * Gets the effective value of a property in 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 <PD>
   *          The type of the property to be retrieved.
   * @param path
@@ -417,7 +496,8 @@
   *           If the named managed object could not be found or if it
   *           could not be decoded.
   */
  public <PD> PD getPropertyValue(ManagedObjectPath<?, ?> path,
  public <C extends ConfigurationClient, S extends Configuration, PD>
  PD getPropertyValue(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      ConfigException, PropertyException {
    SortedSet<PD> values = getPropertyValues(path, pd);
@@ -434,6 +514,12 @@
   * Gets the effective values of a property in 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 <PD>
   *          The type of the property to be retrieved.
   * @param path
@@ -452,15 +538,27 @@
   *           If the named managed object could not be found or if it
   *           could not be decoded.
   */
  public <PD> SortedSet<PD> getPropertyValues(ManagedObjectPath<?, ?> path,
  @SuppressWarnings("unchecked")
  public <C extends ConfigurationClient, S extends Configuration, PD>
  SortedSet<PD> getPropertyValues(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      ConfigException, PropertyException {
    // Check that the requested property is from the definition
    // associated with the path.
    AbstractManagedObjectDefinition<C, S> d = path.getManagedObjectDefinition();
    PropertyDefinition<?> tmp = d.getPropertyDefinition(pd.getName());
    if (tmp != pd) {
      throw new IllegalArgumentException("The property " + pd.getName()
          + " is not associated with a " + d.getName());
    }
    // Determine the exact type of managed object referenced by the
    // path.
    DN dn = DNBuilder.create(path);
    ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
    AbstractManagedObjectDefinition<?, ?> d = path.getManagedObjectDefinition();
    ManagedObjectDefinition<?, ?> mod;
    ManagedObjectDefinition<? extends C, ? extends S> mod;
    try {
      mod = d.resolveManagedObjectDefinition(resolver);
@@ -469,8 +567,13 @@
          .createDecodingExceptionAdaptor(dn, e);
    }
    List<String> values = getAttribute(mod, pd, configEntry);
    return decodeProperty(path, pd, values, null);
    // Make sure we use the correct property definition, the
    // provided one might have been overridden in the resolved
    // definition.
    pd = (PropertyDefinition<PD>) mod.getPropertyDefinition(pd.getName());
    List<AttributeValue> values = getAttribute(mod, pd, configEntry);
    return decodeProperty(path.asSubType(mod), pd, values, null);
  }
@@ -649,7 +752,7 @@
    Map<PropertyDefinition<?>, SortedSet<?>> properties =
      new HashMap<PropertyDefinition<?>, SortedSet<?>>();
    for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
      List<String> values = getAttribute(mod, pd, configEntry);
      List<AttributeValue> values = getAttribute(mod, pd, configEntry);
      try {
        SortedSet<?> pvalues = decodeProperty(path, pd, values, newConfigEntry);
        properties.put(pd, pvalues);
@@ -686,16 +789,16 @@
  // Create a property using the provided string values.
  private <T> SortedSet<T> decodeProperty(ManagedObjectPath<?, ?> path,
      PropertyDefinition<T> pd, List<String> stringValues,
      PropertyDefinition<T> pd, List<AttributeValue> values,
      ConfigEntry newConfigEntry) throws PropertyException {
    PropertyException exception = null;
    SortedSet<T> values = new TreeSet<T>(pd);
    SortedSet<T> pvalues = new TreeSet<T>(pd);
    if (!stringValues.isEmpty()) {
    if (!values.isEmpty()) {
      // The property has values defined for it.
      for (String value : stringValues) {
      for (AttributeValue value : values) {
        try {
          values.add(pd.decodeValue(value));
          pvalues.add(ValueDecoder.decode(pd, value));
        } catch (IllegalPropertyValueStringException e) {
          exception = e;
        }
@@ -703,21 +806,21 @@
    } else {
      // No values defined so get the defaults.
      try {
        values.addAll(getDefaultValues(path, pd, newConfigEntry));
        pvalues.addAll(getDefaultValues(path, pd, newConfigEntry));
      } catch (DefaultBehaviorException e) {
        exception = e;
      }
    }
    if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
    if (pvalues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
      // This exception takes precedence over previous exceptions.
      exception = new PropertyIsSingleValuedException(pd);
      T value = values.first();
      values.clear();
      values.add(value);
      T value = pvalues.first();
      pvalues.clear();
      pvalues.add(value);
    }
    if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
    if (pvalues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
      // The values maybe empty because of a previous exception.
      if (exception == null) {
        exception = new PropertyIsMandatoryException(pd);
@@ -727,29 +830,30 @@
    if (exception != null) {
      throw exception;
    } else {
      return values;
      return pvalues;
    }
  }
  // Gets the attribute associated with a property from a ConfigEntry.
  private List<String> getAttribute(ManagedObjectDefinition<?, ?> d,
  private List<AttributeValue> getAttribute(ManagedObjectDefinition<?, ?> d,
      PropertyDefinition<?> pd, ConfigEntry configEntry) {
    // TODO: we create a default attribute type if it is
    // undefined. We should log a warning here if this is the case
    // since the attribute should have been defined.
    String attrID = LDAPProfile.getInstance().getAttributeName(d, pd);
    AttributeType type = DirectoryServer.getAttributeType(attrID, true);
    AttributeValueDecoder<String> decoder =
      new AttributeValueDecoder<String>() {
    AttributeValueDecoder<AttributeValue> decoder =
      new AttributeValueDecoder<AttributeValue>() {
      public String decode(AttributeValue value) throws DirectoryException {
        return value.getStringValue();
      public AttributeValue decode(AttributeValue value)
          throws DirectoryException {
        return value;
      }
    };
    List<String> values = new LinkedList<String>();
    List<AttributeValue> values = new LinkedList<AttributeValue>();
    try {
      configEntry.getEntry().getAttributeValues(type, decoder, values);
    } catch (DirectoryException e) {
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/AggregationPropertyDefinitionTest.java
New file
@@ -0,0 +1,132 @@
/*
 * 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;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * AggregationPropertyDefinition Tester.
 */
@Test(sequential = true)
public class AggregationPropertyDefinitionTest extends DirectoryServerTestCase {
  /**
   * Sets up tests
   *
   * @throws Exception
   *           If the server could not be initialized.
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    TestCfg.setUp();
  }
  /**
   * Tears down test environment.
   */
  @AfterClass
  public void tearDown() {
    TestCfg.cleanup();
  }
  /**
   * Tests that the
   * {@link AggregationPropertyDefinition#normalizeValue(String)}
   * works.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testNormalizeValue() throws Exception {
    TestChildCfgDefn d = TestChildCfgDefn.getInstance();
    AggregationPropertyDefinition<?, ?> pd = d
        .getAggregationPropertyPropertyDefinition();
    String nvalue = pd.normalizeValue("  LDAP   connection    handler  ");
    Assert.assertEquals(nvalue, "ldap connection handler");
  }
  /**
   * Tests that the
   * {@link AggregationPropertyDefinition#getChildDN(String)} works.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testGetChildDN() throws Exception {
    TestChildCfgDefn d = TestChildCfgDefn.getInstance();
    AggregationPropertyDefinition<?, ?> pd = d
        .getAggregationPropertyPropertyDefinition();
    DN expected = DN
        .decode("cn=ldap connection handler, cn=connection handlers, cn=config");
    DN actual = pd.getChildDN("  LDAP  connection handler  ");
    Assert.assertEquals(actual, expected);
  }
  /**
   * Tests that the
   * {@link AggregationPropertyDefinition#getChildPath(String)} works.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testGetChildPath() throws Exception {
    TestChildCfgDefn d = TestChildCfgDefn.getInstance();
    AggregationPropertyDefinition<?, ?> pd = d
        .getAggregationPropertyPropertyDefinition();
    ManagedObjectPath<?, ?> path = pd.getChildPath("LDAP connection handler");
    Assert.assertSame(path.getManagedObjectDefinition(), pd
        .getRelationDefinition().getChildDefinition());
    Assert.assertSame(path.getRelationDefinition(), pd.getRelationDefinition());
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
@@ -81,6 +81,8 @@
        return "ds-cfg-virtual-attribute-base-dn";
      } else if (pd == (PropertyDefinition<?>)td.getOptionalMultiValuedDNProperty2PropertyDefinition()) {
        return "ds-cfg-virtual-attribute-group-dn";
      } else if (pd == (PropertyDefinition<?>)td.getAggregationPropertyPropertyDefinition()) {
        return "ds-cfg-backend-base-dn";
      } else {
        throw new RuntimeException("Unexpected test-child property"
            + pd.getName());
@@ -117,9 +119,9 @@
  public String getObjectClass(AbstractManagedObjectDefinition<?, ?> d) {
    // These casts throughout are required to work around a bug in JDK versions prior to 1.5.0_08.
    if (d == (AbstractManagedObjectDefinition<?, ?>)TestParentCfgDefn.getInstance()) {
      return "ds-cfg-virtual-attribute";
      return "ds-cfg-test-parent-dummy";
    } else if (d == (AbstractManagedObjectDefinition<?, ?>)TestChildCfgDefn.getInstance()) {
      return "ds-cfg-virtual-attribute";
      return "ds-cfg-test-child-dummy";
    } else {
      // Not known.
      return null;
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestCfg.java
@@ -28,7 +28,14 @@
import java.util.ResourceBundle;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.core.DirectoryServer;
import org.opends.server.schema.ObjectClassSyntax;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.ObjectClass;
@@ -51,8 +58,7 @@
  // Create a one-to-many relation for test-parent components.
  static {
    InstantiableRelationDefinition.Builder<TestParentCfgClient, TestParentCfg> builder =
      new InstantiableRelationDefinition.Builder<TestParentCfgClient, TestParentCfg>(
    InstantiableRelationDefinition.Builder<TestParentCfgClient, TestParentCfg> builder = new InstantiableRelationDefinition.Builder<TestParentCfgClient, TestParentCfg>(
        RootCfgDefn.getInstance(), "test-one-to-many-parent",
        "test-one-to-many-parents", TestParentCfgDefn.getInstance());
    RD_TEST_ONE_TO_MANY_PARENT = builder.getInstance();
@@ -60,13 +66,94 @@
  // Create a one-to-many relation for test-parent components.
  static {
    OptionalRelationDefinition.Builder<TestParentCfgClient, TestParentCfg> builder =
      new OptionalRelationDefinition.Builder<TestParentCfgClient, TestParentCfg>(
    OptionalRelationDefinition.Builder<TestParentCfgClient, TestParentCfg> builder = new OptionalRelationDefinition.Builder<TestParentCfgClient, TestParentCfg>(
        RootCfgDefn.getInstance(), "test-one-to-zero-or-one-parent",
        TestParentCfgDefn.getInstance());
    RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT = builder.getInstance();
  }
  // Test parent object class definition.
  private static ObjectClass TEST_PARENT_OCD = null;
  // Test child object class definition.
  private static ObjectClass TEST_CHILD_OCD = null;
  /**
   * Registers test parent and child object class definitions and any
   * required resource bundles.
   * <p>
   * Unit tests which call this method <b>must</b> call
   * {@link #cleanup()} on completion.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  public synchronized static void setUp() throws Exception {
    if (TEST_PARENT_OCD == null) {
      String ocd = "( 1.3.6.1.4.1.26027.1.2.4455114401 "
          + "NAME 'ds-cfg-test-parent-dummy' "
          + "SUP top STRUCTURAL "
          + "MUST ( cn $ ds-cfg-virtual-attribute-class $ "
          + "ds-cfg-virtual-attribute-enabled $ ds-cfg-virtual-attribute-type ) "
          + "MAY ( ds-cfg-virtual-attribute-base-dn $ ds-cfg-virtual-attribute-group-dn $ "
          + "ds-cfg-virtual-attribute-filter $ ds-cfg-virtual-attribute-conflict-behavior ) "
          + "X-ORIGIN 'OpenDS Directory Server' )";
      ByteString b = ByteStringFactory.create(ocd);
      TEST_PARENT_OCD = ObjectClassSyntax.decodeObjectClass(b, DirectoryServer
          .getSchema(), false);
    }
    if (TEST_CHILD_OCD == null) {
      String ocd = "( 1.3.6.1.4.1.26027.1.2.4455114402 "
          + "NAME 'ds-cfg-test-child-dummy' "
          + "SUP top STRUCTURAL "
          + "MUST ( cn $ ds-cfg-virtual-attribute-class $ "
          + "ds-cfg-virtual-attribute-enabled $ ds-cfg-virtual-attribute-type ) "
          + "MAY ( ds-cfg-virtual-attribute-base-dn $ ds-cfg-virtual-attribute-group-dn $ "
          + "ds-cfg-virtual-attribute-filter $ ds-cfg-virtual-attribute-conflict-behavior $"
          + "ds-cfg-backend-base-dn) " + "X-ORIGIN 'OpenDS Directory Server' )";
      ByteString b = ByteStringFactory.create(ocd);
      TEST_CHILD_OCD = ObjectClassSyntax.decodeObjectClass(b, DirectoryServer
          .getSchema(), false);
    }
    {
      // Register the test parent object class.
      DirectoryServer.registerObjectClass(TEST_PARENT_OCD, true);
      // Register the test parent resource bundle.
      TestParentCfgDefn d = TestParentCfgDefn.getInstance();
      String baseName = d.getClass().getName();
      ResourceBundle resourceBundle = ResourceBundle.getBundle(baseName);
      ManagedObjectDefinitionI18NResource.getInstance().setResourceBundle(d,
          resourceBundle);
    }
    {
      // Register the test child object class.
      DirectoryServer.registerObjectClass(TEST_CHILD_OCD, true);
      // Register the test child resource bundle.
      TestChildCfgDefn d = TestChildCfgDefn.getInstance();
      String baseName = d.getClass().getName();
      ResourceBundle resourceBundle = ResourceBundle.getBundle(baseName);
      ManagedObjectDefinitionI18NResource.getInstance().setResourceBundle(d,
          resourceBundle);
    }
    // Ensure that the relations are registered (do this after things
    // that can fail and leave tests in a bad state).
    RootCfgDefn.getInstance().registerRelationDefinition(
        RD_TEST_ONE_TO_MANY_PARENT);
    RootCfgDefn.getInstance().registerRelationDefinition(
        RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT);
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
  }
  /**
@@ -74,10 +161,24 @@
   * framework.
   */
  public static void cleanup() {
    RootCfgDefn.getInstance().deregisterRelationDefinition(
        RD_TEST_ONE_TO_MANY_PARENT);
    RootCfgDefn.getInstance().deregisterRelationDefinition(
        RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT);
    LDAPProfile.getInstance().popWrapper();
    {
      RootCfgDefn.getInstance().deregisterRelationDefinition(
          RD_TEST_ONE_TO_MANY_PARENT);
      RootCfgDefn.getInstance().deregisterRelationDefinition(
          RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT);
      DirectoryServer.deregisterObjectClass(TEST_PARENT_OCD);
      TestParentCfgDefn d = TestParentCfgDefn.getInstance();
      ManagedObjectDefinitionI18NResource.getInstance().removeResourceBundle(d);
    }
    {
      DirectoryServer.deregisterObjectClass(TEST_CHILD_OCD);
      TestChildCfgDefn d = TestChildCfgDefn.getInstance();
      ManagedObjectDefinitionI18NResource.getInstance().removeResourceBundle(d);
    }
  }
@@ -86,16 +187,13 @@
   * Gets the one-to-many relation between the root and test-parent
   * components.
   * <p>
   * Unit tests which call this method <b>must</b> call
   * {@link #cleanup()} on completion.
   * Unit tests which call this method <b>must</b> have already
   * called {@link #setUp()}.
   *
   * @return Returns the one-to-many relation between the root and
   *         test-parent components.
   */
  public static InstantiableRelationDefinition<TestParentCfgClient, TestParentCfg> getTestOneToManyParentRelationDefinition() {
    // Ensure that the relation is registered.
    RootCfgDefn.getInstance().registerRelationDefinition(
        RD_TEST_ONE_TO_MANY_PARENT);
    return RD_TEST_ONE_TO_MANY_PARENT;
  }
@@ -105,21 +203,42 @@
   * Gets the one-to-zero-or-one relation between the root and a
   * test-parent component.
   * <p>
   * Unit tests which call this method <b>must</b> call
   * {@link #cleanup()} on completion.
   * Unit tests which call this method <b>must</b> have already
   * called {@link #setUp()}.
   *
   * @return Returns the one-to-zero-or-one relation between the root
   *         and a test-parent component.
   */
  public static OptionalRelationDefinition<TestParentCfgClient, TestParentCfg> getTestOneToZeroOrOneParentRelationDefinition() {
    // Ensure that the relation is registered.
    RootCfgDefn.getInstance().registerRelationDefinition(
        RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT);
    return RD_TEST_ONE_TO_ZERO_OR_ONE_PARENT;
  }
  /**
   * Adds a constraint temporarily with test child definition.
   *
   * @param constraint
   *          The constraint.
   */
  public static void addConstraint(Constraint constraint) {
    TestChildCfgDefn.getInstance().registerConstraint(constraint);
  }
  /**
   * Removes a constraint from the test child definition.
   *
   * @param constraint
   *          The constraint.
   */
  public static void removeConstraint(Constraint constraint) {
    TestChildCfgDefn.getInstance().deregisterConstraint(constraint);
  }
  // Prevent instantiation.
  private TestCfg() {
    // No implementation required.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfg.java
@@ -32,6 +32,7 @@
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -76,6 +77,17 @@
  /**
   * Get the "aggregation-property" property.
   * <p>
   * An aggregation property which references connection handlers.
   *
   * @return Returns the values of the "aggregation-property" property.
   */
  SortedSet<String> getAggregationProperty();
  /**
   * Get the "mandatory-boolean-property" property.
   * <p>
   * A mandatory boolean property.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgClient.java
@@ -34,6 +34,7 @@
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -59,6 +60,30 @@
  /**
   * Get the "aggregation-property" property.
   * <p>
   * An aggregation property which references connection handlers.
   *
   * @return Returns the values of the "aggregation-property" property.
   */
  SortedSet<String> getAggregationProperty();
  /**
   * Set the "aggregation-property" property.
   * <p>
   * An aggregation property which references connection handlers.
   *
   * @param values The values of the "aggregation-property" property.
   * @throws IllegalPropertyValueException
   *           If one or more of the new values are invalid.
   */
  void setAggregationProperty(Collection<String> values) throws IllegalPropertyValueException;
  /**
   * Get the "mandatory-boolean-property" property.
   * <p>
   * A mandatory boolean property.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
@@ -34,24 +34,30 @@
import org.opends.server.admin.AttributeTypePropertyDefinition;
import org.opends.server.admin.BooleanPropertyDefinition;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.DNPropertyDefinition;
import org.opends.server.admin.DefaultBehaviorProvider;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.DefaultBehaviorProvider;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.DNPropertyDefinition;
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.std.client.ConnectionHandlerCfgClient;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn;
import org.opends.server.admin.std.server.ConnectionHandlerCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.types.AttributeType;
import org.opends.server.types.DN;
@@ -72,6 +78,11 @@
  // The "aggregation-property" property definition.
  private static final AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg> PD_AGGREGATION_PROPERTY;
  // The "mandatory-boolean-property" property definition.
  private static final BooleanPropertyDefinition PD_MANDATORY_BOOLEAN_PROPERTY;
@@ -97,6 +108,21 @@
  // Build the "aggregation-property" property definition.
  static {
      AggregationPropertyDefinition.Builder<ConnectionHandlerCfgClient, ConnectionHandlerCfg> builder = AggregationPropertyDefinition.createBuilder(INSTANCE, "aggregation-property");
      builder.setOption(PropertyOption.MULTI_VALUED);
      builder.setAdministratorAction(new AdministratorAction(AdministratorAction.Type.NONE, INSTANCE, "aggregation-property"));
      builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider<String>());
      builder.setParentPath(ManagedObjectPath.valueOf("/"));
      builder.setRelationDefinition("connection-handler");
      builder.setManagedObjectDefinition(ConnectionHandlerCfgDefn.getInstance());
      PD_AGGREGATION_PROPERTY = builder.getInstance();
      INSTANCE.registerPropertyDefinition(PD_AGGREGATION_PROPERTY);
  }
  // Build the "mandatory-boolean-property" property definition.
  static {
      BooleanPropertyDefinition.Builder builder = BooleanPropertyDefinition.createBuilder(INSTANCE, "mandatory-boolean-property");
@@ -180,28 +206,6 @@
  private TestChildCfgDefn() {
    super("test-child", null);
  }
  /**
   * Adds a constraint temporarily with this test definition.
   *
   * @param constraint The constraint.
   */
  public void addConstraint(Constraint constraint) {
    registerConstraint(constraint);
  }
  /**
   * Removes a constraint from this test definition.
   *
   * @param constraint The constraint.
   */
  public void removeConstraint(Constraint constraint) {
    deregisterConstraint(constraint);
  }
@@ -235,6 +239,19 @@
  /**
   * Get the "aggregation-property" property definition.
   * <p>
   * An aggregation property which references connection handlers.
   *
   * @return Returns the "aggregation-property" property definition.
   */
  public AggregationPropertyDefinition<ConnectionHandlerCfgClient, ConnectionHandlerCfg> getAggregationPropertyPropertyDefinition() {
    return PD_AGGREGATION_PROPERTY;
  }
  /**
   * Get the "mandatory-boolean-property" property definition.
   * <p>
   * A mandatory boolean property.
@@ -323,6 +340,24 @@
    /**
     * {@inheritDoc}
     */
    public SortedSet<String> getAggregationProperty() {
      return impl.getPropertyValues(INSTANCE.getAggregationPropertyPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public void setAggregationProperty(Collection<String> values) {
      impl.setPropertyValues(INSTANCE.getAggregationPropertyPropertyDefinition(), values);
    }
    /**
     * {@inheritDoc}
     */
    public Boolean isMandatoryBooleanProperty() {
      return impl.getPropertyValue(INSTANCE.getMandatoryBooleanPropertyPropertyDefinition());
    }
@@ -483,6 +518,15 @@
    /**
     * {@inheritDoc}
     */
    public SortedSet<String> getAggregationProperty() {
      return impl.getPropertyValues(INSTANCE.getAggregationPropertyPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public boolean isMandatoryBooleanProperty() {
      return impl.getPropertyValue(INSTANCE.getMandatoryBooleanPropertyPropertyDefinition());
    }
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.properties
New file
@@ -0,0 +1,9 @@
user-friendly-name=Test Child
user-friendly-plural-name=Test Children
synopsis=A configuration for testing components that are subordinate to a parent component. It re-uses the virtual-attribute configuration LDAP profile.
property.aggregation-property.synopsis=An aggregation property which references connection handlers.
property.mandatory-boolean-property.synopsis=A mandatory boolean property.
property.mandatory-class-property.synopsis=A mandatory Java-class property requiring a component restart.
property.mandatory-read-only-attribute-type-property.synopsis=A mandatory read-only attribute type property.
property.optional-multi-valued-dn-property1.synopsis=An optional multi-valued DN property which inherits its values from optional-multi-valued-dn-property in the parent.
property.optional-multi-valued-dn-property2.synopsis=An optional multi-valued DN property which inherits its values from optional-multi-valued-dn-property1.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildConfiguration.xml
@@ -26,7 +26,7 @@
  !      Portions Copyright 2007 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="test-child" plural-name="test-children"
  package="org.opends.server.admin.std"
  package="org.opends.server.admin"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
@@ -36,8 +36,8 @@
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.91</ldap:oid>
      <ldap:name>ds-cfg-virtual-attribute</ldap:name>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.4455114402</ldap:oid>
      <ldap:name>ds-cfg-test-child-dummy</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
@@ -140,4 +140,22 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="aggregation-property" multi-valued="true">
    <adm:synopsis>
      An aggregation property which references connection handlers.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:undefined />
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation parent-path="/"
        relation-name="connection-handler" />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.332</ldap:oid>
        <ldap:name>ds-task-initialize-domain-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgDefn.properties
New file
@@ -0,0 +1,12 @@
user-friendly-name=Test Parent
user-friendly-plural-name=Test Parents
synopsis=A configuration for testing components that have child components. It re-uses the virtual-attribute configuration LDAP profile.
property.mandatory-boolean-property.synopsis=A mandatory boolean property.
property.mandatory-class-property.synopsis=A mandatory Java-class property requiring a component restart.
property.mandatory-read-only-attribute-type-property.synopsis=A mandatory read-only attribute type property.
property.optional-multi-valued-dn-property.synopsis=An optional multi-valued DN property with a defined default behavior.
relation.optional-test-child.user-friendly-name=Optional Test Child
relation.optional-test-child.synopsis=A configuration for testing components that are subordinate to a parent component. It re-uses the virtual-attribute configuration LDAP profile.
relation.test-child.user-friendly-name=Test Child
relation.test-child.user-friendly-plural-name=Test Children
relation.test-child.synopsis=A configuration for testing components that are subordinate to a parent component. It re-uses the virtual-attribute configuration LDAP profile.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentConfiguration.xml
@@ -26,7 +26,7 @@
  !      Portions Copyright 2007 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="test-parent" plural-name="test-parents"
  package="org.opends.server.admin.std"
  package="org.opends.server.admin"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
@@ -35,8 +35,8 @@
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.91</ldap:oid>
      <ldap:name>ds-cfg-virtual-attribute</ldap:name>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.4455114401</ldap:oid>
      <ldap:name>ds-cfg-test-parent-dummy</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/AggregationTest.java
New file
@@ -0,0 +1,357 @@
/*
 * 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.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfgClient;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.std.client.RootCfgClient;
import org.opends.server.core.DirectoryServer;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for aggregations on the client-side.
 */
@Test(sequential = true)
public class AggregationTest extends AdminTestCase {
  // Test LDIF.
  private static final String[] TEST_LDIF = new String[] {
      // Base entries.
      "dn: cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: config",
      "",
      "dn: cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: test-parents",
      "",
      // Parent 1 - uses default values for
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "",
      // Child base entry.
      "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: multiple children",
      "",
      // Child 1 has no references.
      "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "",
      // Child 2 has a single valid reference.
      "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config",
      "",
      // Child 3 has a multiple valid references.
      "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 3",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config",
      "ds-cfg-backend-base-dn: cn=LDAPS Connection Handler, cn=connection handlers, cn=config",
      "",
      // Child 4 has a single bad reference.
      "dn: cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 4",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=bad rdn, cn=config",
      "",
  };
  /**
   * Sets up tests
   *
   * @throws Exception
   *           If the server could not be initialized.
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    TestCfg.setUp();
  }
  /**
   * Tears down test environment.
   */
  @AfterClass
  public void tearDown() {
    TestCfg.cleanup();
  }
  /**
   * Tests that aggregation contains no values when it contains does
   * not contain any DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationEmpty() throws Exception {
    MockLDAPConnection c = new MockLDAPConnection();
    c.importLDIF(TEST_LDIF);
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 1");
    assertSetEquals(child.getAggregationProperty(), new String[0]);
  }
  /**
   * Tests that aggregation contains single valid value when it
   * contains a single valid DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationSingle() throws Exception {
    MockLDAPConnection c = new MockLDAPConnection();
    c.importLDIF(TEST_LDIF);
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 2");
    // Test normalization.
    assertSetEquals(child.getAggregationProperty(), "LDAP Connection Handler");
    assertSetEquals(child.getAggregationProperty(),
        "  LDAP   Connection  Handler ");
    assertSetEquals(child.getAggregationProperty(),
        "  ldap connection HANDLER ");
  }
  /**
   * Tests that aggregation contains multiple valid values when it
   * contains a multiple valid DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationMultiple() throws Exception {
    MockLDAPConnection c = new MockLDAPConnection();
    c.importLDIF(TEST_LDIF);
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 3");
    assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler",
        "LDAP Connection Handler");
  }
  /**
   * Tests that aggregation is rejected when the LDAP DN contains a
   * valid RDN but an invalid parent DN.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationBadBaseDN() throws Exception {
    MockLDAPConnection c = new MockLDAPConnection();
    c.importLDIF(TEST_LDIF);
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    try {
      parent.getTestChild("test child 4");
      Assert.fail("Unexpectedly retrieved test child 4"
          + " when it had a bad aggregation value");
    } catch (ManagedObjectDecodingException e) {
      Collection<PropertyException> causes = e.getCauses();
      Assert.assertEquals(causes.size(), 1);
      Throwable cause = causes.iterator().next();
      if (cause instanceof IllegalPropertyValueStringException) {
        IllegalPropertyValueStringException pe = (IllegalPropertyValueStringException) cause;
        Assert.assertEquals(pe.getPropertyDefinition(), TestChildCfgDefn
            .getInstance().getAggregationPropertyPropertyDefinition());
        Assert.assertEquals(pe.getIllegalValueString(),
            "cn=LDAP Connection Handler, cn=bad rdn, cn=config");
      } else {
        // Got an unexpected cause.
        throw e;
      }
    }
  }
  /**
   * Tests creation of a child managed object with a single reference.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test
  public void testCreateChildManagedObject() throws Exception {
    CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
        "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child new");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-type", "description");
    c.addExpectedAttribute("ds-cfg-backend-base-dn",
        "cn=LDAP Connection Handler,cn=connection handlers,cn=config");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
        .getInstance(), "test child new", null);
    child.setMandatoryBooleanProperty(true);
    child.setMandatoryReadOnlyAttributeTypeProperty(DirectoryServer
        .getAttributeType("description"));
    child.setAggregationProperty(Collections
        .singleton("LDAP Connection Handler"));
    child.commit();
    c.assertEntryIsCreated();
  }
  /**
   * Tests modification of a child managed object so that it has a
   * different reference.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test
  public void testModifyChildManagedObject() throws Exception {
    ModifyEntryMockLDAPConnection c = new ModifyEntryMockLDAPConnection(
        "cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedModification("ds-cfg-backend-base-dn",
        "cn=LDAPS Connection Handler,cn=connection handlers,cn=config",
        "cn=JMX Connection Handler,cn=connection handlers,cn=config");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c);
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 2");
    child.setAggregationProperty(Arrays.asList("LDAPS Connection Handler",
        "JMX Connection Handler"));
    child.commit();
    Assert.assertTrue(c.isEntryModified());
  }
  // Retrieve the named test parent managed object.
  private TestParentCfgClient getTestParent(ManagementContext context,
      String name) throws DefinitionDecodingException,
      ManagedObjectDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, ConcurrentModificationException,
      CommunicationException {
    ManagedObject<RootCfgClient> root = context
        .getRootConfigurationManagedObject();
    return root.getChild(TestCfg.getTestOneToManyParentRelationDefinition(),
        name).getConfiguration();
  }
  // Asserts that the actual set of DNs contains the expected values.
  private void assertSetEquals(SortedSet<String> actual, String... expected) {
    SortedSet<String> values = new TreeSet<String>(TestChildCfgDefn
        .getInstance().getAggregationPropertyPropertyDefinition());
    if (expected != null) {
      for (String value : expected) {
        values.add(value);
      }
    }
    Assert.assertEquals((Object) actual, (Object) values);
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java
@@ -42,10 +42,8 @@
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.TestChildCfgDefn;
@@ -93,7 +91,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -103,7 +101,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -115,7 +113,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 3,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 3",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -139,7 +137,7 @@
      // optional-multi-valued-dn-property2.
      "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -149,7 +147,7 @@
      // optional-multi-valued-dn-property2.
      "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -162,7 +160,7 @@
      // optional-multi-valued-dn-property2.
      "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 3",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -177,12 +175,12 @@
      // optional-multi-valued-dn-property2.
      "dn: cn=test child 1,cn=test children,cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      ""
      "",
  };
@@ -273,7 +271,7 @@
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
    TestCfg.setUp();
  }
@@ -283,7 +281,6 @@
   */
  @AfterClass
  public void tearDown() {
    LDAPProfile.getInstance().popWrapper();
    TestCfg.cleanup();
  }
@@ -301,7 +298,7 @@
        "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child new");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -372,7 +369,7 @@
        "cn=test parent new,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test parent new");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-parent-dummy");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -438,6 +435,7 @@
        "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
    assertDNSetEquals(child.getOptionalMultiValuedDNProperty2(),
        "dc=domain1,dc=com", "dc=domain2,dc=com", "dc=domain3,dc=com");
    Assert.assertEquals(child.isMandatoryBooleanProperty(), Boolean.TRUE);
  }
@@ -537,7 +535,7 @@
        "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child new");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -582,7 +580,7 @@
        "cn=test child new,cn=test children,cn=test parent 2,cn=test parents,cn=config");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child new");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
    c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -808,14 +806,14 @@
  @Test
  public void testAddConstraintSuccess() throws Exception {
    Constraint constraint = new MockConstraint(true, false, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
          "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
      c.importLDIF(TEST_LDIF);
      c.addExpectedAttribute("cn", "test child new");
      c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
      c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
      c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
      c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -833,7 +831,7 @@
      c.assertEntryIsCreated();
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
@@ -849,14 +847,14 @@
  @Test(expectedExceptions=OperationRejectedException.class)
  public void testAddConstraintFail() throws Exception {
    Constraint constraint = new MockConstraint(false, true, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
          "cn=test child new,cn=test children,cn=test parent 1,cn=test parents,cn=config");
      c.importLDIF(TEST_LDIF);
      c.addExpectedAttribute("cn", "test child new");
      c.addExpectedAttribute("objectclass", "top", "ds-cfg-virtual-attribute");
      c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child-dummy");
      c.addExpectedAttribute("ds-cfg-virtual-attribute-enabled", "true");
      c.addExpectedAttribute("ds-cfg-virtual-attribute-class",
          "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
@@ -873,7 +871,7 @@
      Assert.fail("The add constraint failed to prevent creation of the managed object");
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
@@ -889,7 +887,7 @@
  @Test
  public void testRemoveConstraintSuccess() throws Exception {
    Constraint constraint = new MockConstraint(false, false, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      DeleteSubtreeMockLDAPConnection c = new DeleteSubtreeMockLDAPConnection(
@@ -901,7 +899,7 @@
      c.assertSubtreeIsDeleted();
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
@@ -917,7 +915,7 @@
  @Test(expectedExceptions=OperationRejectedException.class)
  public void testRemoveConstraintFail() throws Exception {
    Constraint constraint = new MockConstraint(true, true, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      DeleteSubtreeMockLDAPConnection c = new DeleteSubtreeMockLDAPConnection(
@@ -929,7 +927,7 @@
      Assert.fail("The remove constraint failed to prevent removal of the managed object");
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
@@ -945,7 +943,7 @@
  @Test
  public void testModifyConstraintSuccess() throws Exception {
    Constraint constraint = new MockConstraint(false, true, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      ModifyEntryMockLDAPConnection c = new ModifyEntryMockLDAPConnection(
@@ -960,7 +958,7 @@
      Assert.assertTrue(c.isEntryModified());
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
@@ -976,7 +974,7 @@
  @Test(expectedExceptions = OperationRejectedException.class)
  public void testModifyConstraintFail() throws Exception {
    Constraint constraint = new MockConstraint(true, false, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      ModifyEntryMockLDAPConnection c = new ModifyEntryMockLDAPConnection(
@@ -992,7 +990,7 @@
          .fail("The modify constraint failed to prevent modification of the managed object");
    } finally {
      // Clean up.
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
    }
  }
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/AggregationTest.java
New file
@@ -0,0 +1,377 @@
/*
 * 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.server;
import java.util.Collection;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.naming.ldap.LdapName;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
 * Test cases for aggregations on the server-side.
 */
@Test(sequential=true)
public final class AggregationTest extends AdminTestCase {
  // Test child 1 LDIF.
  private static final String[] TEST_CHILD_1 = new String[] {
      "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real"
  };
  // Assert that the values of child 1 are correct.
  private void assertChild1(TestChildCfg child) {
    Assert.assertEquals(child.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeType("description"));
    assertSetEquals(child.getAggregationProperty(), new String[0]);
  }
  // Test child 2 LDIF.
  private static final String[] TEST_CHILD_2 = new String[] {
      "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config"
  };
  // Assert that the values of child 2 are correct.
  private void assertChild2(TestChildCfg child) {
    Assert.assertEquals(child.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeType("description"));
    // Test normalization.
    assertSetEquals(child.getAggregationProperty(), "LDAP Connection Handler");
    assertSetEquals(child.getAggregationProperty(),
        "  LDAP   Connection  Handler ");
    assertSetEquals(child.getAggregationProperty(),
        "  ldap connection HANDLER ");
  }
  // Test child 3 LDIF (invalid reference).
  private static final String[] TEST_CHILD_3 = new String[] {
      "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 3",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=bad rdn, cn=config"
  };
  // Test child 4 LDIF.
  private static final String[] TEST_CHILD_4 = new String[] {
      "dn: cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 4",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "ds-cfg-backend-base-dn: cn=LDAP Connection Handler, cn=connection handlers, cn=config",
      "ds-cfg-backend-base-dn: cn=LDAPS Connection Handler, cn=connection handlers, cn=config"
  };
  // Assert that the values of child 4 are correct.
  private void assertChild4(TestChildCfg child) {
    Assert.assertEquals(child.getMandatoryClassProperty(),
        "org.opends.server.extensions.UserDefinedVirtualAttributeProvider");
    Assert.assertEquals(child.getMandatoryReadOnlyAttributeTypeProperty(),
        DirectoryServer.getAttributeType("description"));
    assertSetEquals(child.getAggregationProperty(), "LDAPS Connection Handler",
        "LDAP Connection Handler");
  }
  // Test LDIF.
  private static final String[] TEST_LDIF = new String[] {
      // Base entries.
      "dn: cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: test parents",
      "",
      // Parent 1.
      "dn: cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "",
      // Child base entries.
      "dn:cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-branch",
      "cn: test children",
      ""
  };
  // JNDI LDAP context.
  private JNDIDirContextAdaptor adaptor = null;
  /**
   * Sets up tests
   *
   * @throws Exception
   *           If the server could not be initialized.
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    TestCfg.setUp();
    // Add test managed objects.
    TestCaseUtils.addEntries(TEST_LDIF);
  }
  /**
   * Tears down test environment.
   *
   * @throws Exception
   *           If the test entries could not be removed.
   */
  @AfterClass
  public void tearDown() throws Exception {
    TestCfg.cleanup();
    // Remove test entries.
    deleteSubtree("cn=test parents,cn=config");
  }
  /**
   * Tests that aggregation contains no values when it
   * contains does not contain any DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationEmpty() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_1);
    try {
      TestParentCfg parent = getParent("test parent 1");
      assertChild1(parent.getTestChild("test child 1"));
    } finally {
      deleteSubtree("cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }
  /**
   * Tests that aggregation contains single valid value when it
   * contains a single valid DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationSingle() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_2);
    try {
      TestParentCfg parent = getParent("test parent 1");
      assertChild2(parent.getTestChild("test child 2"));
    } finally {
      deleteSubtree("cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }
  /**
   * Tests that aggregation is rejected when the LDAP DN contains a
   * valid RDN but an invalid parent DN.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationBadBaseDN() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_3);
    try {
      TestParentCfg parent = getParent("test parent 1");
      parent.getTestChild("test child 3");
      Assert
          .fail("Unexpectedly added test child 3 when it had a bad aggregation value");
    } catch (ConfigException e) {
      // Check that we have a decoding exception as the cause and
      // there was only one cause the illegal property value.
      Throwable cause = e.getCause();
      if (cause instanceof ServerManagedObjectDecodingException) {
        ServerManagedObjectDecodingException de = (ServerManagedObjectDecodingException) cause;
        Collection<PropertyException> causes = de.getCauses();
        Assert.assertEquals(causes.size(), 1);
        cause = causes.iterator().next();
        if (cause instanceof IllegalPropertyValueStringException) {
          IllegalPropertyValueStringException pe = (IllegalPropertyValueStringException) cause;
          Assert.assertEquals(pe.getPropertyDefinition(), TestChildCfgDefn
              .getInstance().getAggregationPropertyPropertyDefinition());
          Assert.assertEquals(pe.getIllegalValueString(),
              "cn=LDAP Connection Handler, cn=bad rdn, cn=config");
        } else {
          // Got an unexpected cause.
          throw e;
        }
      } else {
        // Got an unexpected cause.
        throw e;
      }
    } finally {
      deleteSubtree("cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }
  /**
   * Tests that aggregation contains multiple valid values when it
   * contains a multiple valid DN attribute values.
   *
   * @throws Exception
   *           If the test unexpectedly fails.
   */
  @Test
  public void testAggregationMultipleValues() throws Exception {
    // Add the entry.
    TestCaseUtils.addEntry(TEST_CHILD_4);
    try {
      TestParentCfg parent = getParent("test parent 1");
      assertChild4(parent.getTestChild("test child 4"));
    } finally {
      deleteSubtree("cn=test child 4,cn=test children,cn=test parent 1,cn=test parents,cn=config");
    }
  }
  // Asserts that the actual set of DNs contains the expected values.
  private void assertSetEquals(SortedSet<String> actual, String... expected) {
    SortedSet<String> values = new TreeSet<String>(TestChildCfgDefn
        .getInstance().getAggregationPropertyPropertyDefinition());
    if (expected != null) {
      for (String value : expected) {
        values.add(value);
      }
    }
    Assert.assertEquals((Object) actual, (Object) values);
  }
  // Deletes the named sub-tree.
  private void deleteSubtree(String dn) throws Exception {
    getAdaptor().deleteSubtree(new LdapName(dn));
  }
  // Gets the JNDI connection for the test server instance.
  private synchronized JNDIDirContextAdaptor getAdaptor() throws Exception {
    if (adaptor == null) {
      adaptor = JNDIDirContextAdaptor.simpleBind("127.0.0.1", TestCaseUtils
          .getServerLdapPort(), "cn=directory manager", "password");
    }
    return adaptor;
  }
  // Gets the named parent configuration.
  private TestParentCfg getParent(String name) throws IllegalArgumentException,
      ConfigException {
    ServerManagementContext ctx = ServerManagementContext.getInstance();
    ServerManagedObject<RootCfg> root = ctx.getRootConfigurationManagedObject();
    TestParentCfg parent = root.getChild(
        TestCfg.getTestOneToManyParentRelationDefinition(), name)
        .getConfiguration();
    return parent;
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ConstraintTest.java
@@ -36,11 +36,8 @@
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.std.server.RootCfg;
@@ -153,7 +150,7 @@
  private static final String[] TEST_CHILD_1 = new String[] {
      "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -173,7 +170,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -204,7 +201,7 @@
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
    TestCfg.setUp();
    // Add test managed objects.
    TestCaseUtils.addEntries(TEST_LDIF);
@@ -220,7 +217,6 @@
   */
  @AfterClass
  public void tearDown() throws Exception {
    LDAPProfile.getInstance().popWrapper();
    TestCfg.cleanup();
    // Remove test entries.
@@ -242,7 +238,7 @@
    parent.addTestChildAddListener(listener);
    MockConstraint constraint = new MockConstraint(true, false, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      try {
@@ -256,7 +252,7 @@
        }
      }
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      parent.removeTestChildAddListener(listener);
    }
  }
@@ -276,7 +272,7 @@
    parent.addTestChildAddListener(listener);
    MockConstraint constraint = new MockConstraint(false, true, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      try {
@@ -290,7 +286,7 @@
        }
      }
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      parent.removeTestChildAddListener(listener);
    }
  }
@@ -310,7 +306,7 @@
    parent.addTestChildDeleteListener(listener);
    MockConstraint constraint = new MockConstraint(false, false, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      // Add the entry.
@@ -319,7 +315,7 @@
      // Now delete it - this should trigger the constraint.
      deleteSubtree(TEST_CHILD_1_DN);
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      parent.removeTestChildDeleteListener(listener);
      try {
@@ -346,7 +342,7 @@
    parent.addTestChildDeleteListener(listener);
    MockConstraint constraint = new MockConstraint(true, true, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      // Add the entry.
@@ -361,7 +357,7 @@
        // Ignore - this is the expected exception.
      }
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      parent.removeTestChildDeleteListener(listener);
      try {
@@ -386,7 +382,7 @@
    TestParentCfg parent = getParent("test parent 1");
    MockConstraint constraint = new MockConstraint(false, true, false);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      // Add the entry.
@@ -412,7 +408,7 @@
      int result = TestCaseUtils.applyModifications(changes);
      Assert.assertEquals(result, ResultCode.SUCCESS.getIntValue());
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      try {
        deleteSubtree(TEST_CHILD_1_DN);
      } catch (Exception e) {
@@ -434,7 +430,7 @@
    TestParentCfg parent = getParent("test parent 1");
    MockConstraint constraint = new MockConstraint(true, false, true);
    TestChildCfgDefn.getInstance().addConstraint(constraint);
    TestCfg.addConstraint(constraint);
    try {
      // Add the entry.
@@ -461,7 +457,7 @@
      Assert
          .assertEquals(result, ResultCode.UNWILLING_TO_PERFORM.getIntValue());
    } finally {
      TestChildCfgDefn.getInstance().removeConstraint(constraint);
      TestCfg.removeConstraint(constraint);
      try {
        deleteSubtree(TEST_CHILD_1_DN);
      } catch (Exception e) {
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DNBuilderTest.java
@@ -36,7 +36,6 @@
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.TestCfg;
@@ -67,7 +66,7 @@
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
    TestCfg.setUp();
  }
@@ -77,7 +76,6 @@
   */
  @AfterClass
  public void tearDown() {
    LDAPProfile.getInstance().popWrapper();
    TestCfg.cleanup();
  }
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/DefaultBehaviorTest.java
@@ -33,11 +33,9 @@
import javax.naming.ldap.LdapName;
import org.opends.server.TestCaseUtils;
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestChildCfg;
import org.opends.server.admin.TestParentCfg;
@@ -181,7 +179,7 @@
  private static final String[] TEST_CHILD_1 = new String[] {
      "dn: cn=test child 1,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -193,7 +191,7 @@
  private static final String[] TEST_CHILD_2 = new String[] {
      "dn: cn=test child 2,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -207,7 +205,7 @@
  private static final String[] TEST_CHILD_3 = new String[] {
      "dn: cn=test child 3,cn=test children,cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 3",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -223,7 +221,7 @@
  private static final String[] TEST_CHILD_4 = new String[] {
      "dn: cn=test child 4,cn=test children,cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-child-dummy",
      "cn: test child 4",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -243,7 +241,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 1,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 1",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -254,7 +252,7 @@
      // optional-multi-valued-dn-property.
      "dn: cn=test parent 2,cn=test parents,cn=config",
      "objectclass: top",
      "objectclass: ds-cfg-virtual-attribute",
      "objectclass: ds-cfg-test-parent-dummy",
      "cn: test parent 2",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions.UserDefinedVirtualAttributeProvider",
@@ -292,7 +290,7 @@
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
    TestCfg.setUp();
    // Add test managed objects.
    TestCaseUtils.addEntries(TEST_LDIF);
@@ -308,7 +306,6 @@
   */
  @AfterClass
  public void tearDown() throws Exception {
    LDAPProfile.getInstance().popWrapper();
    TestCfg.cleanup();
    // Remove test entries.
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/server/ListenerTest.java
@@ -32,11 +32,9 @@
import java.util.List;
import org.opends.server.TestCaseUtils;
import org.opends.messages.Message;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.AdminTestCase;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.TestCfg;
import org.opends.server.admin.TestParentCfg;
import org.opends.server.admin.std.server.RootCfg;
@@ -125,7 +123,7 @@
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    LDAPProfile.getInstance().pushWrapper(new MockLDAPProfile());
    TestCfg.setUp();
  }
@@ -135,7 +133,6 @@
   */
  @AfterClass
  public void tearDown() {
    LDAPProfile.getInstance().popWrapper();
    TestCfg.cleanup();
  }