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

matthew_swift
28.31.2007 45359adc09ac1d9e48206c549e667ed6965c7cd3
Fix the following issues:

* 1444: implement inherited default values
* 1445: prevent commit if mandatory properties are missing
* 1446: refactor managed object factory method API in generated APIs
* 1625: allow read-only properties to be defined during managed object construction and add support for "monitoring" properties (e.g. a property which is server generated and contains the list of support ciphers)

These issues have been fixed in a single change as they depend upon each (issue 1446 being the main issue).

[Reviewed by Josu]
6 files deleted
2 files added
24 files modified
4503 ■■■■ changed files
opends/resource/admin/admin.xsd 80 ●●●● patch | view | raw | blame | history
opends/resource/admin/clientMO.xsl 77 ●●●●● patch | view | raw | blame | history
opends/resource/admin/metaMO.xsl 125 ●●●● patch | view | raw | blame | history
opends/resource/admin/preprocessor.xsl 30 ●●●●● patch | view | raw | blame | history
opends/resource/admin/property-types.xsl 44 ●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java 5 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java 53 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/ConfigurationClient.java 32 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/DefaultBehaviorException.java 90 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/DefaultBehaviorPropertyValueException.java 93 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/InheritedDefaultValueException.java 97 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/InheritedDefaultValueProvider.java 71 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/ManagedObjectPath.java 206 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/PropertyOption.java 23 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java 80 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/StringPropertyProvider.java 88 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/InitialManagedObject.java 347 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ManagedObject.java 124 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/MissingMandatoryPropertiesException.java 120 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/PropertySet.java 584 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java 799 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java 723 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java 20 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/RelativeInheritedDefaultBehaviorProviderTest.java 63 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfg.java 18 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgClient.java 42 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java 117 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgClient.java 35 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgDefn.java 15 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java 76 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java 115 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockPropertyProvider.java 111 ●●●●● patch | view | raw | blame | history
opends/resource/admin/admin.xsd
@@ -308,8 +308,23 @@
      <xsd:annotation>
        <xsd:documentation>
          Indicates whether or not this property is read-only. By
          default, properties are modifiable. Read-only monitoring
          information is a good use-case for read-only properties.
          default, properties are not read-only. Read-only properties
          can only be initialized during construction of the associated
          managed object and cannot be modified once the managed object
          has been created.
        </xsd:documentation>
      </xsd:annotation>
    </xsd:attribute>
    <xsd:attribute name="monitoring" type="xsd:boolean" use="optional"
      default="false">
      <xsd:annotation>
        <xsd:documentation>
          Indicates whether or not this property is read-only and
          generated automatically by the server as monitoring
          information. By default, properties are not for monitoring.
          Monitoring properties are always read-only because their
          values are generated by the server. During construction of a
          managed object their values are undefined.
        </xsd:documentation>
      </xsd:annotation>
    </xsd:attribute>
@@ -746,6 +761,25 @@
                    </xsd:restriction>
                  </xsd:simpleType>
                </xsd:attribute>
                <xsd:attribute name="managed-object-name"
                  type="tns:name-type" use="required">
                  <xsd:annotation>
                    <xsd:documentation>
                      The type of managed object providing the default
                      values.
                    </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 managed object
                      definition if it is not the same as this managed
                      object's package.
                    </xsd:documentation>
                  </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="property-name" type="tns:name-type"
                  use="required">
                  <xsd:annotation>
@@ -765,16 +799,15 @@
                </xsd:documentation>
              </xsd:annotation>
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name="path">
                    <xsd:annotation>
                      <xsd:documentation>
                        TODO: not sure what form this will take.
                      </xsd:documentation>
                    </xsd:annotation>
                    <xsd:complexType />
                  </xsd:element>
                </xsd:sequence>
                <xsd:attribute name="path" type="tns:path-type"
                  use="required">
                  <xsd:annotation>
                    <xsd:documentation>
                      The location of the managed object containing the
                      default values.
                    </xsd:documentation>
                  </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="property-name" type="tns:name-type"
                  use="required">
                  <xsd:annotation>
@@ -1237,19 +1270,28 @@
    <xsd:annotation>
      <xsd:documentation>
        A managed object path which can be used to specify the location
        of refererenced managed objects. A managed object path has a
        of referenced managed objects. A managed object path has a
        similar syntax to unix file system paths, and comprises of zero
        or more path elements separated by a forward slash "/". The root
        configuration is referenced using the path "/". Subsequent path
        elements name either a relation or its subordinate named managed
        object (for one-to-many relations). For example, the path
        "/connection-handlers/LDAP connection handler" references the
        LDAP connection handler managed object referenced by the root
        configuration's connection-handlers relation.
        elements identify subordinate managed objects. Each path element
        is comprised of the relation name, an optional definition name,
        and the name of the managed object instance if the relation is
        one-to-many. The path "/relation=connection-handler+name=my
        handler" identifies a connection handler called "my handler"
        where "my handler" can be any type of connection handler. If "my
        handler" must be an LDAP connection handler then the type needs
        to be specified in the path:
        "/relation=connection-handler+type=ldap-connection-handler+name=my
        handler". The global configuration is identified by the path
        "/relation=global-configuration" (no name is required because
        the relation is one-to-one).
      </xsd:documentation>
    </xsd:annotation>
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="/([^/]+(/[^/]+)*)?"></xsd:pattern>
      <xsd:pattern
        value="/|(/relation=[^/+]+(\+type=[^/+]+)?(\+name=[^/]+)?)+">
      </xsd:pattern>
    </xsd:restriction>
  </xsd:simpleType>
  <xsd:complexType name="profile-type">
opends/resource/admin/clientMO.xsl
@@ -176,34 +176,26 @@
          <xsl:with-param name="indent" select="2" />
          <xsl:with-param name="content"
            select="concat(
                       'Creates the ', $ufn,' if it does not exist yet.&#xa;',
                       'Creates a new ', $ufn,'. The new ', $ufn,' will initially ',
                       'not contain any property values (including mandatory ',
                       'properties). Once the ', $ufn,' has been configured it ',
                       'can be added to the server using the {@link #commit()} ',
                       'method.&#xa;',
                       '&#xa;',
                       '@param &lt;C&gt;&#xa;',
                       '         The type of the ', $ufn,' being added.&#xa;',
                       '         The type of the ', $ufn,' being created.&#xa;',
                       '@param d&#xa;',
                       '         The definition of the ', $ufn,' to be created.&#xa;',
                       '@param p&#xa;',
                       '         A property provider which can be used to initialize the property values of the new ', $ufn,'.&#xa;',
                       '@return Returns the ', $ufn,' instance representing the ', $ufn,' that was created.&#xa;',
                       '@throws ManagedObjectDecodingException&#xa;',
                       '          If the ', $ufn,' could not be created because one or more of its properties are invalid.&#xa;',
                       '@throws ManagedObjectAlreadyExistsException&#xa;',
                       '          If the ', $ufn,' cannot be created because it already exists on the server.&#xa;',
                       '@throws ConcurrentModificationException&#xa;',
                       '          If this ', $ufn,' has been removed from the server by another client.&#xa;',
                       '@throws OperationRejectedException&#xa;',
                       '          If the server refuses to create the ', $ufn,' due to some server-side constraint which cannot be satisfied.&#xa;',
                       '@throws AuthorizationException&#xa;',
                       '          If the server refuses to create the ', $ufn,' because the client does not have the correct privileges.&#xa;',
                       '@throws CommunicationException&#xa;',
                       '          If the client cannot contact the server due to an underlying communication problem.&#xa;')" />
                       '@param exceptions&#xa;',
                       '         An optional collection in which to place any ',
                       '{@link DefaultBehaviorException}s that occurred whilst ',
                       'attempting to determine the default values of the ', $ufn,
                       '. This argument can be &lt;code&gt;null&lt;code&gt;.&#xa;',
                       '@return Returns a new ', $ufn,' configuration instance.&#xa;')" />
        </xsl:call-template>
        <xsl:value-of
          select="concat('  &lt;C extends ', $java-class-name,'CfgClient&gt; C create', $java-relation-name, '(&#xa;',
                           '      ManagedObjectDefinition&lt;C, ?&gt; d, PropertyProvider p)&#xa;',
                           '      throws ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,&#xa;',
                           '      ConcurrentModificationException, OperationRejectedException,&#xa;',
                           '      AuthorizationException, CommunicationException;&#xa;')" />
                           '      ManagedObjectDefinition&lt;C, ?&gt; d, Collection&lt;DefaultBehaviorException&gt; exceptions);&#xa;')" />
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -297,36 +289,28 @@
          <xsl:with-param name="indent" select="2" />
          <xsl:with-param name="content"
            select="concat(
                       'Creates a new ', $ufn,'.&#xa;',
                       'Creates a new ', $ufn,'. The new ', $ufn,' will initially ',
                       'not contain any property values (including mandatory ',
                       'properties). Once the ', $ufn,' has been configured it ',
                       'can be added to the server using the {@link #commit()} ',
                       'method.&#xa;',
                       '&#xa;',
                       '@param &lt;C&gt;&#xa;',
                       '         The type of the ', $ufn,' being added.&#xa;',
                       '         The type of the ', $ufn,' being created.&#xa;',
                       '@param d&#xa;',
                       '         The definition of the ', $ufn,' to be created.&#xa;',
                       '@param name&#xa;',
                       '         The name of the new ', $ufn,'.&#xa;',
                       '@param p&#xa;',
                       '         A property provider which can be used to initialize the property values of the new ', $ufn,'.&#xa;',
                       '@return Returns a new ', $ufn,' instance representing the ', $ufn,' that was created.&#xa;',
                       '@throws ManagedObjectDecodingException&#xa;',
                       '          If the ', $ufn,' could not be created because one or more of its properties are invalid.&#xa;',
                       '@throws ManagedObjectAlreadyExistsException&#xa;',
                       '          If the ', $ufn,' cannot be created because it already exists on the server.&#xa;',
                       '@throws ConcurrentModificationException&#xa;',
                       '          If this ', $ufn,' has been removed from the server by another client.&#xa;',
                       '@throws OperationRejectedException&#xa;',
                       '          If the server refuses to create the ', $ufn,' due to some server-side constraint which cannot be satisfied.&#xa;',
                       '@throws AuthorizationException&#xa;',
                       '          If the server refuses to create the ', $ufn,' because the client does not have the correct privileges.&#xa;',
                       '@throws CommunicationException&#xa;',
                       '          If the client cannot contact the server due to an underlying communication problem.&#xa;')" />
                       '@param exceptions&#xa;',
                       '         An optional collection in which to place any ',
                       '{@link DefaultBehaviorException}s that occurred whilst ',
                       'attempting to determine the default values of the ', $ufn,
                       '. This argument can be &lt;code&gt;null&lt;code&gt;.&#xa;',
                       '@return Returns a new ', $ufn,' configuration instance.&#xa;')" />
        </xsl:call-template>
        <xsl:value-of
          select="concat('  &lt;C extends ', $java-class-name,'CfgClient&gt; C create', $java-relation-name, '(&#xa;',
                           '      ManagedObjectDefinition&lt;C, ?&gt; d, String name, PropertyProvider p)&#xa;',
                           '      throws ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,&#xa;',
                           '      ConcurrentModificationException, OperationRejectedException,&#xa;',
                           '      AuthorizationException, CommunicationException;&#xa;')" />
                           '      ManagedObjectDefinition&lt;C, ?&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions);&#xa;')" />
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -382,11 +366,14 @@
          <import>java.util.Collection</import>
          <import>java.util.SortedSet</import>
        </xsl:if>
        <xsl:if test="$this-local-properties[not(@read-only='true')]">
        <xsl:if test="$this-local-properties[not(@monitoring='true')]">
          <import>
            org.opends.server.admin.IllegalPropertyValueException
          </import>
        </xsl:if>
        <xsl:if test="$this-local-properties[@read-only='true']">
          <import>org.opends.server.admin.PropertyIsReadOnlyException</import>
        </xsl:if>
        <xsl:if test="$this-local-relations">
          <import>
            org.opends.server.admin.DefinitionDecodingException
@@ -409,9 +396,9 @@
        </xsl:if>
        <xsl:if
          test="$this-local-relations/adm:one-to-zero-or-one|$this-local-relations/adm:one-to-many">
          <import>org.opends.server.admin.PropertyProvider</import>
          <import>java.util.Collection</import>
          <import>
            org.opends.server.admin.ManagedObjectAlreadyExistsException
            org.opends.server.admin.DefaultBehaviorException
          </import>
          <import>
            org.opends.server.admin.client.OperationRejectedException
opends/resource/admin/metaMO.xsl
@@ -387,7 +387,8 @@
      select="concat('    /**&#xa;',
                     '     * {@inheritDoc}&#xa;',
                     '     */&#xa;',
                     '    public void commit() throws ConcurrentModificationException,&#xa;',
                     '    public void commit() throws ManagedObjectAlreadyExistsException,&#xa;',
                     '        MissingMandatoryPropertiesException, ConcurrentModificationException,&#xa;',
                     '        OperationRejectedException, AuthorizationException,&#xa;',
                     '        CommunicationException {&#xa;',
                     '      impl.commit();&#xa;',
@@ -568,29 +569,33 @@
          select="concat('      ', $type, '.Builder builder = ', $type, '.createBuilder(INSTANCE, &quot;',@name, '&quot;);&#xa;')" />
      </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="string(@multi-valued) = 'true'">
    <xsl:if test="@multi-valued='true'">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.MULTI_VALUED);&#xa;'" />
    </xsl:if>
    <xsl:if test="string(@read-only) = 'true'">
    <xsl:if test="@read-only='true'">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.READ_ONLY);&#xa;'" />
    </xsl:if>
    <xsl:if test="@monitoring='true'">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.MONITORING);&#xa;'" />
    </xsl:if>
    <xsl:if
      test="adm:requires-admin-action/adm:server-restart|adm:requires-admin-action/adm:component-restart|adm:requires-admin-action/adm:other">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.REQUIRES_ADMIN_ACTION);&#xa;'" />
    </xsl:if>
    <xsl:if test="string(@mandatory) = 'true'">
    <xsl:if test="@mandatory='true'">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.MANDATORY);&#xa;'" />
    </xsl:if>
    <xsl:if test="string(@hidden) = 'true'">
    <xsl:if test="@hidden='true'">
      <xsl:value-of
        select="'      builder.setOption(PropertyOption.HIDDEN);&#xa;'" />
    </xsl:if>
    <xsl:choose>
      <xsl:when test="string(@mandatory) = 'true'">
      <xsl:when test="@mandatory='true'">
        <xsl:value-of
          select="concat('      builder.setDefaultBehaviorProvider(new UndefinedDefaultBehaviorProvider&lt;', $value-type,'&gt;());&#xa;')" />
      </xsl:when>
@@ -629,19 +634,37 @@
          </xsl:when>
          <xsl:when
            test="adm:default-behavior/adm:inherited/adm:relative">
            <xsl:message terminate="yes">
              <xsl:value-of
                select="concat('Relative inherited property defaults not yet implemented (property &quot;', @name,
                         '&quot;).')" />
            </xsl:message>
            <xsl:value-of
              select="concat('      DefaultBehaviorProvider&lt;', $value-type,'&gt; provider = ',
                             'new RelativeInheritedDefaultBehaviorProvider&lt;', $value-type,'&gt;(')" />
            <xsl:variable name="managed-object-name">
              <xsl:call-template name="name-to-java">
                <xsl:with-param name="value"
                  select="adm:default-behavior/adm:inherited/adm:relative/@managed-object-name" />
              </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="property-name"
              select="adm:default-behavior/adm:inherited/adm:relative/@property-name" />
            <xsl:variable name="offset"
              select="adm:default-behavior/adm:inherited/adm:relative/@offset" />
            <xsl:value-of
              select="concat($managed-object-name, 'CfgDefn.getInstance(), &quot;', $property-name, '&quot;, ', $offset, ');&#xa;')" />
            <xsl:value-of
              select="'      builder.setDefaultBehaviorProvider(provider);&#xa;'" />
          </xsl:when>
          <xsl:when
            test="adm:default-behavior/adm:inherited/adm:absolute">
            <xsl:message terminate="yes">
              <xsl:value-of
                select="concat('Absolute inherited property defaults not yet implemented (property &quot;', @name,
                         '&quot;).')" />
            </xsl:message>
            <xsl:value-of
              select="concat('      DefaultBehaviorProvider&lt;', $value-type,'&gt; provider = ',
                             'new AbsoluteInheritedDefaultBehaviorProvider&lt;', $value-type,'&gt;(')" />
            <xsl:variable name="property-name"
              select="adm:default-behavior/adm:inherited/adm:absolute/@property-name" />
            <xsl:variable name="path"
              select="adm:default-behavior/adm:inherited/adm:absolute/@path" />
            <xsl:value-of
              select="concat('ManagedObjectPath.valueOf(&quot;', $path, '&quot;), &quot;', $property-name, '&quot;);&#xa;')" />
            <xsl:value-of
              select="'      builder.setDefaultBehaviorProvider(provider);&#xa;'" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:message terminate="yes">
@@ -1017,7 +1040,7 @@
    Generate a property value setter.
  -->
  <xsl:template name="generate-property-setter">
    <xsl:if test="string(@read-only) != 'true'">
    <xsl:if test="not(@monitoring='true')">
      <xsl:variable name="java-prop-name">
        <xsl:call-template name="name-to-java">
          <xsl:with-param name="value" select="@name" />
@@ -1031,9 +1054,9 @@
                     $java-prop-name ,
                     '(')" />
      <xsl:choose>
        <xsl:when test="string(@multi-valued) != 'true'">
        <xsl:when test="not(@multi-valued='true')">
          <xsl:choose>
            <xsl:when test="@mandatory = 'true'">
            <xsl:when test="@mandatory='true'">
              <xsl:call-template
                name="get-property-java-primitive-type" />
            </xsl:when>
@@ -1041,8 +1064,12 @@
              <xsl:call-template name="get-property-java-type" />
            </xsl:otherwise>
          </xsl:choose>
          <xsl:value-of select="' value)'" />
          <xsl:if test="@read-only='true'">
            <xsl:value-of select="' throws PropertyIsReadOnlyException'" />
          </xsl:if>
          <xsl:value-of
            select="concat(' value) {&#xa;' ,
            select="concat(' {&#xa;' ,
                     '      impl.setPropertyValue(INSTANCE.get',
                     $java-prop-name ,
                     'PropertyDefinition(), value);&#xa;',
@@ -1119,11 +1146,8 @@
                         '     * {@inheritDoc}&#xa;',
                         '     */&#xa;',
                         '    public &lt;M extends ', $java-class-name, 'CfgClient&gt; M create', $java-relation-name, '(&#xa;',
                         '        ManagedObjectDefinition&lt;M, ?&gt; d, PropertyProvider p)&#xa;',
                         '        throws ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,&#xa;',
                         '        ConcurrentModificationException, OperationRejectedException,&#xa;',
                         '        AuthorizationException, CommunicationException {&#xa;',
                         '      return impl.createChild(INSTANCE.get', $java-relation-name,'RelationDefinition(), d, p).getConfiguration();&#xa;',
                         '        ManagedObjectDefinition&lt;M, ?&gt; d, Collection&lt;DefaultBehaviorException&gt; exceptions) {&#xa;',
                         '      return impl.createChild(INSTANCE.get', $java-relation-name,'RelationDefinition(), d, exceptions).getConfiguration();&#xa;',
                         '    }&#xa;')" />
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -1175,11 +1199,8 @@
                         '     * {@inheritDoc}&#xa;',
                         '     */&#xa;',
                         '    public &lt;M extends ', $java-class-name, 'CfgClient&gt; M create', $java-relation-name, '(&#xa;',
                         '        ManagedObjectDefinition&lt;M, ?&gt; d, String name, PropertyProvider p)&#xa;',
                         '        throws ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,&#xa;',
                         '        ConcurrentModificationException, OperationRejectedException,&#xa;',
                         '        AuthorizationException, CommunicationException {&#xa;',
                         '      return impl.createChild(INSTANCE.get', $java-relation-plural-name,'RelationDefinition(), d, name, p).getConfiguration();&#xa;',
                         '        ManagedObjectDefinition&lt;M, ?&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions) {&#xa;',
                         '      return impl.createChild(INSTANCE.get', $java-relation-plural-name,'RelationDefinition(), d, name, exceptions).getConfiguration();&#xa;',
                         '    }&#xa;')" />
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -1571,6 +1592,7 @@
        <xsl:if
          test="$this-local-properties[@multi-valued='true' or
                                       @read-only='true' or
                                       @monitoring='true' or
                                       @hidden='true' or
                                       @mandatory='true']">
          <import>org.opends.server.admin.PropertyOption</import>
@@ -1589,6 +1611,34 @@
          </import>
        </xsl:if>
        <xsl:if
          test="$this-local-properties/adm:default-behavior/adm:inherited/adm:absolute">
          <import>
            org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider
          </import>
          <import>org.opends.server.admin.ManagedObjectPath</import>
        </xsl:if>
        <xsl:if
          test="$this-local-properties/adm:default-behavior/adm:inherited/adm:relative">
          <import>
            org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider
          </import>
          <xsl:for-each
            select="$this-local-properties/adm:default-behavior/adm:inherited/adm:relative">
            <xsl:if test="@managed-object-package != $this-package">
              <xsl:variable name="java-class-name">
                <xsl:call-template name="name-to-java">
                  <xsl:with-param name="value"
                    select="@managed-object-name" />
                </xsl:call-template>
              </xsl:variable>
              <xsl:element name="import">
                <xsl:value-of
                  select="concat(@managed-object-package, '.meta.', $java-class-name, 'CfgDefn')" />
              </xsl:element>
            </xsl:if>
          </xsl:for-each>
        </xsl:if>
        <xsl:if
          test="$this-local-properties/adm:default-behavior/adm:defined">
          <import>
            org.opends.server.admin.DefinedDefaultBehaviorProvider
@@ -1655,6 +1705,12 @@
            </import>
            <import>org.opends.server.admin.PropertyProvider</import>
            <import>
              org.opends.server.admin.client.MissingMandatoryPropertiesException
            </import>
            <import>
              org.opends.server.admin.ManagedObjectAlreadyExistsException
            </import>
            <import>
              org.opends.server.admin.client.AuthorizationException
            </import>
            <import>
@@ -1692,8 +1748,9 @@
              </import>
            </xsl:if>
            <xsl:if test="$this-all-relations/adm:one-to-many">
              <import>java.util.Collection</import>
              <import>
                org.opends.server.admin.ManagedObjectAlreadyExistsException
                org.opends.server.admin.DefaultBehaviorException
              </import>
              <import>
                org.opends.server.admin.server.ConfigurationAddListener
@@ -1704,8 +1761,9 @@
              <import>org.opends.server.config.ConfigException</import>
            </xsl:if>
            <xsl:if test="$this-all-relations/adm:one-to-zero-or-one">
              <import>java.util.Collection</import>
              <import>
                org.opends.server.admin.ManagedObjectAlreadyExistsException
                org.opends.server.admin.DefaultBehaviorException
              </import>
              <import>
                org.opends.server.admin.server.ConfigurationAddListener
@@ -1722,6 +1780,9 @@
              <import>java.util.SortedSet</import>
              <import>java.util.Collection</import>
            </xsl:if>
            <xsl:if test="$this-all-properties[@read-only='true']">
              <import>org.opends.server.admin.PropertyIsReadOnlyException</import>
            </xsl:if>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:if test="$this/@extends">
opends/resource/admin/preprocessor.xsl
@@ -696,6 +696,36 @@
    </xsl:copy>
  </xsl:template>
  <!--
    Process a relative inherited default behavior
  -->
  <xsl:template match="adm:relative" mode="pre-process">
    <xsl:param name="mopackage" select="/.." />
    <xsl:param name="moname" select="/.." />
    <xsl:param name="hierarchy" select="/.." />
    <xsl:copy>
      <!--
        Shallow copy.
      -->
      <xsl:copy-of select="@*" />
      <!--
        Add missing attribute managed-object-package if it is not provided.
      -->
      <xsl:if test="not(@managed-object-package)">
        <xsl:attribute name="managed-object-package">
          <xsl:value-of select="$mopackage" />
        </xsl:attribute>
      </xsl:if>
      <!--
        Apply templates to subordinate elements.
      -->
      <xsl:apply-templates mode="pre-process">
        <xsl:with-param name="mopackage" select="$mopackage" />
        <xsl:with-param name="moname" select="$moname" />
        <xsl:with-param name="hierarchy" select="$hierarchy" />
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>
  <!--
    Process a user-friendly-name element.
  -->
  <xsl:template match="adm:user-friendly-name"
opends/resource/admin/property-types.xsl
@@ -270,7 +270,7 @@
    Generate the property setter declarations.
  -->
  <xsl:template name="generate-property-setter-declaration">
    <xsl:if test="string(@read-only) != 'true'">
    <xsl:if test="not(@monitoring='true')">
      <xsl:variable name="name" select="@name" />
      <xsl:variable name="java-property-name">
        <xsl:call-template name="name-to-java">
@@ -294,14 +294,28 @@
          <xsl:with-param name="content" select="adm:description" />
        </xsl:call-template>
      </xsl:if>
      <xsl:if test="@read-only='true'">
        <xsl:value-of select="'   * &lt;p&gt;&#xa;'" />
        <xsl:value-of
          select="concat(
                     '   * This property is read-only and can only be modified during&#xa;',
                     '   * creation of a ', $this-ufn, '.&#xa;')" />
      </xsl:if>
      <xsl:choose>
        <xsl:when test="string(@multi-valued) != 'true'">
        <xsl:when test="not(@multi-valued='true')">
          <xsl:value-of
            select="concat('   *&#xa;',
                     '   * @param value The value of the &quot;', $name, '&quot; property.&#xa;',
                     '   * @throws IllegalPropertyValueException&#xa;',
                     '   *           If the new value is invalid.&#xa;',
                     '   *&#xa;',
                     '   *           If the new value is invalid.&#xa;')" />
          <xsl:if test="@read-only='true'">
            <xsl:value-of
              select="concat(
                     '   * @throws PropertyIsReadOnlyException&#xa;',
                     '   *           If this ', $this-ufn, ' is not being initialized.&#xa;')" />
          </xsl:if>
          <xsl:value-of
            select="concat(
                     '   */&#xa;',
                     '  void set', $java-property-name, '(')" />
          <xsl:choose>
@@ -313,23 +327,33 @@
              <xsl:call-template name="get-property-java-type" />
            </xsl:otherwise>
          </xsl:choose>
          <xsl:value-of
            select="' value) throws IllegalPropertyValueException;&#xa;'" />
          <xsl:value-of select="' value'" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of
            select="concat('   *&#xa;',
                     '   * @param values The values of the &quot;', $name, '&quot; property.&#xa;',
                     '   * @throws IllegalPropertyValueException&#xa;',
                     '   *           If one or more of the new values are invalid.&#xa;',
                     '   *&#xa;',
                     '   *           If one or more of the new values are invalid.&#xa;')" />
          <xsl:if test="@read-only='true'">
            <xsl:value-of
              select="concat(
                     '   * @throws PropertyIsReadOnlyException&#xa;',
                     '   *           If this ', $this-ufn, ' is not being initialized.&#xa;')" />
          </xsl:if>
          <xsl:value-of
            select="concat(
                     '   */&#xa;',
                     '  void set', $java-property-name, '(Collection&lt;')" />
          <xsl:call-template name="get-property-java-type" />
          <xsl:value-of
            select="'&gt; values) throws IllegalPropertyValueException;&#xa;'" />
          <xsl:value-of select="'&gt; values'" />
        </xsl:otherwise>
      </xsl:choose>
      <xsl:value-of select="') throws IllegalPropertyValueException'" />
      <xsl:if test="@read-only='true'">
        <xsl:value-of select="', PropertyIsReadOnlyException'" />
      </xsl:if>
      <xsl:value-of select="';&#xa;'" />
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java
@@ -32,7 +32,6 @@
import javax.naming.ldap.InitialLdapContext;
import org.opends.server.admin.client.InitialManagedObject;
import org.opends.server.admin.client.ManagementContext;
import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor;
import org.opends.server.admin.client.ldap.LDAPManagementContext;
@@ -121,9 +120,7 @@
      if (backend == null)
      {
        BackendCfgDefn provider = BackendCfgDefn.getInstance();
        backend = root.createBackend(provider, backendName,
            new InitialManagedObject<BackendCfgClient>(
                BackendCfgDefn.getInstance()));
        backend = root.createBackend(provider, backendName, null);
      }
      SortedSet<DN> suffixes = backend.getBackendBaseDN();
      if (suffixes == null)
opends/src/server/org/opends/server/admin/AbsoluteInheritedDefaultBehaviorProvider.java
@@ -29,9 +29,10 @@
/**
 * A default behavior provider which retrieves default values from a managed
 * object in an abolute location. It should be used by properties which inherit
 * their default value(s) from properties held in an other managed object.
 * A default behavior provider which retrieves default values from a
 * managed object in an abolute location. It should be used by
 * properties which inherit their default value(s) from properties
 * held in an other managed object.
 *
 * @param <T>
 *          The type of values represented by this provider.
@@ -48,16 +49,26 @@
  /**
   * Create an absolute inherited default behavior provider associated with the
   * managed object at the specified absolute location.
   * Create an absolute inherited default behavior provider associated
   * with the managed object at the specified absolute location.
   *
   * @param path
   *          The absolute location of the managed object.
   * @param propertyName
   *          The name of the property containing the inherited default values.
   *          The name of the property containing the inherited
   *          default values.
   * @throws IllegalArgumentException
   *           If the named property is associated with the managed
   *           object definition identified by the path.
   * @throws ClassCastException
   *           If the named property does not have the same type of
   *           property values as this default behavior provider.
   */
  @SuppressWarnings("unchecked")
  public AbsoluteInheritedDefaultBehaviorProvider(ManagedObjectPath path,
      String propertyName) {
      String propertyName) throws IllegalArgumentException, ClassCastException {
    // We do not decode the property name now because the property
    // might not have been constructed at this point.
    this.path = path;
    this.propertyName = propertyName;
  }
@@ -74,11 +85,24 @@
  /**
   * Get the absolute path of the managed object containing the property which
   * has the default values.
   * Get the definition of the parent managed object containing the
   * inherited default values.
   *
   * @return Returns the absolute path of the managed object containing the
   *         property which has the default values.
   * @return Returns the definition of the parent managed object
   *         containing the inherited default values.
   */
  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
    return path.getManagedObjectDefinition();
  }
  /**
   * Get the absolute path of the managed object containing the
   * property which has the default values.
   *
   * @return Returns the absolute path of the managed object
   *         containing the property which has the default values.
   */
  public ManagedObjectPath getManagedObjectPath() {
    return path;
@@ -87,10 +111,11 @@
  /**
   * Get the name of the property containing the inherited default values.
   * Get the name of the property containing the inherited default
   * values.
   *
   * @return Returns the name of the property containing the inherited default
   *         values.
   * @return Returns the name of the property containing the inherited
   *         default values.
   */
  public String getPropertyName() {
    return propertyName;
opends/src/server/org/opends/server/admin/ConfigurationClient.java
@@ -27,9 +27,12 @@
package org.opends.server.admin;
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.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
@@ -62,22 +65,35 @@
  /**
   * Commit any changes made to this configuration client.
   * If this is a new configuration this method will attempt to add it
   * to the server, otherwise it will commit any changes made to this
   * configuration.
   *
   * @throws ManagedObjectAlreadyExistsException
   *           If this is a new configuration but it could not be
   *           added to the server because it already exists.
   * @throws MissingMandatoryPropertiesException
   *           If this configuration contains some mandatory
   *           properties which have been left undefined.
   * @throws ConcurrentModificationException
   *           If this configuration has been removed from the server
   *           by another client.
   *           If this is a new configuration which is being added to
   *           the server but its parent has been removed by another
   *           client, or if this configuration is being modified but
   *           it has been removed from the server by another client.
   * @throws OperationRejectedException
   *           If the server refuses to apply the changes due to some
   *           server-side constraint which cannot be satisfied.
   *           If the server refuses to add or modify this
   *           configuration due to some server-side constraint which
   *           cannot be satisfied.
   * @throws AuthorizationException
   *           If the server refuses to apply the changes because the
   *           client does not have the correct privileges.
   *           If the server refuses to add or modify this
   *           configuration because the client does not have the
   *           correct privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  void commit() throws ConcurrentModificationException,
  void commit() throws ManagedObjectAlreadyExistsException,
      MissingMandatoryPropertiesException, ConcurrentModificationException,
      OperationRejectedException, AuthorizationException,
      CommunicationException;
opends/src/server/org/opends/server/admin/DefaultBehaviorException.java
New file
@@ -0,0 +1,90 @@
/*
 * 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;
/**
 * This exception is thrown when a property's default values cannot be
 * determined. This can occur in the following situations:
 * <ul>
 * <li>the property has a well-defined set of default values but they
 * are invalid according to the property's syntax
 * <li>the property inherits its default values from another managed
 * object but they could not be retrieved, perhaps because of a
 * communication problem.
 * </ul>
 */
public class DefaultBehaviorException extends PropertyException {
  /**
   * Serialization ID.
   */
  private static final long serialVersionUID = -2542117466747573053L;
  // The cause of this exception.
  private final Throwable cause;
  /**
   * Create a new default behavior exception with a cause.
   *
   * @param pd
   *          The property definition whose default values could not
   *          be determined.
   * @param cause
   *          The exception that prevented the default values from
   *          being determined.
   */
  public DefaultBehaviorException(PropertyDefinition pd, Throwable cause) {
    super(pd);
    this.cause = cause;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public Throwable getCause() {
    return cause;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getMessage() {
    return "The default values could not be determined "
        + "for the property \"" + getPropertyDefinition().getName() + "\"";
  }
}
opends/src/server/org/opends/server/admin/DefaultBehaviorPropertyValueException.java
File was deleted
opends/src/server/org/opends/server/admin/InheritedDefaultValueException.java
File was deleted
opends/src/server/org/opends/server/admin/InheritedDefaultValueProvider.java
File was deleted
opends/src/server/org/opends/server/admin/ManagedObjectPath.java
@@ -44,6 +44,64 @@
/**
 * A path which can be used to determine the location of a managed
 * object instance.
 * <p>
 * A path is made up of zero or more elements each of which represents
 * a managed object. Managed objects are arranged hierarchically with
 * the root configuration being the top-most managed object. Elements
 * are ordered such that the root configuration managed object is the
 * first element and subsequent elements representing managed objects
 * further down the hierarchy.
 * <p>
 * A path can be encoded into a string representation using the
 * {@link #toString()} and {@link #toString(StringBuilder)} methods.
 * Conversely, this string representation can be parsed using the
 * {@link #valueOf(String)} method.
 * <p>
 * The string representation of a managed object path is similar in
 * principle to a UNIX file-system path and is defined as follows:
 * <ul>
 * <li>the root element is represented by the string <code>/</code>
 * <li>subordinate elements are arranged in big-endian order
 * separated by a forward slash <code>/</code> character
 * <li>an element representing a managed object associated with a
 * one-to-one (singleton) or one-to-zero-or-one (optional) relation
 * has the form <code>relation=</code><i>relation</i>
 * <code>[+type=</code><i>definition</i><code>]</code>, where
 * <i>relation</i> is the name of the relation and <i>definition</i>
 * is the name of the referenced managed object's definition if
 * required (usually this is implied by the relation itself)
 * <li>an element representing a managed object associated with a
 * one-to-many (instantiable) relation has the form
 * <code>relation=</code><i>relation</i><code>[+type=</code>
 * <i>definition</i><code>]</code><code>+name=</code><i>name</i>,
 * where <i>relation</i> is the name of the relation and
 * <i>definition</i> is the name of the referenced managed object's
 * definition if required (usually this is implied by the relation
 * itself), and <i>name</i> is the name of the managed object
 * instance.
 * </ul>
 * The following path string representation identifies a connection
 * handler instance (note that the <code>type</code> is not
 * specified indicating that the path identifies a connection handler
 * called <i>my handler</i> which can be any type of connection
 * handler):
 *
 * <pre>
 *  /relation=connection-handler+name=my handler
 * </pre>
 *
 * If the identified connection handler must be an LDAP connection
 * handler then the above path should include the <code>type</code>:
 *
 * <pre>
 *  /relation=connection-handler+type=ldap-connection-handler+name=my handler
 * </pre>
 *
 * The final example identifies the global configuration:
 *
 * <pre>
 *  /relation=global-configuration
 * </pre>
 *
 * @param <C>
 *          The type of client managed object configuration that this
@@ -52,14 +110,14 @@
 *          The type of server managed object configuration that this
 *          path references.
 */
public final class ManagedObjectPath
    <C extends ConfigurationClient, S extends Configuration> {
public final class ManagedObjectPath<C extends ConfigurationClient,
    S extends Configuration> {
  /**
   * Abstract path element.
   */
  private static abstract class Element
      <C extends ConfigurationClient, S extends Configuration> {
  private static abstract class Element<C extends ConfigurationClient,
      S extends Configuration> {
    // The type of managed object referenced by this element.
    private final AbstractManagedObjectDefinition<C, S> definition;
@@ -92,6 +150,17 @@
    /**
     * Get the relation definition associated with this element.
     *
     * @return Returns the relation definition associated with this
     *         element.
     */
    public abstract RelationDefinition<? super C, ? super S>
        getRelationDefinition();
    /**
     * Serialize this path element using the provided serialization
     * strategy.
     *
@@ -111,8 +180,8 @@
      extends Element<C, S> {
    // Factory method.
    private static final
        <C extends ConfigurationClient, S extends Configuration>
    private static final <C extends ConfigurationClient,
        S extends Configuration>
        InstantiableElement<C, S> create(
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
@@ -142,6 +211,17 @@
     * {@inheritDoc}
     */
    @Override
    public InstantiableRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer.appendManagedObjectPathElement(r,
          getManagedObjectDefinition(), name);
@@ -158,9 +238,8 @@
      extends Element<C, S> {
    // Factory method.
    private static final
        <C extends ConfigurationClient, S extends Configuration>
        OptionalElement<C, S> create(
    private static final <C extends ConfigurationClient,
        S extends Configuration> OptionalElement<C, S> create(
        OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new OptionalElement<C, S>(r, d);
@@ -184,6 +263,17 @@
     * {@inheritDoc}
     */
    @Override
    public OptionalRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer
          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
@@ -200,9 +290,8 @@
      extends Element<C, S> {
    // Factory method.
    private static final
        <C extends ConfigurationClient, S extends Configuration>
        SingletonElement<C, S> create(
    private static final <C extends ConfigurationClient,
        S extends Configuration> SingletonElement<C, S> create(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new SingletonElement<C, S>(r, d);
@@ -227,6 +316,17 @@
     * {@inheritDoc}
     */
    @Override
    public SingletonRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer
          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
@@ -312,8 +412,8 @@
  // Single instance of a root path.
  private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH =
    new ManagedObjectPath<RootCfgClient, RootCfg>(
      new LinkedList<Element<?, ?>>(), RootCfgDefn.getInstance());
      new ManagedObjectPath<RootCfgClient, RootCfg>(
      new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance());
  // A regular expression used to parse path elements.
  private static final Pattern PE_REGEXP = Pattern
@@ -358,6 +458,7 @@
    // Parse the elements.
    LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>();
    Element<?, ?> lastElement = null;
    AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn
        .getInstance();
@@ -397,10 +498,14 @@
            + "\" in path \"" + ns + "\"");
      }
      String relation = m.group(1); // Mandatory.
      String type = m.group(3); // Optional.
      String name = m.group(5); // Mandatory if relation is
      // instantiable.
      // Mandatory.
      String relation = m.group(1);
      // Optional.
      String type = m.group(3);
      // Mandatory if relation is instantiable.
      String name = m.group(5);
      // Get the relation definition.
      RelationDefinition<?, ?> r;
@@ -413,9 +518,9 @@
      }
      // Append the next element.
      Element<?, ?> e = createElement(r, ns, es, type, name);
      elements.add(e);
      definition = e.getManagedObjectDefinition();
      lastElement = createElement(r, ns, es, type, name);
      elements.add(lastElement);
      definition = lastElement.getManagedObjectDefinition();
      // Update start to point to the beginning of the next element.
      if (end < ns.length()) {
@@ -428,7 +533,7 @@
    }
    // Construct the new path.
    return create(elements, definition);
    return create(elements, lastElement);
  }
@@ -437,9 +542,9 @@
  // construction of new paths.
  private static <C extends ConfigurationClient, S extends Configuration>
      ManagedObjectPath<C, S> create(
      LinkedList<Element<?, ?>> elements,
      AbstractManagedObjectDefinition<C, S> definition) {
    return new ManagedObjectPath<C, S>(elements, definition);
      LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) {
    return new ManagedObjectPath<C, S>(elements, lastElement
        .getRelationDefinition(), lastElement.getManagedObjectDefinition());
  }
@@ -508,19 +613,24 @@
    }
  }
  // The last element in this path.
  private final AbstractManagedObjectDefinition<C, S> definition;
  // The managed object definition in this path.
  private final AbstractManagedObjectDefinition<C, S> d;
  // The list of path elements in this path.
  private final List<Element<?, ?>> elements;
  // The last relation definition in this path.
  private final RelationDefinition<? super C, ? super S> r;
  // Private constructor.
  private ManagedObjectPath(LinkedList<Element<?, ?>> elements,
      AbstractManagedObjectDefinition<C, S> definition) {
      RelationDefinition<? super C, ? super S> r,
      AbstractManagedObjectDefinition<C, S> d) {
    this.elements = Collections.unmodifiableList(elements);
    this.definition = definition;
    this.r = r;
    this.d = d;
  }
@@ -552,7 +662,7 @@
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new InstantiableElement<M, N>(r, d, name));
    return new ManagedObjectPath<M, N>(celements, d);
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
@@ -607,7 +717,7 @@
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new OptionalElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, d);
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
@@ -628,8 +738,7 @@
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      OptionalRelationDefinition<M, N> r) {
      ManagedObjectPath<M, N> child(OptionalRelationDefinition<M, N> r) {
    return child(r, r.getChildDefinition());
  }
@@ -660,7 +769,7 @@
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new SingletonElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, d);
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
@@ -681,8 +790,7 @@
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SingletonRelationDefinition<M, N> r) {
      ManagedObjectPath<M, N> child(SingletonRelationDefinition<M, N> r) {
    return child(r, r.getChildDefinition());
  }
@@ -716,7 +824,23 @@
   *         empty.
   */
  public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
    return definition;
    return d;
  }
  /**
   * Get the relation definition of the managed object referred to by
   * this path.
   * <p>
   * When the path is empty, the <code>null</code> is returned.
   *
   * @return Returns the relation definition of the managed object
   *         referred to by this path, or the <code>null</code> if
   *         the path is empty.
   */
  public RelationDefinition<? super C, ? super S> getRelationDefinition() {
    return r;
  }
@@ -745,8 +869,8 @@
  /**
   * Creates a new parent managed object path representing the
   * immediate parent of this path. This method is a short-hand
   * for <code>parent(1)</code>.
   * immediate parent of this path. This method is a short-hand for
   * <code>parent(1)</code>.
   *
   * @return Returns a new parent managed object path representing the
   *         immediate parent of this path.
@@ -796,9 +920,7 @@
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements.subList(0, elements.size() - offset));
    AbstractManagedObjectDefinition<?, ?> definition = celements.getLast()
        .getManagedObjectDefinition();
    return create(celements, definition);
    return create(celements, celements.getLast());
  }
opends/src/server/org/opends/server/admin/PropertyOption.java
@@ -30,13 +30,13 @@
/**
 * This enumeration contains various options that can be associated with
 * property definitions.
 * This enumeration contains various options that can be associated
 * with property definitions.
 */
public enum PropertyOption {
  /**
   * Use this option to identify properties which must not be directly exposed
   * in client applications.
   * Use this option to identify properties which must not be directly
   * exposed in client applications.
   */
  HIDDEN,
@@ -51,14 +51,21 @@
  MULTI_VALUED,
  /**
   * Use this option to identify properties which cannot be modified.
   * Use this option to identify properties which can be initialized
   * once only and are read-only thereafter.
   */
  READ_ONLY,
  /**
   * Use this option to identify properties which, when modified, will require
   * some additiona administrator action in order for the changes to take
   * effect.
   * Use this option to identify properties which are for monitoring
   * purposes only and are generated automatically by the server..
   */
  MONITORING,
  /**
   * Use this option to identify properties which, when modified, will
   * require some additiona administrator action in order for the
   * changes to take effect.
   */
  REQUIRES_ADMIN_ACTION
}
opends/src/server/org/opends/server/admin/RelativeInheritedDefaultBehaviorProvider.java
@@ -29,9 +29,10 @@
/**
 * A default behavior provider which retrieves default values from a parent
 * managed object. It should be used by properties which inherit their default
 * value(s) from properties held in an other managed object.
 * A default behavior provider which retrieves default values from a
 * parent managed object. It should be used by properties which
 * inherit their default value(s) from properties held in an other
 * managed object.
 *
 * @param <T>
 *          The type of values represented by this provider.
@@ -39,8 +40,11 @@
public final class RelativeInheritedDefaultBehaviorProvider<T> implements
    DefaultBehaviorProvider<T> {
  // The relative offset (where 1 = parent, 2 = grandparent) of the managed
  // object containing the property.
  // The type of managed object expected at the relative offset.
  private final AbstractManagedObjectDefinition<?, ?> d;
  // The relative offset (where 1 = parent, 2 = grandparent) of the
  // managed object containing the property.
  private final int offset;
  // The name of the property containing the inherited default values.
@@ -49,23 +53,33 @@
  /**
   * Create a relative inherited default behavior provider associated with a
   * parent managed object.
   * Create a relative inherited default behavior provider associated
   * with a parent managed object.
   *
   * @param offset
   *          The relative location of the parent managed object (where 0 is the
   *          managed object itself, 1 is the parent, and 2 is the
   *          grand-parent).
   * @param d
   *          The type of parent managed object expected at the
   *          relative location.
   * @param propertyName
   *          The name of the property containing the inherited default values.
   *          The name of the property containing the inherited
   *          default values.
   * @param offset
   *          The relative location of the parent managed object
   *          (where 0 is the managed object itself, 1 is the parent,
   *          and 2 is the grand-parent).
   * @throws IllegalArgumentException
   *           If the offset is less than 0.
   */
  public RelativeInheritedDefaultBehaviorProvider(int offset,
      String propertyName) throws IllegalArgumentException {
  @SuppressWarnings("unchecked")
  public RelativeInheritedDefaultBehaviorProvider(
      AbstractManagedObjectDefinition<?, ?> d, String propertyName, int offset)
      throws IllegalArgumentException {
    // We do not decode the property name now because the property
    // might not have been constructed at this point (e.g. when the
    // offset is 0).
    if (offset < 0) {
      throw new IllegalArgumentException("Negative offset");
    }
    this.d = d;
    this.propertyName = propertyName;
    this.offset = offset;
  }
@@ -82,14 +96,27 @@
  /**
   * Get the absolute path of the managed object containing the property which
   * has the default values.
   * Get the definition of the parent managed object containing the
   * inherited default values.
   *
   * @return Returns the definition of the parent managed object
   *         containing the inherited default values.
   */
  public AbstractManagedObjectDefinition<?, ?> getManagedObjectDefinition() {
    return d;
  }
  /**
   * Get the absolute path of the managed object containing the
   * property which has the default values.
   *
   * @param path
   *          The path of the current managed object from which the relative
   *          path should be determined.
   * @return Returns the absolute path of the managed object containing the
   *         property which has the default values.
   *          The path of the current managed object from which the
   *          relative path should be determined.
   * @return Returns the absolute path of the managed object
   *         containing the property which has the default values.
   */
  public ManagedObjectPath getManagedObjectPath(ManagedObjectPath path) {
    return path.parent(offset);
@@ -98,10 +125,11 @@
  /**
   * Get the name of the property containing the inherited default values.
   * Get the name of the property containing the inherited default
   * values.
   *
   * @return Returns the name of the property containing the inherited default
   *         values.
   * @return Returns the name of the property containing the inherited
   *         default values.
   */
  public String getPropertyName() {
    return propertyName;
@@ -112,9 +140,9 @@
  /**
   * Get the relative location of the parent managed object.
   *
   * @return Returns the relative location of the parent managed object (where 0
   *         is the managed object itself, 1 is the parent, and 2 is the
   *         grand-parent).
   * @return Returns the relative location of the parent managed
   *         object (where 0 is the managed object itself, 1 is the
   *         parent, and 2 is the grand-parent).
   */
  public int getRelativeOffset() {
    return offset;
opends/src/server/org/opends/server/admin/StringPropertyProvider.java
File was deleted
opends/src/server/org/opends/server/admin/client/InitialManagedObject.java
File was deleted
opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -32,6 +32,7 @@
import java.util.Collection;
import java.util.SortedSet;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.InstantiableRelationDefinition;
@@ -91,27 +92,40 @@
    PropertyProvider {
  /**
   * Commit any changes made to this managed object. Pending property
   * values will be committed to the managed object. If successful,
   * the pending values will become active values.
   * Adds this managed object to the server or commits any changes
   * made to it depending on whether or not the managed object already
   * exists on the server. Pending property values will be committed
   * to the managed object. If successful, the pending values will
   * become active values.
   * <p>
   * See the class description for more information regarding pending
   * and active values.
   *
   * @throws ManagedObjectAlreadyExistsException
   *           If the managed object cannot be added to the server
   *           because it already exists.
   * @throws MissingMandatoryPropertiesException
   *           If the managed object contains some mandatory
   *           properties which have been left undefined.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   *           If the managed object is being added to the server but
   *           its parent has been removed by another client, or if
   *           this managed object is being modified but it has been
   *           removed from the server by another client.
   * @throws OperationRejectedException
   *           If the server refuses to apply the changes due to some
   *           server-side constraint which cannot be satisfied.
   *           If the server refuses to add or modify this managed
   *           object due to some server-side constraint which cannot
   *           be satisfied.
   * @throws AuthorizationException
   *           If the server refuses to apply the changes because the
   *           client does not have the correct privileges.
   *           If the server refuses to add or modify this managed
   *           object because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  void commit() throws ConcurrentModificationException,
  void commit() throws ManagedObjectAlreadyExistsException,
      MissingMandatoryPropertiesException, ConcurrentModificationException,
      OperationRejectedException, AuthorizationException,
      CommunicationException;
@@ -119,10 +133,10 @@
  /**
   * Creates a new child managed object bound to the specified
   * instantiable relation. The new managed object instance will be
   * created with values taken from a property provider. The caller
   * must make sure that the property provider provides values for
   * mandatory properties.
   * instantiable relation. The new managed object will initially not
   * contain any property values (including mandatory properties).
   * Once the managed object has been configured it can be added to
   * the server using the {@link #commit()} method.
   *
   * @param <M>
   *          The expected type of the child managed object
@@ -136,50 +150,30 @@
   *          The definition of the managed object to be created.
   * @param name
   *          The name of the child managed object.
   * @param p
   *          A property provider which should be used to initialize
   *          property values of the new managed object.
   * @param exceptions
   *          A collection in which to place any
   *          {@link DefaultBehaviorException}s that occurred whilst
   *          attempting to determine the managed object's default
   *          values.
   * @return Returns a new child managed object bound to the specified
   *         instantiable relation.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws ManagedObjectDecodingException
   *           If the managed object could not be create because one
   *           or more of its properties are invalid.
   * @throws ManagedObjectAlreadyExistsException
   *           If the managed object cannot be created because it
   *           already exists on the server.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws OperationRejectedException
   *           If the server refuses to create the managed object due
   *           to some server-side constraint which cannot be
   *           satisfied.
   * @throws AuthorizationException
   *           If the server refuses to create the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <M extends ConfigurationClient, N extends M> ManagedObject<N> createChild(
      InstantiableRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
      String name, PropertyProvider p) throws IllegalArgumentException,
      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
      ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException;
      String name, Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException;
  /**
   * Creates a new child managed object bound to the specified
   * optional relation. The new managed object instance will be
   * created with values taken from a property provider. The caller
   * must make sure that the property provider provides values for
   * mandatory properties.
   * optional relation. The new managed object will initially not
   * contain any property values (including mandatory properties).
   * Once the managed object has been configured it can be added to
   * the server using the {@link #commit()} method.
   *
   * @param <M>
   *          The expected type of the child managed object
@@ -191,41 +185,21 @@
   *          The optional relation definition.
   * @param d
   *          The definition of the managed object to be created.
   * @param p
   *          A property provider which should be used to initialize
   *          property values of the new managed object.
   * @param exceptions
   *          A collection in which to place any
   *          {@link DefaultBehaviorException}s that occurred whilst
   *          attempting to determine the managed object's default
   *          values.
   * @return Returns a new child managed object bound to the specified
   *         optional relation.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws ManagedObjectDecodingException
   *           If the managed object could not be created because one
   *           or more of its properties are invalid.
   * @throws ManagedObjectAlreadyExistsException
   *           If the managed object cannot be created because it
   *           already exists on the server.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws OperationRejectedException
   *           If the server refuses to create the managed object due
   *           to some server-side constraint which cannot be
   *           satisfied.
   * @throws AuthorizationException
   *           If the server refuses to create the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <M extends ConfigurationClient, N extends M> ManagedObject<N> createChild(
      OptionalRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
      PropertyProvider p) throws IllegalArgumentException,
      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
      ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException;
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException;
@@ -576,7 +550,8 @@
   *           If the new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If this is not a new managed object and the property is
   *           read-only or for monitoring purposes.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
@@ -611,7 +586,8 @@
   *           If an attempt was made to add multiple pending values
   *           to a single-valued property.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If this is not a new managed object and the property is
   *           read-only or for monitoring purposes.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
opends/src/server/org/opends/server/admin/client/MissingMandatoryPropertiesException.java
New file
@@ -0,0 +1,120 @@
/*
 * 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.opends.server.admin.OperationsException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.util.Validator;
/**
 * This exception is thrown when an attempt is made to add or modify a
 * managed object when one or more of its mandatory properties are
 * undefined.
 */
public class MissingMandatoryPropertiesException extends OperationsException {
  /**
   * Serialization ID.
   */
  private static final long serialVersionUID = 6342522125252055588L;
  // The causes of this exception.
  private final Collection<PropertyIsMandatoryException> causes;
  /**
   * Creates a new missing mandatory properties exception with the
   * provided causes.
   *
   * @param causes
   *          The causes of this exception (must be non-<code>null</code>
   *          and non-empty).
   */
  public MissingMandatoryPropertiesException(
      Collection<PropertyIsMandatoryException> causes) {
    Validator.ensureNotNull(causes);
    Validator.ensureTrue(!causes.isEmpty());
    this.causes = new ArrayList<PropertyIsMandatoryException>(causes);
  }
  /**
   * Gets the first exception that caused this exception.
   *
   * @return Returns the first exception that caused this exception.
   */
  @Override
  public PropertyIsMandatoryException getCause() {
    return causes.iterator().next();
  }
  /**
   * Gets an unmodifiable collection view of the causes of this
   * exception.
   *
   * @return Returns an unmodifiable collection view of the causes of
   *         this exception.
   */
  public Collection<PropertyIsMandatoryException> getCauses() {
    return Collections.unmodifiableCollection(causes);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getMessage() {
    StringBuilder builder = new StringBuilder();
    builder.append("The following properties are mandatory: ");
    boolean isFirst = true;
    for (PropertyIsMandatoryException e : causes) {
      if (!isFirst) {
        builder.append(", ");
      }
      builder.append(e.getPropertyDefinition().getName());
      isFirst = false;
    }
    return builder.toString();
  }
}
opends/src/server/org/opends/server/admin/client/PropertySet.java
@@ -32,41 +32,23 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.DefaultBehaviorPropertyValueException;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InheritedDefaultValueException;
import org.opends.server.admin.InheritedDefaultValueProvider;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OperationsException;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.StringPropertyProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
/**
 * A set of properties. Instances of this class can be used as the core of a
 * managed object implementation.
 * A set of properties. Instances of this class can be used as the
 * core of a managed object implementation.
 */
public final class PropertySet implements PropertyProvider {
@@ -78,50 +60,65 @@
   */
  private static final class MyProperty<T> implements Property<T> {
    // The active set of values.
    private final SortedSet<T> activeValues;
    // The definition associated with this property.
    private final PropertyDefinition<T> d;
    // The default set of values (read-only).
    private final SortedSet<T> defaultValues;
    // The active set of values (read-only).
    private final SortedSet<T> activeValues;
    // The pending set of values.
    private final SortedSet<T> pendingValues;
    /**
     * Create a property with the provided sets of pre-validated default and
     * active values.
     * <p>
     * This constructor takes ownership of the provided value sets.
     * Create a property with the provided sets of pre-validated
     * default and active values.
     *
     * @param d
     * @param pd
     *          The property definition.
     * @param defaultValues
     *          The set of default values for the property.
     * @param activeValues
     *          The set of active values for the property.
     */
    public MyProperty(PropertyDefinition<T> d, SortedSet<T> defaultValues,
        SortedSet<T> activeValues) {
      this.d = d;
      this.defaultValues = Collections.unmodifiableSortedSet(defaultValues);
      this.activeValues = Collections.unmodifiableSortedSet(activeValues);
    public MyProperty(PropertyDefinition<T> pd, Collection<T> defaultValues,
        Collection<T> activeValues) {
      this.d = pd;
      // Initially the pending values is the same as the active values.
      SortedSet<T> sortedDefaultValues = new TreeSet<T>(pd);
      sortedDefaultValues.addAll(defaultValues);
      this.defaultValues = Collections
          .unmodifiableSortedSet(sortedDefaultValues);
      this.activeValues = new TreeSet<T>(pd);
      this.activeValues.addAll(activeValues);
      // Initially the pending values is the same as the active
      // values.
      this.pendingValues = new TreeSet<T>(this.activeValues);
    }
    /**
     * Makes the pending values active.
     */
    public void commit() {
      activeValues.clear();
      activeValues.addAll(pendingValues);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> getActiveValues() {
      return activeValues;
      return Collections.unmodifiableSortedSet(activeValues);
    }
@@ -181,21 +178,18 @@
     * {@inheritDoc}
     */
    public boolean isModified() {
      if (activeValues == pendingValues) {
      if (activeValues.size() == pendingValues.size()
          && activeValues.containsAll(pendingValues)) {
        return false;
      } else if (activeValues.size() != pendingValues.size()) {
        return true;
      } else if (activeValues.containsAll(pendingValues)) {
        return false;
      } else {
        return true;
      }
      return true;
    }
    /**
     * Replace all pending values of this property with the provided values.
     * Replace all pending values of this property with the provided
     * values.
     *
     * @param c
     *          The new set of pending property values.
@@ -210,376 +204,19 @@
    /**
     * {@inheritDoc}
     */
    public boolean wasEmpty() {
      return activeValues.isEmpty();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
      return getEffectiveValues().toString();
    }
  }
  /**
   * Internal default behavior visitor implementation.
   *
   * @param <T>
   *          The type of the default property values.
   */
  private static final class DefaultVisitor<T> implements
      DefaultBehaviorProviderVisitor<T, SortedSet<T>,
      Collection<PropertyException>> {
    // The property definition.
    private final PropertyDefinition<T> pd;
    // Used to retrieve inherited properties.
    private final InheritedDefaultValueProvider provider;
    // Private constructor.
    private DefaultVisitor(PropertyDefinition<T> pd,
        InheritedDefaultValueProvider provider) {
      this.pd = pd;
      this.provider = provider;
    }
    // Cast a set of objects to the required type.
    private Collection<T> castValues(Collection<?> values,
        Collection<PropertyException> exceptions) {
      List<T> castValues = new LinkedList<T>();
      for (Object value : values) {
        try {
          castValues.add(pd.castValue(value));
        } catch (ClassCastException e) {
          exceptions.add(new IllegalPropertyValueException(pd, value));
        }
      }
      return castValues;
    }
    // Build set of default values and validate them.
    private SortedSet<T> validateStrings(Collection<String> values,
        Collection<PropertyException> exceptions) {
      TreeSet<T> defaultValues = new TreeSet<T>(pd);
      for (String value : values) {
        try {
          defaultValues.add(pd.decodeValue(value));
        } catch (IllegalPropertyValueStringException e) {
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
        if (defaultValues.size() > 1) {
          PropertyException e = new PropertyIsSingleValuedException(pd);
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      return defaultValues;
    }
    // Build set of default values and validate them.
    private SortedSet<T> validate(Collection<T> values,
        Collection<PropertyException> exceptions) {
      TreeSet<T> defaultValues = new TreeSet<T>(pd);
      for (T value : values) {
        try {
          pd.validateValue(value);
          defaultValues.add(value);
        } catch (IllegalPropertyValueException e) {
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
        if (defaultValues.size() > 1) {
          PropertyException e = new PropertyIsSingleValuedException(pd);
          exceptions.add(new DefaultBehaviorPropertyValueException(pd, e));
        }
      }
      return defaultValues;
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // Get the values from the managed object at the specified path.
      try {
        // Get the property values/defaults.
        ManagedObjectPath path = d.getManagedObjectPath();
        Collection<?> values = provider.getDefaultPropertyValues(path, d
            .getPropertyName());
        return validate(castValues(values, p), p);
      } catch (OperationsException e) {
        p.add(new InheritedDefaultValueException(pd, e));
        return new TreeSet<T>(pd);
      }
    public boolean wasEmpty() {
      return activeValues.isEmpty();
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // No values applicable - just return the empty set.
      return new TreeSet<T>(pd);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      return validateStrings(d.getDefaultValues(), p);
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      if (d.getRelativeOffset() == 0) {
        // TODO: we're inheriting default values from another property in this
        // property set. Logging is a good use-case: there is a general logging
        // level for all categories and then category specific levels which can
        // override. Should the default values be determined dynamically every
        // time they are accessed? If dynamically, how will decoding errors be
        // handled? Dynamically: we could return a SortedSet<T> which is lazily
        // computed.
        return new TreeSet<T>(pd);
      } else {
        // Inheriting default values from a parent managed object.
        try {
          ManagedObjectPath base = provider.getManagedObjectPath();
          ManagedObjectPath path = d.getManagedObjectPath(base);
          Collection<?> values = provider.getDefaultPropertyValues(path, d
              .getPropertyName());
          return validate(castValues(values, p), p);
        } catch (OperationsException e) {
          p.add(new InheritedDefaultValueException(pd, e));
          return new TreeSet<T>(pd);
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    public SortedSet<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        Collection<PropertyException> p) {
      // No values applicable - just return the empty set.
      return new TreeSet<T>(pd);
    }
  }
  /**
   * Create a new property set using a property provider to supply the active
   * property values. This constructor takes care of validation of the property
   * values and retrieval of any default values.
   * <p>
   * Any exceptions that occurred whilst processing the properties will be
   * placed in the provided exception collection. Properties that caused the
   * exceptions will be created with an empty set of values (note that this
   * could mean that the resulting property set might contain empty mandatory
   * properties).
   *
   * @param d
   *          The managed object definition.
   * @param p
   *          The property provider.
   * @param i
   *          An inherited managed object provider for retrieving inherited
   *          properties.
   * @param exceptions
   *          A collection in which any property exceptions can be placed.
   * @return Returns the new property set.
   */
  public static PropertySet create(ManagedObjectDefinition<?, ?> d,
      PropertyProvider p, InheritedDefaultValueProvider i,
      Collection<PropertyException> exceptions) {
    Map<PropertyDefinition, MyProperty> properties =
      new HashMap<PropertyDefinition, MyProperty>();
    // Copy the properties from the provider.
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      createProperty(pd, p, i, properties, exceptions);
    }
    return new PropertySet(properties);
  }
  /**
   * Create a new property set using a string property provider to supply the
   * active property values. This constructor takes care of validation of the
   * property values and retrieval of any default values.
   * <p>
   * Any exceptions that occurred whilst processing the properties will be
   * placed in the provided exception collection. Properties that caused the
   * exceptions will be created with an empty set of values (note that this
   * could mean that the resulting property set might contain empty mandatory
   * properties).
   *
   * @param d
   *          The managed object definition.
   * @param p
   *          The string property provider.
   * @param i
   *          An inherited managed object provider for retrieving inherited
   *          properties.
   * @param exceptions
   *          A collection in which any property exceptions can be placed.
   * @return Returns the new property set.
   */
  public static PropertySet create(ManagedObjectDefinition<?, ?> d,
      StringPropertyProvider p, InheritedDefaultValueProvider i,
      Collection<PropertyException> exceptions) {
    Map<PropertyDefinition, MyProperty> properties =
      new HashMap<PropertyDefinition, MyProperty>();
    // Copy the properties from the provider.
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      createProperty(pd, p, i, properties, exceptions);
    }
    return new PropertySet(properties);
  }
  // Create new property using string values taken from a property provider.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      StringPropertyProvider p, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Get the active values for this property.
    Collection<String> activeStringValues;
    try {
      activeStringValues = p.getPropertyValues(pd);
    } catch (IllegalArgumentException e) {
      // Default to empty set of values.
      activeStringValues = Collections.<String> emptySet();
    }
    SortedSet<T> activeValues = new TreeSet<T>(pd);
    boolean gotException = false;
    for (String stringValue : activeStringValues) {
      try {
        activeValues.add(pd.decodeValue(stringValue));
      } catch (IllegalPropertyValueStringException e) {
        exceptions.add(e);
        gotException = true;
      }
    }
    if (gotException == false) {
      if (pd.hasOption(PropertyOption.MANDATORY)) {
        if (activeValues.isEmpty()) {
          exceptions.add(new PropertyIsMandatoryException(pd));
        }
      }
    }
    createProperty(pd, activeValues, i, properties, exceptions);
  }
  // Create new property using values taken from a property provider.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      PropertyProvider p, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Get the active values for this property.
    Collection<T> activeValues;
    try {
      activeValues = p.getPropertyValues(pd);
    } catch (IllegalArgumentException e) {
      // Default to empty set of values.
      activeValues = Collections.<T> emptySet();
    }
    SortedSet<T> validActiveValues = new TreeSet<T>(pd);
    boolean gotException = false;
    for (T value : activeValues) {
      try {
        pd.validateValue(value);
        validActiveValues.add(value);
      } catch (IllegalPropertyValueException e) {
        exceptions.add(e);
        gotException = true;
      }
    }
    if (gotException == false) {
      if (pd.hasOption(PropertyOption.MANDATORY)) {
        if (validActiveValues.isEmpty()) {
          exceptions.add(new PropertyIsMandatoryException(pd));
        }
      }
    }
    createProperty(pd, validActiveValues, i, properties, exceptions);
  }
  // Create new property using the provided validated values.
  private static <T> void createProperty(PropertyDefinition<T> pd,
      SortedSet<T> activeValues, InheritedDefaultValueProvider i,
      Map<PropertyDefinition, MyProperty> properties,
      Collection<PropertyException> exceptions) {
    // Do remaining validation of active values.
    if (!pd.hasOption(PropertyOption.MULTI_VALUED)) {
      if (activeValues.size() > 1) {
        exceptions.add(new PropertyIsSingleValuedException(pd));
      }
    }
    // Get the default values for this property.
    DefaultVisitor<T> visitor = new DefaultVisitor<T>(pd, i);
    SortedSet<T> defaultValues = pd.getDefaultBehaviorProvider().accept(
        visitor, exceptions);
    // Create the property.
    properties.put(pd, new MyProperty<T>(pd, defaultValues, activeValues));
  }
  // The properties.
@@ -587,25 +224,60 @@
  // Private constructor.
  private PropertySet(Map<PropertyDefinition, MyProperty> properties) {
    this.properties = properties;
  /**
   * Creates a new empty property set.
   */
  public PropertySet() {
    this.properties = new HashMap<PropertyDefinition, MyProperty>();
  }
  /**
   * Get the property associated with the specified property definition.
   * Creates a property with the provided sets of pre-validated
   * default and active values.
   *
   * @param <T>
   *          The type of the property.
   * @param pd
   *          The property definition.
   * @param defaultValues
   *          The set of default values for the property.
   * @param activeValues
   *          The set of active values for the property.
   */
  public <T> void addProperty(PropertyDefinition<T> pd,
      Collection<T> defaultValues, Collection<T> activeValues) {
    MyProperty<T> p = new MyProperty<T>(pd, defaultValues, activeValues);
    properties.put(pd, p);
  }
  /**
   * Makes all pending values active.
   */
  public void commit() {
    for (MyProperty<?> p : properties.values()) {
      p.commit();
    }
  }
  /**
   * Get the property associated with the specified property
   * definition.
   *
   * @param <T>
   *          The underlying type of the property.
   * @param d
   *          The Property definition.
   * @return Returns the property associated with the specified property
   *         definition.
   * @return Returns the property associated with the specified
   *         property definition.
   * @throws IllegalArgumentException
   *           If this property provider does not recognise the requested
   *           property definition.
   *           If this property provider does not recognise the
   *           requested property definition.
   */
  @SuppressWarnings("unchecked")
  public <T> Property<T> getProperty(PropertyDefinition<T> d)
@@ -622,18 +294,19 @@
  /**
   * Get the effective value of the specified property.
   * <p>
   * See the class description for more information about how the effective
   * property value is derived.
   * See the class description for more information about how the
   * effective property value is derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective value, or <code>null</code> if
   *         there is no effective value defined.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there is no effective value
   *         defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this managed
   *           object's definition.
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> T getPropertyValue(PropertyDefinition<T> d)
      throws IllegalArgumentException {
@@ -650,18 +323,18 @@
  /**
   * Get the effective values of the specified property.
   * <p>
   * See the class description for more information about how the effective
   * property values are derived.
   * See the class description for more information about how the
   * effective property values are derived.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's effective values, or an empty set if there
   *         are no effective values defined.
   * @return Returns the property's effective values, or an empty set
   *         if there are no effective values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this managed
   *           object's definition.
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
      throws IllegalArgumentException {
@@ -674,29 +347,29 @@
  /**
   * Set a new pending value for the specified property.
   * <p>
   * See the class description for more information regarding pending values.
   * See the class description for more information regarding pending
   * values.
   *
   * @param <T>
   *          The type of the property to be modified.
   * @param d
   *          The property to be modified.
   * @param value
   *          The new pending value for the property, or <code>null</code> if
   *          the property should be reset to its default behavior.
   *          The new pending value for the property, or
   *          <code>null</code> if the property should be reset to
   *          its default behavior.
   * @throws IllegalPropertyValueException
   *           If the new pending value is deemed to be invalid according to the
   *           property definition.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If the new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
   *           If the specified property definition is not associated with this
   *           managed object.
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
      throws IllegalPropertyValueException, PropertyIsMandatoryException,
      IllegalArgumentException {
    if (value == null) {
      setPropertyValues(d, Collections.<T> emptySet());
    } else {
@@ -709,51 +382,42 @@
  /**
   * Set a new pending values for the specified property.
   * <p>
   * See the class description for more information regarding pending values.
   * See the class description for more information regarding pending
   * values.
   *
   * @param <T>
   *          The type of the property to be modified.
   * @param d
   *          The property to be modified.
   * @param values
   *          A non-<code>null</code> set of new pending values for the
   *          property (an empty set indicates that the property should be reset
   *          to its default behavior). The set will not be referenced by this
   *          managed object.
   *          A non-<code>null</code> set of new pending values for
   *          the property (an empty set indicates that the property
   *          should be reset to its default behavior). The set will
   *          not be referenced by this managed object.
   * @throws IllegalPropertyValueException
   *           If a new pending value is deemed to be invalid according to the
   *           property definition.
   *           If a new pending value is deemed to be invalid
   *           according to the property definition.
   * @throws PropertyIsSingleValuedException
   *           If an attempt was made to add multiple pending values to a
   *           single-valued property.
   * @throws PropertyIsReadOnlyException
   *           If an attempt was made to modify a read-only property.
   *           If an attempt was made to add multiple pending values
   *           to a single-valued property.
   * @throws PropertyIsMandatoryException
   *           If an attempt was made to remove a mandatory property.
   * @throws IllegalArgumentException
   *           If the specified property definition is not associated with this
   *           managed object.
   *           If the specified property definition is not associated
   *           with this managed object.
   */
  public <T> void setPropertyValues(PropertyDefinition<T> d,
      Collection<T> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
      PropertyIsSingleValuedException, PropertyIsMandatoryException,
      IllegalArgumentException {
    MyProperty<T> property = (MyProperty<T>) getProperty(d);
    if (d.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(d);
    if (values.size() > 1 && !d.hasOption(PropertyOption.MULTI_VALUED)) {
      throw new PropertyIsSingleValuedException(d);
    }
    if (!d.hasOption(PropertyOption.MULTI_VALUED)) {
      if (values.size() > 1) {
        throw new PropertyIsSingleValuedException(d);
      }
    }
    if (d.hasOption(PropertyOption.MANDATORY)) {
      if (values.isEmpty()) {
        throw new PropertyIsMandatoryException(d);
      }
    if (values.isEmpty() && d.hasOption(PropertyOption.MANDATORY)) {
      throw new PropertyIsMandatoryException(d);
    }
    // Validate each value.
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -51,18 +51,22 @@
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.DefinitionResolver;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.InheritedDefaultValueProvider;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OperationsException;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
@@ -70,16 +74,18 @@
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyNotFoundException;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.StringPropertyProvider;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.client.Property;
import org.opends.server.admin.client.PropertySet;
@@ -99,31 +105,66 @@
    ManagedObject<C> {
  /**
   * Internal inherited default value provider implementation.
   * A default behavior visitor used for retrieving the default values
   * of a property.
   *
   * @param <T>
   *          The type of the property.
   */
  private static class MyInheritedDefaultValueProvider implements
      InheritedDefaultValueProvider {
  private static class DefaultValueFinder<T> implements
      DefaultBehaviorProviderVisitor<T, Collection<T>, ManagedObjectPath> {
    /**
     * Get the default values for the specified property.
     *
     * @param <T>
     *          The type of the property.
     * @param context
     *          The LDAP management context.
     * @param p
     *          The managed object path of the current managed object.
     * @param pd
     *          The property definition.
     * @return Returns the default values for the specified property.
     * @throws DefaultBehaviorException
     *           If the default values could not be retrieved or
     *           decoded properly.
     */
    public static <T> Collection<T> getDefaultValues(
        LDAPManagementContext context, ManagedObjectPath p,
        PropertyDefinition<T> pd) throws DefaultBehaviorException {
      DefaultValueFinder<T> v = new DefaultValueFinder<T>(context, pd);
      Collection<T> values = pd.getDefaultBehaviorProvider().accept(v, p);
      if (v.exception != null) {
        throw v.exception;
      }
      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
        throw new DefaultBehaviorException(pd,
            new PropertyIsSingleValuedException(pd));
      }
      return values;
    }
    // The LDAP management context.
    private final LDAPManagementContext context;
    // The base path.
    private final ManagedObjectPath path;
    // Any exception that occurred whilst retrieving inherited default
    // values.
    private DefaultBehaviorException exception = null;
    // The property definition whose default values are required.
    private final PropertyDefinition<T> pd;
    /**
     * Create a new inherited default value provider.
     *
     * @param context
     *          The LDAP management context.
     * @param path
     *          The base path.
     */
    public MyInheritedDefaultValueProvider(LDAPManagementContext context,
        ManagedObjectPath path) {
    // Private constructor.
    private DefaultValueFinder(LDAPManagementContext context,
        PropertyDefinition<T> pd) {
      this.context = context;
      this.path = path;
      this.pd = pd;
    }
@@ -131,26 +172,15 @@
    /**
     * {@inheritDoc}
     */
    public Collection<?> getDefaultPropertyValues(ManagedObjectPath path,
        String propertyName) throws OperationsException,
        PropertyNotFoundException {
      ManagedObjectPath<?, ?> tmp = path;
      ManagedObject<?> mo;
    public Collection<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
      try {
        mo = readEntry(context, tmp, tmp.getManagedObjectDefinition());
      } catch (AuthorizationException e) {
        throw new OperationsException(e);
      } catch (CommunicationException e) {
        throw new OperationsException(e);
        return getInheritedProperty(d.getManagedObjectPath(), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = new DefaultBehaviorException(pd, e);
      }
      ManagedObjectDefinition<?, ?> mod = mo.getManagedObjectDefinition();
      try {
        PropertyDefinition<?> dpd = mod.getPropertyDefinition(propertyName);
        return mo.getPropertyValues(dpd);
      } catch (IllegalArgumentException e) {
        throw new PropertyNotFoundException(propertyName);
      }
      return Collections.emptySet();
    }
@@ -158,10 +188,133 @@
    /**
     * {@inheritDoc}
     */
    public ManagedObjectPath getManagedObjectPath() {
      return path;
    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      return Collections.emptySet();
    }
  }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      Collection<String> stringValues = d.getDefaultValues();
      List<T> values = new ArrayList<T>(stringValues.size());
      for (String stringValue : stringValues) {
        try {
          values.add(pd.decodeValue(stringValue));
        } catch (IllegalPropertyValueStringException e) {
          exception = new DefaultBehaviorException(pd, e);
          break;
        }
      }
      return values;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(p), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = new DefaultBehaviorException(pd, e);
      }
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      return Collections.emptySet();
    }
    // Get an inherited property value.
    private Collection<T> getInheritedProperty(ManagedObjectPath target,
        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
        throws DefaultBehaviorException {
      try {
        // First check that the requested type of managed object
        // corresponds to the path.
        AbstractManagedObjectDefinition<?, ?> supr = target
            .getManagedObjectDefinition();
        if (!supr.isParentOf(d)) {
          throw new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION);
        }
        // Get the actual managed object definition.
        LdapName dn = LDAPNameBuilder.create(target, context.getLDAPProfile());
        ManagedObjectDefinition<?, ?> mod = getEntryDefinition(context, d, dn);
        PropertyDefinition<?> pd2;
        try {
          pd2 = mod.getPropertyDefinition(propertyName);
        } catch (IllegalArgumentException e) {
          throw new PropertyNotFoundException(propertyName);
        }
        String attrID = context.getLDAPProfile().getAttributeName(mod, pd2);
        Attributes attributes = context.getLDAPConnection().readEntry(dn,
            Collections.singleton(attrID));
        Attribute attr = attributes.get(attrID);
        if (attr == null || attr.size() == 0) {
          // Recursively retrieve this property's default values.
          Collection<?> tmp = getDefaultValues(context, target, pd2);
          Collection<T> values = new ArrayList<T>(tmp.size());
          for (Object o : tmp) {
            T value;
            try {
              value = pd.castValue(o);
            } catch (ClassCastException e) {
              throw new IllegalPropertyValueException(pd, o);
            }
            pd.validateValue(value);
            values.add(value);
          }
          return values;
        } else {
          Collection<T> values = new LinkedList<T>();
          NamingEnumeration<?> ne = attr.getAll();
          while (ne.hasMore()) {
            Object value = ne.next();
            if (value != null) {
              values.add(pd.decodeValue(value.toString()));
            }
          }
          return values;
        }
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (PropertyNotFoundException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (IllegalPropertyValueException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (IllegalPropertyValueStringException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (NameNotFoundException e) {
        throw new DefaultBehaviorException(pd,
            new ManagedObjectNotFoundException());
      } catch (NoPermissionException e) {
        throw new DefaultBehaviorException(pd, new AuthorizationException(e));
      } catch (NamingException e) {
        throw new DefaultBehaviorException(pd, new CommunicationException(e));
      }
    }
  };
@@ -176,30 +329,8 @@
   */
  static ManagedObject<RootCfgClient> getRootManagedObject(
      LDAPManagementContext context) {
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        context, ManagedObjectPath.emptyPath());
    PropertySet properties = PropertySet.create(RootCfgDefn.getInstance(),
        PropertyProvider.DEFAULT_PROVIDER, i, exceptions);
    // Should never get any exceptions.
    if (!exceptions.isEmpty()) {
      throw new RuntimeException(
          "Got exceptions when creating root managed object");
    }
    return new LDAPManagedObject<RootCfgClient>(context, RootCfgDefn
        .getInstance(), ManagedObjectPath.emptyPath(), properties);
  }
  // Create a new child LDAP managed object.
  private static <M extends ConfigurationClient>
      ManagedObject<M> createLDAPManagedObject(
      LDAPManagementContext context, ManagedObjectDefinition<M, ?> d,
      ManagedObjectPath p, PropertySet properties) {
    return new LDAPManagedObject<M>(context, d, p, properties);
        .getInstance(), ManagedObjectPath.emptyPath(), new PropertySet(), true);
  }
@@ -247,104 +378,17 @@
    return d.resolveManagedObjectDefinition(resolver);
  }
  // Read the entry identified by the path and which is a sub-type of
  // the specified definition.
  private static <M extends ConfigurationClient>
      ManagedObject<? extends M> readEntry(
      final LDAPManagementContext context, ManagedObjectPath p,
      AbstractManagedObjectDefinition<M, ?> d)
      throws DefinitionDecodingException, ManagedObjectDecodingException,
      ManagedObjectNotFoundException, AuthorizationException,
      CommunicationException {
    LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
    final ManagedObjectDefinition<? extends M, ?> mod;
    final Attributes attributes;
    try {
      mod = getEntryDefinition(context, d, dn);
      ArrayList<String> attrIds = new ArrayList<String>();
      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
        String attrId = context.getLDAPProfile().getAttributeName(mod, pd);
        attrIds.add(attrId);
      }
      attributes = context.getLDAPConnection().readEntry(dn, attrIds);
    } catch (NameNotFoundException e) {
      throw new ManagedObjectNotFoundException();
    } catch (NoPermissionException e) {
      throw new AuthorizationException(e);
    } catch (NamingException e) {
      throw new CommunicationException(e);
    }
    // Create a provider which uses LDAP string representations.
    // TODO: the exception handling is a bit of a hack here.
    final List<NamingException> nelist = new LinkedList<NamingException>();
    StringPropertyProvider provider = new StringPropertyProvider() {
      public Collection<String> getPropertyValues(PropertyDefinition<?> d)
          throws IllegalArgumentException {
        String attrID = context.getLDAPProfile().getAttributeName(mod, d);
        Attribute attribute = attributes.get(attrID);
        List<String> values = new LinkedList<String>();
        if (attribute != null && attribute.size() != 0) {
          try {
            NamingEnumeration<?> ldapValues = attribute.getAll();
            while (ldapValues.hasMore()) {
              Object obj = ldapValues.next();
              if (obj != null) {
                values.add(obj.toString());
              }
            }
          } catch (NamingException e) {
            nelist.add(e);
          }
        }
        return values;
      }
    };
    // There can only be at most one exception.
    if (!nelist.isEmpty()) {
      try {
        throw nelist.get(0);
      } catch (NameNotFoundException e) {
        throw new ManagedObjectNotFoundException();
      } catch (NoPermissionException e) {
        throw new AuthorizationException(e);
      } catch (NamingException e) {
        throw new CommunicationException(e);
      }
    }
    // Now decode the properties using the provider.
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        context, p);
    PropertySet properties = PropertySet.create(mod, provider, i, exceptions);
    ManagedObject<? extends M> mo = createLDAPManagedObject(context, mod, p,
        properties);
    // If there were no decoding problems then return the object,
    // otherwise throw an operations exception.
    if (exceptions.isEmpty()) {
      return mo;
    } else {
      throw new ManagedObjectDecodingException(mo, exceptions);
    }
  }
  // The LDAP management context used for the ldap connection.
  private final LDAPManagementContext context;
  // The managed object definition associated with this managed
  // object.
  private final ManagedObjectDefinition<C, ?> definition;
  // The LDAP management context used for the ldap connection.
  private final LDAPManagementContext context;
  // Indicates whether or not this managed object exists on the server
  // (false means the managed object is new and has not been
  // committed).
  private boolean existsOnServer;
  // The path associated with this managed object.
  private final ManagedObjectPath<?, ?> path;
@@ -357,11 +401,12 @@
  // Create an new LDAP managed object with the provided JNDI context.
  private LDAPManagedObject(LDAPManagementContext context,
      ManagedObjectDefinition<C, ?> d, ManagedObjectPath path,
      PropertySet properties) {
      PropertySet properties, boolean existsOnServer) {
    this.definition = d;
    this.context = context;
    this.path = path;
    this.properties = properties;
    this.existsOnServer = existsOnServer;
  }
@@ -369,36 +414,30 @@
  /**
   * {@inheritDoc}
   */
  public void commit() throws ConcurrentModificationException,
      OperationRejectedException, AuthorizationException,
      CommunicationException {
    // Build the list of modified attributes.
    ManagedObjectDefinition<C, ?> d = getManagedObjectDefinition();
    Attributes mods = new BasicAttributes();
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
  public void commit() throws MissingMandatoryPropertiesException,
      ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException,
      ManagedObjectAlreadyExistsException {
    // First make sure all mandatory properties are defined.
    List<PropertyIsMandatoryException> exceptions =
      new LinkedList<PropertyIsMandatoryException>();
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      Property<?> p = properties.getProperty(pd);
      if (p.isModified()) {
        String attrID = context.getLDAPProfile().getAttributeName(d, pd);
        Attribute attribute = new BasicAttribute(attrID);
        encodeProperty(attribute, pd, properties);
        mods.put(attribute);
      if (pd.hasOption(PropertyOption.MANDATORY) && p.isEmpty()) {
        exceptions.add(new PropertyIsMandatoryException(pd));
      }
    }
    // Perform the LDAP modification if something has changed.
    if (mods.size() > 0) {
      try {
        LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
        context.getLDAPConnection().modifyEntry(dn, mods);
      } catch (NoPermissionException e) {
        throw new AuthorizationException(e);
      } catch (OperationNotSupportedException e) {
        // Unwilling to perform.
        throw new OperationRejectedException(e);
      } catch (NamingException e) {
        // Just treat it as a communication problem.
        throw new CommunicationException(e);
      }
    if (!exceptions.isEmpty()) {
      throw new MissingMandatoryPropertiesException(exceptions);
    }
    // Commit the managed object.
    if (existsOnServer) {
      commitExistingManagedObject();
    } else {
      commitNewManagedObject();
    }
  }
@@ -410,60 +449,11 @@
  public <M extends ConfigurationClient, N extends M>
      ManagedObject<N> createChild(
      InstantiableRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
      String name, PropertyProvider p) throws IllegalArgumentException,
      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
      ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException {
      String name, Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException {
    validateRelationDefinition(r);
    ManagedObjectPath childPath = path.child(r, name);
    // First make sure all the properties are valid.
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        context, childPath);
    PropertySet properties = PropertySet.create(d, p, i, exceptions);
    if (!exceptions.isEmpty()) {
      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
          properties);
      throw new ManagedObjectDecodingException(mo, exceptions);
    }
    ensureThisManagedObjectExists();
    // TODO: this implementation does not handle relations which
    // comprise of more than one RDN arc (this will probably never
    // be required anyway).
    LdapName dn = LDAPNameBuilder.create(path, r, context.getLDAPProfile());
    if (!entryExists(dn)) {
      // Need to create the child managed object's parent entry i.e.
      // the entry representing the relation itself.
      Attributes attributes = new BasicAttributes();
      // Create the branch's object class attribute.
      Attribute oc = new BasicAttribute("objectClass");
      for (String objectClass : context.getLDAPProfile()
          .getInstantiableRelationObjectClasses(r)) {
        oc.add(objectClass);
      }
      attributes.put(oc);
      // Create the branch's naming attribute.
      Rdn rdn = dn.getRdn(dn.size() - 1);
      attributes.put(rdn.getType(), rdn.getValue().toString());
      // Create the entry.
      try {
        context.getLDAPConnection().createEntry(dn, attributes);
      } catch (OperationNotSupportedException e) {
        // Unwilling to perform.
        throw new OperationRejectedException(e);
      } catch (NamingException e) {
        adaptNamingException(e);
      }
    }
    return createManagedObject(childPath, d, properties);
    return createNewManagedObject(d, childPath, exceptions);
  }
@@ -474,28 +464,11 @@
  public <M extends ConfigurationClient, N extends M>
      ManagedObject<N> createChild(
      OptionalRelationDefinition<M, ?> r, ManagedObjectDefinition<N, ?> d,
      PropertyProvider p) throws IllegalArgumentException,
      ManagedObjectDecodingException, ManagedObjectAlreadyExistsException,
      ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException {
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException {
    validateRelationDefinition(r);
    ManagedObjectPath childPath = path.child(r);
    // First make sure all the properties are valid.
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        context, childPath);
    PropertySet properties = PropertySet.create(d, p, i, exceptions);
    if (!exceptions.isEmpty()) {
      ManagedObject<N> mo = new LDAPManagedObject<N>(context, d, childPath,
          properties);
      throw new ManagedObjectDecodingException(mo, exceptions);
    }
    ensureThisManagedObjectExists();
    return createManagedObject(childPath, d, properties);
    return createNewManagedObject(d, childPath, exceptions);
  }
@@ -511,7 +484,7 @@
      CommunicationException {
    validateRelationDefinition(d);
    ensureThisManagedObjectExists();
    return readEntry(context, path.child(d, name), d.getChildDefinition());
    return readManagedObject(d.getChildDefinition(), path.child(d, name));
  }
@@ -526,7 +499,7 @@
      AuthorizationException, CommunicationException {
    validateRelationDefinition(d);
    ensureThisManagedObjectExists();
    return readEntry(context, path.child(d), d.getChildDefinition());
    return readManagedObject(d.getChildDefinition(), path.child(d));
  }
@@ -541,7 +514,7 @@
      AuthorizationException, CommunicationException {
    validateRelationDefinition(d);
    ensureThisManagedObjectExists();
    return readEntry(context, path.child(d), d.getChildDefinition());
    return readManagedObject(d.getChildDefinition(), path.child(d));
  }
@@ -674,6 +647,14 @@
  public <T> void setPropertyValue(PropertyDefinition<T> d, T value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (d.hasOption(PropertyOption.MONITORING)) {
      throw new PropertyIsReadOnlyException(d);
    }
    if (existsOnServer && d.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(d);
    }
    properties.setPropertyValue(d, value);
  }
@@ -686,6 +667,14 @@
      Collection<T> values) throws IllegalPropertyValueException,
      PropertyIsSingleValuedException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
    if (d.hasOption(PropertyOption.MONITORING)) {
      throw new PropertyIsReadOnlyException(d);
    }
    if (existsOnServer && d.hasOption(PropertyOption.READ_ONLY)) {
      throw new PropertyIsReadOnlyException(d);
    }
    properties.setPropertyValues(d, values);
  }
@@ -711,30 +700,118 @@
  // Creates a new managed object. The parent LDAP entry is assumed to
  // already exist.
  private <N extends ConfigurationClient> ManagedObject<N> createManagedObject(
      ManagedObjectPath path, ManagedObjectDefinition<N, ?> d,
      PropertySet properties) throws ManagedObjectAlreadyExistsException,
      OperationRejectedException, AuthorizationException,
      CommunicationException {
  // Commit modifications made to this managed object.
  private void commitExistingManagedObject()
      throws ConcurrentModificationException, OperationRejectedException,
      AuthorizationException, CommunicationException {
    // Build the list of modified attributes.
    Attributes mods = new BasicAttributes();
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      Property<?> p = properties.getProperty(pd);
      if (p.isModified()) {
        String attrID = context.getLDAPProfile().getAttributeName(definition,
            pd);
        Attribute attribute = new BasicAttribute(attrID);
        encodeProperty(attribute, pd, properties);
        mods.put(attribute);
      }
    }
    // Perform the LDAP modification if something has changed.
    if (mods.size() > 0) {
      try {
        LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
        context.getLDAPConnection().modifyEntry(dn, mods);
      } catch (NoPermissionException e) {
        throw new AuthorizationException(e);
      } catch (OperationNotSupportedException e) {
        // Unwilling to perform.
        throw new OperationRejectedException(e);
      } catch (NamingException e) {
        // Just treat it as a communication problem.
        throw new CommunicationException(e);
      }
    }
    // The changes were committed successfully so update this managed
    // object's state.
    properties.commit();
  }
  // Commit this new managed object.
  private void commitNewManagedObject() throws AuthorizationException,
      CommunicationException, OperationRejectedException,
      ConcurrentModificationException, ManagedObjectAlreadyExistsException {
    // First make sure that the parent managed object still exists.
    ManagedObjectPath<?, ?> parent = path.parent();
    if (!parent.isEmpty()) {
      LdapName dn = LDAPNameBuilder.create(parent, context.getLDAPProfile());
      if (!entryExists(dn)) {
        throw new ConcurrentModificationException();
      }
    }
    // We may need to create the parent "relation" entry if this is a
    // child of an instantiable relation.
    RelationDefinition<?, ?> r = path.getRelationDefinition();
    if (r instanceof InstantiableRelationDefinition) {
      InstantiableRelationDefinition<?, ?> ir =
        (InstantiableRelationDefinition<?, ?>) r;
      // TODO: this implementation does not handle relations which
      // comprise of more than one RDN arc (this will probably never
      // be required anyway).
      LdapName dn = LDAPNameBuilder
          .create(parent, ir, context.getLDAPProfile());
      if (!entryExists(dn)) {
        // We need to create the entry.
        Attributes attributes = new BasicAttributes();
        // Create the branch's object class attribute.
        Attribute oc = new BasicAttribute("objectClass");
        for (String objectClass : context.getLDAPProfile()
            .getInstantiableRelationObjectClasses(ir)) {
          oc.add(objectClass);
        }
        attributes.put(oc);
        // Create the branch's naming attribute.
        Rdn rdn = dn.getRdn(dn.size() - 1);
        attributes.put(rdn.getType(), rdn.getValue().toString());
        // Create the entry.
        try {
          context.getLDAPConnection().createEntry(dn, attributes);
        } catch (OperationNotSupportedException e) {
          // Unwilling to perform.
          throw new OperationRejectedException(e);
        } catch (NamingException e) {
          adaptNamingException(e);
        }
      }
    }
    // Now add the entry representing this new managed object.
    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
    Attributes attributes = new BasicAttributes(true);
    // Create the child's object class attribute.
    // Create the object class attribute.
    Attribute oc = new BasicAttribute("objectclass");
    for (String objectClass : context.getLDAPProfile().getObjectClasses(d)) {
    for (String objectClass : context.getLDAPProfile().getObjectClasses(
        definition)) {
      oc.add(objectClass);
    }
    attributes.put(oc);
    // Create the child's naming attribute.
    // Create the naming attribute.
    Rdn rdn = dn.getRdn(dn.size() - 1);
    attributes.put(rdn.getType(), rdn.getValue().toString());
    // Create the remaining attributes.
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      String attrID = context.getLDAPProfile().getAttributeName(d, pd);
    for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
      String attrID = context.getLDAPProfile().getAttributeName(definition, pd);
      Attribute attribute = new BasicAttribute(attrID);
      encodeProperty(attribute, pd, properties);
      if (attribute.size() != 0) {
@@ -754,7 +831,108 @@
      adaptNamingException(e);
    }
    return new LDAPManagedObject<N>(context, d, path, properties);
    // The entry was created successfully so update this managed
    // object's state.
    properties.commit();
    existsOnServer = true;
  }
  // Create a managed object which already exists on the server.
  private <M extends ConfigurationClient>
      ManagedObject<M> createExistingManagedObject(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath p,
      PropertySet properties) {
    return new LDAPManagedObject<M>(context, d, p, properties, true);
  }
  // Creates a new managed object with no active values, just default
  // values.
  private <M extends ConfigurationClient>
      ManagedObject<M> createNewManagedObject(
      ManagedObjectDefinition<M, ?> d, ManagedObjectPath p,
      Collection<DefaultBehaviorException> exceptions) {
    PropertySet childProperties = new PropertySet();
    for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
      try {
        createProperty(childProperties, p, pd);
      } catch (DefaultBehaviorException e) {
        // Add the exception if requested.
        if (exceptions != null) {
          exceptions.add(e);
        }
      }
    }
    return new LDAPManagedObject<M>(context, d, p, childProperties, false);
  }
  // Create an empty property.
  private <T> void createProperty(PropertySet properties, ManagedObjectPath p,
      PropertyDefinition<T> pd) throws DefaultBehaviorException {
    try {
      Collection<T> defaultValues = DefaultValueFinder.getDefaultValues(
          context, p, pd);
      properties.addProperty(pd, defaultValues, Collections.<T> emptySet());
    } catch (DefaultBehaviorException e) {
      // Make sure that we have still created the property.
      properties.addProperty(pd, Collections.<T> emptySet(), Collections
          .<T> emptySet());
      throw e;
    }
  }
  // Create a property using the provided string values.
  private <T> void decodeProperty(PropertySet newProperties,
      ManagedObjectPath p, PropertyDefinition<T> pd, List<String> values)
      throws PropertyException {
    PropertyException exception = null;
    // Get the property's active values.
    Collection<T> activeValues = new ArrayList<T>(values.size());
    for (String value : values) {
      try {
        activeValues.add(pd.decodeValue(value));
      } catch (IllegalPropertyValueStringException e) {
        exception = e;
      }
    }
    if (activeValues.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
      // This exception takes precedence over previous exceptions.
      exception = new PropertyIsSingleValuedException(pd);
      T value = activeValues.iterator().next();
      activeValues.clear();
      activeValues.add(value);
    }
    if (activeValues.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
      // The active values maybe empty because of a previous exception.
      if (exception == null) {
        exception = new PropertyIsMandatoryException(pd);
      }
    }
    // Get the property's default values.
    Collection<T> defaultValues;
    try {
      defaultValues = DefaultValueFinder.getDefaultValues(context, p, pd);
    } catch (DefaultBehaviorException e) {
      defaultValues = Collections.emptySet();
      exception = e;
    }
    newProperties.addProperty(pd, defaultValues, activeValues);
    if (exception != null) {
      throw exception;
    }
  }
@@ -775,9 +953,11 @@
  private void ensureThisManagedObjectExists()
      throws ConcurrentModificationException, CommunicationException,
      AuthorizationException {
    LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
    if (!entryExists(dn)) {
      throw new ConcurrentModificationException();
    if (!path.isEmpty()) {
      LdapName dn = LDAPNameBuilder.create(path, context.getLDAPProfile());
      if (!entryExists(dn)) {
        throw new ConcurrentModificationException();
      }
    }
  }
@@ -796,6 +976,75 @@
  // Read the entry identified by the path and which is a sub-type of
  // the specified definition.
  private <M extends ConfigurationClient>
      ManagedObject<? extends M> readManagedObject(
      AbstractManagedObjectDefinition<M, ?> d, ManagedObjectPath p)
      throws DefinitionDecodingException, ManagedObjectDecodingException,
      ManagedObjectNotFoundException, AuthorizationException,
      CommunicationException {
    try {
      // Read the entry associated with the managed object.
      LdapName dn = LDAPNameBuilder.create(p, context.getLDAPProfile());
      ManagedObjectDefinition<? extends M, ?> mod = getEntryDefinition(context,
          d, dn);
      ArrayList<String> attrIds = new ArrayList<String>();
      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
        String attrId = context.getLDAPProfile().getAttributeName(mod, pd);
        attrIds.add(attrId);
      }
      Attributes attributes = context.getLDAPConnection()
          .readEntry(dn, attrIds);
      // Build the managed object's properties.
      List<PropertyException> exceptions = new LinkedList<PropertyException>();
      PropertySet newProperties = new PropertySet();
      for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
        String attrID = context.getLDAPProfile().getAttributeName(mod, pd);
        Attribute attribute = attributes.get(attrID);
        List<String> values = new LinkedList<String>();
        if (attribute != null && attribute.size() != 0) {
          NamingEnumeration<?> ldapValues = attribute.getAll();
          while (ldapValues.hasMore()) {
            Object obj = ldapValues.next();
            if (obj != null) {
              values.add(obj.toString());
            }
          }
        }
        try {
          decodeProperty(newProperties, p, pd, values);
        } catch (PropertyException e) {
          exceptions.add(e);
        }
      }
      // If there were no decoding problems then return the object,
      // otherwise throw an operations exception.
      ManagedObject<? extends M> mo = createExistingManagedObject(mod, p,
          newProperties);
      if (exceptions.isEmpty()) {
        return mo;
      } else {
        throw new ManagedObjectDecodingException(mo, exceptions);
      }
    } catch (NameNotFoundException e) {
      throw new ManagedObjectNotFoundException();
    } catch (NoPermissionException e) {
      throw new AuthorizationException(e);
    } catch (NamingException e) {
      throw new CommunicationException(e);
    }
  }
  // Remove the named managed object.
  private void removeManagedObject(ManagedObjectPath p)
      throws CommunicationException, AuthorizationException,
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -29,38 +29,49 @@
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.DefaultBehaviorException;
import org.opends.server.admin.DefaultBehaviorProviderVisitor;
import org.opends.server.admin.DefinedDefaultBehaviorProvider;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.DefinitionResolver;
import org.opends.server.admin.InheritedDefaultValueProvider;
import org.opends.server.admin.IllegalPropertyValueException;
import org.opends.server.admin.IllegalPropertyValueStringException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.LDAPProfile;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OperationsException;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyIsMandatoryException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyNotFoundException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.StringPropertyProvider;
import org.opends.server.admin.client.PropertySet;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.api.AttributeValueDecoder;
@@ -70,6 +81,7 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.messages.AdminMessages;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
@@ -78,6 +90,7 @@
import org.opends.server.types.DirectoryException;
/**
 * A server-side managed object.
 *
@@ -85,81 +98,246 @@
 *          The type of server configuration represented by the server
 *          managed object.
 */
public final class ServerManagedObject<S extends Configuration>
    implements PropertyProvider {
public final class ServerManagedObject<S extends Configuration> implements
    PropertyProvider {
  /**
   * A default behavior visitor used for retrieving the default values
   * of a property.
   *
   * @param <T>
   *          The type of the property.
   */
  private static class DefaultValueFinder<T> implements
      DefaultBehaviorProviderVisitor<T, Collection<T>, ManagedObjectPath> {
    /**
     * Get the default values for the specified property.
     *
     * @param <T>
     *          The type of the property.
     * @param p
     *          The managed object path of the current managed object.
     * @param pd
     *          The property definition.
     * @return Returns the default values for the specified property.
     * @throws DefaultBehaviorException
     *           If the default values could not be retrieved or
     *           decoded properly.
     */
    public static <T> Collection<T> getDefaultValues(ManagedObjectPath p,
        PropertyDefinition<T> pd) throws DefaultBehaviorException {
      DefaultValueFinder<T> v = new DefaultValueFinder<T>(pd);
      Collection<T> values = pd.getDefaultBehaviorProvider().accept(v, p);
      if (v.exception != null) {
        throw v.exception;
      }
      if (values.size() > 1 && !pd.hasOption(PropertyOption.MULTI_VALUED)) {
        throw new DefaultBehaviorException(pd,
            new PropertyIsSingleValuedException(pd));
      }
      return values;
    }
    // Any exception that occurred whilst retrieving inherited default
    // values.
    private DefaultBehaviorException exception = null;
    // The property definition whose default values are required.
    private final PropertyDefinition<T> pd;
    // Private constructor.
    private DefaultValueFinder(PropertyDefinition<T> pd) {
      this.pd = pd;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAbsoluteInherited(
        AbsoluteInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = new DefaultBehaviorException(pd, e);
      }
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitAlias(AliasDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitDefined(DefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      Collection<String> stringValues = d.getDefaultValues();
      List<T> values = new ArrayList<T>(stringValues.size());
      for (String stringValue : stringValues) {
        try {
          values.add(pd.decodeValue(stringValue));
        } catch (IllegalPropertyValueStringException e) {
          exception = new DefaultBehaviorException(pd, e);
          break;
        }
      }
      return values;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitRelativeInherited(
        RelativeInheritedDefaultBehaviorProvider<T> d, ManagedObjectPath p) {
      try {
        return getInheritedProperty(d.getManagedObjectPath(p), d
            .getManagedObjectDefinition(), d.getPropertyName());
      } catch (DefaultBehaviorException e) {
        exception = new DefaultBehaviorException(pd, e);
      }
      return Collections.emptySet();
    }
    /**
     * {@inheritDoc}
     */
    public Collection<T> visitUndefined(UndefinedDefaultBehaviorProvider<T> d,
        ManagedObjectPath p) {
      return Collections.emptySet();
    }
    // Get an inherited property value.
    private Collection<T> getInheritedProperty(ManagedObjectPath target,
        AbstractManagedObjectDefinition<?, ?> d, String propertyName)
        throws DefaultBehaviorException {
      try {
        // First check that the requested type of managed object
        // corresponds to the path.
        AbstractManagedObjectDefinition<?, ?> supr = target
            .getManagedObjectDefinition();
        if (!supr.isParentOf(d)) {
          throw new DefinitionDecodingException(Reason.WRONG_TYPE_INFORMATION);
        }
        // Get the actual managed object definition.
        DN dn = DNBuilder.create(target);
        ConfigEntry configEntry = getManagedObjectConfigEntry(dn);
        DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
        ManagedObjectDefinition<?, ?> mod = d
            .resolveManagedObjectDefinition(resolver);
        PropertyDefinition<?> pd2;
        try {
          pd2 = mod.getPropertyDefinition(propertyName);
        } catch (IllegalArgumentException e) {
          throw new PropertyNotFoundException(propertyName);
        }
        List<String> stringValues = getAttribute(mod, pd2, configEntry);
        if (stringValues.isEmpty()) {
          // Recursively retrieve this property's default values.
          Collection<?> tmp = getDefaultValues(target, pd2);
          Collection<T> values = new ArrayList<T>(tmp.size());
          for (Object o : tmp) {
            T value;
            try {
              value = pd.castValue(o);
            } catch (ClassCastException e) {
              throw new IllegalPropertyValueException(pd, o);
            }
            pd.validateValue(value);
            values.add(value);
          }
          return values;
        } else {
          Collection<T> values = new ArrayList<T>(stringValues.size());
          for (String s : stringValues) {
            values.add(pd.decodeValue(s));
          }
          return values;
        }
      } catch (DefinitionDecodingException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (PropertyNotFoundException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (IllegalPropertyValueException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (IllegalPropertyValueStringException e) {
        throw new DefaultBehaviorException(pd, e);
      } catch (ConfigException e) {
        throw new DefaultBehaviorException(pd, e);
      }
    }
  }
  /**
   * A definition resolver that determines the managed object
   * definition from the object classes of a ConfigEntry.
   */
  private static class MyDefinitionResolver implements DefinitionResolver {
    // The config entry.
    private final ConfigEntry entry;
    // Private constructor.
    private MyDefinitionResolver(ConfigEntry entry) {
      this.entry = entry;
    }
    /**
     * {@inheritDoc}
     */
    public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
      String oc = LDAPProfile.getInstance().getObjectClass(d);
      return entry.hasObjectClass(oc);
    }
  };
  /**
   * The root server managed object.
   */
  private static final ServerManagedObject<RootCfg> ROOT =
    new ServerManagedObject<RootCfg>(
      ManagedObjectPath.emptyPath(), RootCfgDefn.getInstance(), Collections
          .<PropertyDefinition<?>, SortedSet<?>> emptyMap(), null);
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * Internal inherited default value provider implementation.
   */
  private static class MyInheritedDefaultValueProvider implements
      InheritedDefaultValueProvider {
    // The base path.
    private final ManagedObjectPath<?, ?> path;
    /**
     * Create a new inherited default value provider.
     *
     * @param path
     *          The base path.
     */
    public MyInheritedDefaultValueProvider(ManagedObjectPath path) {
      this.path = path;
    }
    /**
     * {@inheritDoc}
     */
    public Collection<?> getDefaultPropertyValues(
        ManagedObjectPath path, String propertyName)
        throws OperationsException, PropertyNotFoundException {
      // Get the configuration entry.
      DN targetDN = DNBuilder.create(path);
      final ConfigEntry configEntry;
      try {
        configEntry = DirectoryServer.getConfigEntry(targetDN);
      } catch (ConfigException e) {
        throw new ManagedObjectNotFoundException(e);
      }
      if (configEntry == null) {
        throw new ManagedObjectNotFoundException();
      }
      ManagedObjectPath<?, ?> tmp = path;
      ServerManagedObject<?> mo = decode(tmp, tmp
          .getManagedObjectDefinition(), configEntry);
      ManagedObjectDefinition<?, ?> mod = mo
          .getManagedObjectDefinition();
      try {
        PropertyDefinition<?> dpd = mod
            .getPropertyDefinition(propertyName);
        return mo.getPropertyValues(dpd);
      } catch (IllegalArgumentException e) {
        throw new PropertyNotFoundException(propertyName);
      }
    }
    /**
     * {@inheritDoc}
     */
    public ManagedObjectPath getManagedObjectPath() {
      return path;
    }
  }
  /**
@@ -183,74 +361,34 @@
   *           If one or more of the managed object's properties could
   *           not be decoded.
   */
  static <S extends Configuration>
  ServerManagedObject<? extends S> decode(
      ManagedObjectPath path,
      AbstractManagedObjectDefinition<?, S> definition,
      final ConfigEntry configEntry)
      throws DefinitionDecodingException,
  static <S extends Configuration> ServerManagedObject<? extends S> decode(
      ManagedObjectPath path, AbstractManagedObjectDefinition<?, S> definition,
      ConfigEntry configEntry) throws DefinitionDecodingException,
      ServerManagedObjectDecodingException {
    // First determine the correct definition to use for the entry.
    // This could either be the provided definition, or one of its
    // sub-definitions.
    DefinitionResolver resolver = new DefinitionResolver() {
      public boolean matches(AbstractManagedObjectDefinition<?, ?> d) {
        String oc = LDAPProfile.getInstance().getObjectClass(d);
        return configEntry.hasObjectClass(oc);
      }
    };
    final ManagedObjectDefinition<?, ? extends S> mod = definition
    DefinitionResolver resolver = new MyDefinitionResolver(configEntry);
    ManagedObjectDefinition<?, ? extends S> mod = definition
        .resolveManagedObjectDefinition(resolver);
    // Use a string-based property provider to pull in the property
    // values.
    StringPropertyProvider provider = new StringPropertyProvider() {
      public Collection<String> getPropertyValues(
          PropertyDefinition<?> d) throws IllegalArgumentException {
        String attrID = LDAPProfile.getInstance().getAttributeName(
            mod, d);
        // 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.
        AttributeType type = DirectoryServer.getAttributeType(attrID, true);
        AttributeValueDecoder<String> decoder =
          new AttributeValueDecoder<String>() {
          public String decode(AttributeValue value)
              throws DirectoryException {
            return value.getStringValue();
          }
        };
        try {
          Collection<String> values = new LinkedList<String>();
          configEntry.getEntry().getAttributeValues(type, decoder,
              values);
          return values;
        } catch (DirectoryException e) {
          // Should not happen.
          throw new RuntimeException(e);
        }
      }
    };
    // Create the new managed object's property set, saving any
    // decoding exceptions.
    // Build the managed object's properties.
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        path);
    PropertySet properties = PropertySet.create(mod, provider, i,
        exceptions);
    ServerManagedObject<? extends S> mo = decodeAux(path, mod,
        properties, configEntry);
    Map<PropertyDefinition<?>, SortedSet<?>> properties =
      new HashMap<PropertyDefinition<?>, SortedSet<?>>();
    for (PropertyDefinition<?> pd : mod.getAllPropertyDefinitions()) {
      List<String> values = getAttribute(mod, pd, configEntry);
      try {
        decodeProperty(properties, path, pd, values);
      } catch (PropertyException e) {
        exceptions.add(e);
      }
    }
    // If there were no decoding problems then return the object,
    // otherwise throw an operations exception.
    // If there were no decoding problems then return the managed
    // object, otherwise throw an operations exception.
    ServerManagedObject<? extends S> mo = decodeAux(path, mod, properties,
        configEntry);
    if (exceptions.isEmpty()) {
      return mo;
    } else {
@@ -261,28 +399,12 @@
  /**
   * Construct a root server managed object.
   * Gets the root server managed object.
   *
   * @return Returns a root server managed object.
   * @return Returns the root server managed object.
   */
  static ServerManagedObject<RootCfg> getRootManagedObject() {
    ManagedObjectPath path = ManagedObjectPath.emptyPath();
    List<PropertyException> exceptions = new LinkedList<PropertyException>();
    InheritedDefaultValueProvider i = new MyInheritedDefaultValueProvider(
        path);
    PropertySet properties = PropertySet.create(
        RootCfgDefn.getInstance(),
        PropertyProvider.DEFAULT_PROVIDER, i, exceptions);
    // Should never get any exceptions.
    if (!exceptions.isEmpty()) {
      throw new RuntimeException(
          "Got exceptions when creating root managed object");
    }
    return new ServerManagedObject<RootCfg>(path,
        RootCfgDefn.getInstance(), properties,
        null);
    return ROOT;
  }
@@ -290,11 +412,125 @@
  // Decode helper method required to avoid generics warning.
  private static <S extends Configuration> ServerManagedObject<S> decodeAux(
      ManagedObjectPath path, ManagedObjectDefinition<?, S> d,
      PropertySet properties, ConfigEntry configEntry) {
    return new ServerManagedObject<S>(path, d, properties,
        configEntry);
      Map<PropertyDefinition<?>, SortedSet<?>> properties,
      ConfigEntry configEntry) {
    return new ServerManagedObject<S>(path, d, properties, configEntry);
  }
  // Create a property using the provided string values.
  private static <T> void decodeProperty(
      Map<PropertyDefinition<?>, SortedSet<?>> properties,
      ManagedObjectPath path, PropertyDefinition<T> pd,
      List<String> stringValues) throws PropertyException {
    PropertyException exception = null;
    SortedSet<T> values = new TreeSet<T>(pd);
    if (!stringValues.isEmpty()) {
      // The property has values defined for it.
      for (String value : stringValues) {
        try {
          values.add(pd.decodeValue(value));
        } catch (IllegalPropertyValueStringException e) {
          exception = e;
        }
      }
    } else {
      // No values defined so get the defaults.
      try {
        values.addAll(DefaultValueFinder.getDefaultValues(path, pd));
      } catch (DefaultBehaviorException e) {
        exception = e;
      }
    }
    if (values.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);
    }
    if (values.isEmpty() && pd.hasOption(PropertyOption.MANDATORY)) {
      // The values maybe empty because of a previous exception.
      if (exception == null) {
        exception = new PropertyIsMandatoryException(pd);
      }
    }
    // TODO: If an exception occurs should we leave the property
    // empty?
    properties.put(pd, values);
    if (exception != null) {
      throw exception;
    }
  }
  // Gets the attribute associated with a property from a ConfigEntry.
  private static List<String> 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>() {
      public String decode(AttributeValue value) throws DirectoryException {
        return value.getStringValue();
      }
    };
    List<String> values = new LinkedList<String>();
    try {
      configEntry.getEntry().getAttributeValues(type, decoder, values);
    } catch (DirectoryException e) {
      // Should not happen.
      throw new RuntimeException(e);
    }
    return values;
  }
  // Gets a config entry required for a managed object and throws a
  // config exception on failure.
  private static ConfigEntry getManagedObjectConfigEntry(DN dn)
      throws ConfigException {
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(dn);
    } catch (ConfigException e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT;
      String message = getMessage(msgID, String.valueOf(dn),
          stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    // The configuration handler is free to return null indicating
    // that the entry does not exist.
    if (configEntry == null) {
      int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST;
      String message = getMessage(msgID, String.valueOf(dn));
      throw new ConfigException(msgID, message);
    }
    return configEntry;
  }
  // The configuration entry associated with this server managed
  // object (null if root).
  private ConfigEntry configEntry;
  // The managed object's definition.
  private final ManagedObjectDefinition<?, S> definition;
@@ -303,17 +539,14 @@
  private final ManagedObjectPath<?, ?> path;
  // The managed object's properties.
  private final PropertySet properties;
  // The configuration entry associated with this server managed
  // object (null if root).
  private ConfigEntry configEntry;
  private final Map<PropertyDefinition<?>, SortedSet<?>> properties;
  // Create an new server side managed object.
  private ServerManagedObject(ManagedObjectPath path,
      ManagedObjectDefinition<?, S> d, PropertySet properties,
      ManagedObjectDefinition<?, S> d,
      Map<PropertyDefinition<?>, SortedSet<?>> properties,
      ConfigEntry configEntry) {
    this.definition = d;
    this.path = path;
@@ -338,8 +571,7 @@
   */
  public <M extends Configuration> void deregisterAddListener(
      InstantiableRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener)
      throws IllegalArgumentException {
      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
@@ -362,8 +594,7 @@
   *           with this managed object's definition.
   */
  public <M extends Configuration> void deregisterAddListener(
      OptionalRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener)
      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
      throws IllegalArgumentException {
    validateRelationDefinition(d);
@@ -408,8 +639,7 @@
   */
  public <M extends Configuration> void deregisterDeleteListener(
      InstantiableRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener)
      throws IllegalArgumentException {
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
@@ -433,8 +663,7 @@
   */
  public <M extends Configuration> void deregisterDeleteListener(
      OptionalRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener)
      throws IllegalArgumentException {
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path);
@@ -465,7 +694,6 @@
      InstantiableRelationDefinition<?, M> d, String name)
      throws IllegalArgumentException, ConfigException {
    validateRelationDefinition(d);
    ManagedObjectPath childPath = path.child(d, name);
    return getChild(childPath, d);
  }
@@ -489,11 +717,9 @@
   *           could not be decoded.
   */
  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
      OptionalRelationDefinition<?, M> d)
      throws IllegalArgumentException, ConfigException {
      OptionalRelationDefinition<?, M> d) throws IllegalArgumentException,
      ConfigException {
    validateRelationDefinition(d);
    // Get the configuration entry.
    ManagedObjectPath childPath = path.child(d);
    return getChild(childPath, d);
  }
@@ -517,11 +743,9 @@
   *           could not be decoded.
   */
  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
      SingletonRelationDefinition<?, M> d)
      throws IllegalArgumentException, ConfigException {
      SingletonRelationDefinition<?, M> d) throws IllegalArgumentException,
      ConfigException {
    validateRelationDefinition(d);
    // Get the configuration entry.
    ManagedObjectPath childPath = path.child(d);
    return getChild(childPath, d);
  }
@@ -582,44 +806,49 @@
  /**
   * Get the current value of the specified property.
   * <p>
   * If the value has been modified then the new value is returned,
   * otherwise the original value will be returned.
   * Get the effective value of the specified property. If the
   * property is multi-valued then just the first value is returned.
   * If the property does not have a value then its default value is
   * returned if it has one, or <code>null</code> indicating that
   * any default behavior is applicable.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns the property's current value, or
   *         <code>null</code> if there is no value(s) associated
   *         with the property and any default behavior is
   *         applicable.
   * @return Returns the property's effective value, or
   *         <code>null</code> indicating that any default behavior
   *         is applicable.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
   */
  public <T> T getPropertyValue(PropertyDefinition<T> d)
      throws IllegalArgumentException {
    return properties.getPropertyValue(d);
    Set<T> values = getPropertyValues(d);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
  /**
   * Get the current values of the specified property.
   * <p>
   * If the property has been modified then the new values are
   * returned, otherwise the original values will be returned.
   * Get the effective values of the specified property. If the
   * property does not have any values then its default values are
   * returned if it has any, or an empty set indicating that any
   * default behavior is applicable.
   *
   * @param <T>
   *          The type of the property to be retrieved.
   * @param d
   *          The property to be retrieved.
   * @return Returns a newly allocated set containing a copy of the
   *         property's current values. An empty set indicates that
   *         the property has no values defined and any default
   *         behavior is applicable.
   *         property's effective values. An empty set indicates that
   *         the property has no default values defined and any
   *         default behavior is applicable.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with this
   *           managed object's definition.
@@ -627,7 +856,10 @@
  @SuppressWarnings("unchecked")
  public <T> SortedSet<T> getPropertyValues(PropertyDefinition<T> d)
      throws IllegalArgumentException {
    return properties.getPropertyValues(d);
    if (!properties.containsKey(d)) {
      throw new IllegalArgumentException("Unknown property " + d.getName());
    }
    return new TreeSet<T>((SortedSet<T>) properties.get(d));
  }
@@ -721,14 +953,14 @@
   */
  public <M extends Configuration> void registerAddListener(
      InstantiableRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener)
      throws IllegalArgumentException, ConfigException {
      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
      ConfigException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path,
        d, listener);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
        listener);
    if (relationEntry != null) {
      relationEntry.registerAddListener(adaptor);
@@ -758,13 +990,12 @@
   *           with this managed object's definition.
   */
  public <M extends Configuration> void registerAddListener(
      OptionalRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener)
      OptionalRelationDefinition<?, M> d, ConfigurationAddListener<M> listener)
      throws IllegalArgumentException {
    validateRelationDefinition(d);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path,
        d, listener);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
        listener);
    configEntry.registerAddListener(adaptor);
  }
@@ -779,8 +1010,8 @@
   */
  public void registerChangeListener(
      ConfigurationChangeListener<? super S> listener) {
    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(
        path, definition, listener);
    ConfigChangeListener adaptor = new ConfigChangeListenerAdaptor<S>(path,
        definition, listener);
    configEntry.registerChangeListener(adaptor);
  }
@@ -805,14 +1036,14 @@
   */
  public <M extends Configuration> void registerDeleteListener(
      InstantiableRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener)
      throws IllegalArgumentException, ConfigException {
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
      ConfigException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigEntry relationEntry = getListenerConfigEntry(baseDN);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(
        path, d, listener);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
        listener);
    if (relationEntry != null) {
      relationEntry.registerDeleteListener(adaptor);
@@ -843,12 +1074,11 @@
   */
  public <M extends Configuration> void registerDeleteListener(
      OptionalRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener)
      throws IllegalArgumentException {
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(
        path, d, listener);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
        listener);
    configEntry.registerDeleteListener(adaptor);
  }
@@ -870,8 +1100,8 @@
  // Deregister an add listener.
  private <M extends Configuration> void deregisterAddListener(
      DN baseDN, ConfigurationAddListener<M> listener) {
  private <M extends Configuration> void deregisterAddListener(DN baseDN,
      ConfigurationAddListener<M> listener) {
    try {
      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
      if (configEntry != null) {
@@ -886,8 +1116,7 @@
      }
    } catch (ConfigException e) {
      // Ignore the exception since this implies deregistration.
      if (debugEnabled())
      {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
@@ -901,8 +1130,7 @@
    try {
      ConfigEntry configEntry = getListenerConfigEntry(baseDN);
      if (configEntry != null) {
        for (ConfigDeleteListener l : configEntry
            .getDeleteListeners()) {
        for (ConfigDeleteListener l : configEntry.getDeleteListeners()) {
          if (l instanceof ConfigDeleteListenerAdaptor) {
            ConfigDeleteListenerAdaptor adaptor =
              (ConfigDeleteListenerAdaptor) l;
@@ -914,8 +1142,7 @@
      }
    } catch (ConfigException e) {
      // Ignore the exception since this implies deregistration.
      if (debugEnabled())
      {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
    }
@@ -945,15 +1172,13 @@
  // Gets a config entry required for a listener and throws a config
  // exception on failure or returns null if the entry does not exist.
  private ConfigEntry getListenerConfigEntry(DN dn)
      throws ConfigException {
  private ConfigEntry getListenerConfigEntry(DN dn) throws ConfigException {
    // Attempt to retrieve the listener base entry.
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(dn);
    } catch (ConfigException e) {
      if (debugEnabled())
      {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
@@ -968,48 +1193,14 @@
  // Gets a config entry required for a managed object and throws a
  // config exception on failure.
  private ConfigEntry getManagedObjectConfigEntry(DN dn)
      throws ConfigException {
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(dn);
    } catch (ConfigException e) {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      int msgID = AdminMessages.MSGID_ADMIN_CANNOT_GET_MANAGED_OBJECT;
      String message = getMessage(msgID, String.valueOf(dn),
          stackTraceToSingleLineString(e));
      throw new ConfigException(msgID, message, e);
    }
    // The configuration handler is free to return null indicating
    // that the entry does not exist.
    if (configEntry == null) {
      int msgID = AdminMessages.MSGID_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST;
      String message = getMessage(msgID, String.valueOf(dn));
      throw new ConfigException(msgID, message);
    }
    return configEntry;
  }
  // Validate that a relation definition belongs to this managed
  // object.
  private void validateRelationDefinition(RelationDefinition<?, ?> rd)
      throws IllegalArgumentException {
    RelationDefinition tmp = definition.getRelationDefinition(rd
        .getName());
    RelationDefinition tmp = definition.getRelationDefinition(rd.getName());
    if (tmp != rd) {
      throw new IllegalArgumentException("The relation "
          + rd.getName() + " is not associated with a "
          + definition.getName());
      throw new IllegalArgumentException("The relation " + rd.getName()
          + " is not associated with a " + definition.getName());
    }
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ManagedObjectPathTest.java
@@ -116,6 +116,17 @@
  /**
   * Tests that the empty path represents has no relation.
   */
  @Test
  public void testEmptyPathHasNoRelation() {
    ManagedObjectPath<?, ?> path = ManagedObjectPath.emptyPath();
    assertEquals(path.getRelationDefinition(), null);
  }
  /**
   * Tests that the empty path has a string representation of "/".
   */
  @Test
@@ -152,6 +163,8 @@
    assertEquals(child.parent(), path);
    assertEquals(child.getManagedObjectDefinition(), GlobalCfgDefn
        .getInstance());
    assertEquals(child.getRelationDefinition(), RootCfgDefn.getInstance()
        .getGlobalConfigurationRelationDefinition());
    assertEquals(child.toString(), "/relation=global-configuration");
    assertEquals(child, ManagedObjectPath
        .valueOf("/relation=global-configuration"));
@@ -175,6 +188,8 @@
    assertEquals(child.parent(), path);
    assertEquals(child.getManagedObjectDefinition(), ConnectionHandlerCfgDefn
        .getInstance());
    assertEquals(child.getRelationDefinition(), RootCfgDefn.getInstance()
        .getConnectionHandlersRelationDefinition());
    assertEquals(child.toString(),
        "/relation=connection-handler+name=LDAP connection handler");
    assertEquals(child, ManagedObjectPath
@@ -200,6 +215,8 @@
    assertEquals(child.parent(), path);
    assertEquals(child.getManagedObjectDefinition(),
        LDAPConnectionHandlerCfgDefn.getInstance());
    assertEquals(child.getRelationDefinition(), RootCfgDefn.getInstance()
        .getConnectionHandlersRelationDefinition());
    assertEquals(
        child.toString(),
        "/relation=connection-handler+type=ldap-connection-handler+name=LDAP connection handler");
@@ -229,6 +246,9 @@
    assertEquals(domain.parent(2), root);
    assertEquals(domain.getManagedObjectDefinition(), MultimasterDomainCfgDefn
        .getInstance());
    assertEquals(domain.getRelationDefinition(),
        MultimasterSynchronizationProviderCfgDefn.getInstance()
            .getMultimasterDomainsRelationDefinition());
    assertEquals(
        domain.toString(),
        "/relation=synchronization-provider+type=multimaster-synchronization-provider+name=MMR/relation=multimaster-domain+name=Domain");
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/RelativeInheritedDefaultBehaviorProviderTest.java
@@ -27,61 +27,104 @@
package org.opends.server.admin;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
/**
 * RelativeInheritedDefaultBehaviorProvider Tester.
 */
public class RelativeInheritedDefaultBehaviorProviderTest {
  static private final String PROPERTY_NAME = "test-property";
  static private final int OFFSET = 0;
  private static final int OFFSET = 0;
  private RelativeInheritedDefaultBehaviorProvider<Object> ridbp = null;
  private static final TestParentCfgDefn d = TestParentCfgDefn.getInstance();
  private RelativeInheritedDefaultBehaviorProvider<Integer> ridbp = null;
  /**
   * Creates the default behavior provider.
   */
  @BeforeClass
  public void setUp() {
    this.ridbp = new RelativeInheritedDefaultBehaviorProvider<Object>(OFFSET, PROPERTY_NAME);
    this.ridbp = new RelativeInheritedDefaultBehaviorProvider<Integer>(d, d
        .getMaximumLengthPropertyDefinition().getName(), OFFSET);
  }
  /**
   * Tests the accept method.
   */
  @Test
  public void testAccept() {
    ridbp.accept(new DefaultBehaviorProviderVisitor<Object,Object,Object>(){
    ridbp.accept(new DefaultBehaviorProviderVisitor<Integer, Object, Object>() {
      public Object visitAbsoluteInherited(AbsoluteInheritedDefaultBehaviorProvider d, Object o) {
      public Object visitAbsoluteInherited(
          AbsoluteInheritedDefaultBehaviorProvider d, Object o) {
        return null;
      }
      public Object visitAlias(AliasDefaultBehaviorProvider d, Object o) {
        return null;
      }
      public Object visitDefined(DefinedDefaultBehaviorProvider d, Object o) {
        return null;
      }
      public Object visitRelativeInherited(RelativeInheritedDefaultBehaviorProvider d, Object o) {
      public Object visitRelativeInherited(
          RelativeInheritedDefaultBehaviorProvider d, Object o) {
        return null;
      }
      public Object visitUndefined(UndefinedDefaultBehaviorProvider d, Object o) {
        return null;
      }
    }, new Object());
  }
  /**
   * Tests the getManagedObjectPath method.
   */
  @Test
  public void testGetManagedObjectPath() {
    ridbp.getManagedObjectPath(ManagedObjectPath.emptyPath());
    assertEquals(ridbp.getManagedObjectPath(ManagedObjectPath.emptyPath()),
        ManagedObjectPath.emptyPath());
  }
  /**
   * Tests the getPropertyDefinition method.
   */
  @Test
  public void testGetPropertyName() {
    assertEquals(ridbp.getPropertyName(), PROPERTY_NAME);
  public void testGetPropertyDefinition() {
    assertEquals(ridbp.getPropertyName(), d
        .getMaximumLengthPropertyDefinition().getName());
  }
  /**
   * Tests the getRelativeOffset method.
   */
  @Test
  public void testGetRelativeOffset() {
    assertEquals(ridbp.getRelativeOffset(), OFFSET);
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfg.java
@@ -51,4 +51,22 @@
   * @return Returns the value of the "heartbeat-interval" property.
   */
  long getHeartbeatInterval();
  /**
   * Get the "maximum-length" property.
   *
   * @return Returns the value of the "maximum-length" property.
   */
  int getMaximumLength();
  /**
   * Get the "minimum-length" property.
   *
   * @return Returns the value of the "minimum-length" property.
   */
  int getMinimumLength();
}
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgClient.java
@@ -63,4 +63,46 @@
   *           If the new value is invalid.
   */
  void setHeartbeatInterval(Long value) throws IllegalPropertyValueException;
  /**
   * Get the "maximum-length" property.
   *
   * @return Returns the value of the "maximum-length" property.
   */
  int getMaximumLength();
  /**
   * Set the "maximum-length" property.
   *
   * @param value
   *          The value of the "maximum-length" property.
   * @throws IllegalPropertyValueException
   *           If the new value is invalid.
   */
  void setMaximumLength(Integer value) throws IllegalPropertyValueException;
  /**
   * Get the "minimum-length" property.
   *
   * @return Returns the value of the "minimum-length" property.
   */
  int getMinimumLength();
  /**
   * Set the "minimum-length" property.
   *
   * @param value
   *          The value of the "minimum-length" property.
   * @throws IllegalPropertyValueException
   *           If the new value is invalid.
   */
  void setMinimumLength(Integer value) throws IllegalPropertyValueException;
}
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestChildCfgDefn.java
@@ -32,6 +32,7 @@
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.server.ServerManagedObject;
import org.opends.server.types.DN;
@@ -47,9 +48,40 @@
  // The singleton configuration definition instance.
  private static final TestChildCfgDefn INSTANCE = new TestChildCfgDefn();
  // The "maximum-length" property definition.
  private static final IntegerPropertyDefinition PD_MAXIMUM_LENGTH;
  // The "minimum-length" property definition.
  private static final IntegerPropertyDefinition PD_MINIMUM_LENGTH;
  // The "heartbeat-interval" property definition.
  private static final DurationPropertyDefinition PD_HEARTBEAT_INTERVAL;
  // Build the "maximum-length" property definition.
  static {
    IntegerPropertyDefinition.Builder builder = IntegerPropertyDefinition
        .createBuilder(INSTANCE, "maximum-length");
    DefaultBehaviorProvider<Integer> provider = new RelativeInheritedDefaultBehaviorProvider<Integer>(
        TestParentCfgDefn.getInstance(), "maximum-length", 1);
    builder.setDefaultBehaviorProvider(provider);
    builder.setLowerLimit(0);
    PD_MAXIMUM_LENGTH = builder.getInstance();
    INSTANCE.registerPropertyDefinition(PD_MAXIMUM_LENGTH);
  }
  // Build the "minimum-length" property definition.
  static {
    IntegerPropertyDefinition.Builder builder = IntegerPropertyDefinition
        .createBuilder(INSTANCE, "minimum-length");
    DefaultBehaviorProvider<Integer> provider = new AbsoluteInheritedDefaultBehaviorProvider<Integer>(
        ManagedObjectPath.valueOf("/relation=test-parent+name=test parent 2"),
        "minimum-length");
    builder.setDefaultBehaviorProvider(provider);
    builder.setLowerLimit(0);
    PD_MINIMUM_LENGTH = builder.getInstance();
    INSTANCE.registerPropertyDefinition(PD_MINIMUM_LENGTH);
  }
  // Build the "heartbeat-interval" property definition.
  static {
    DurationPropertyDefinition.Builder builder = DurationPropertyDefinition
@@ -127,6 +159,28 @@
  /**
   * Get the "maximum-length" property definition.
   *
   * @return Returns the "maximum-length" property definition.
   */
  public IntegerPropertyDefinition getMaximumLengthPropertyDefinition() {
    return PD_MAXIMUM_LENGTH;
  }
  /**
   * Get the "minimum-length" property definition.
   *
   * @return Returns the "minimum-length" property definition.
   */
  public IntegerPropertyDefinition getMinimumLengthPropertyDefinition() {
    return PD_MINIMUM_LENGTH;
  }
  /**
   * Managed object client implementation.
   */
  private static class TestChildCfgClientImpl implements TestChildCfgClient {
@@ -167,6 +221,46 @@
    /**
     * {@inheritDoc}
     */
    public int getMaximumLength() {
      return impl.getPropertyValue(INSTANCE
          .getMaximumLengthPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public void setMaximumLength(Integer value) {
      impl.setPropertyValue(INSTANCE.getMaximumLengthPropertyDefinition(),
          value);
    }
    /**
     * {@inheritDoc}
     */
    public int getMinimumLength() {
      return impl.getPropertyValue(INSTANCE
          .getMinimumLengthPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public void setMinimumLength(Integer value) {
      impl.setPropertyValue(INSTANCE.getMinimumLengthPropertyDefinition(),
          value);
    }
    /**
     * {@inheritDoc}
     */
    public ManagedObjectDefinition<? extends TestChildCfgClient, ? extends TestChildCfg> definition() {
      return INSTANCE;
    }
@@ -187,7 +281,8 @@
     */
    public void commit() throws ConcurrentModificationException,
        OperationRejectedException, AuthorizationException,
        CommunicationException {
        CommunicationException, ManagedObjectAlreadyExistsException,
        MissingMandatoryPropertiesException {
      impl.commit();
    }
  }
@@ -225,6 +320,26 @@
    /**
     * {@inheritDoc}
     */
    public int getMaximumLength() {
      return impl.getPropertyValue(INSTANCE
          .getMaximumLengthPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public int getMinimumLength() {
      return impl.getPropertyValue(INSTANCE
          .getMinimumLengthPropertyDefinition());
    }
    /**
     * {@inheritDoc}
     */
    public ManagedObjectDefinition<? extends TestChildCfgClient, ? extends TestChildCfg> definition() {
      return INSTANCE;
    }
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgClient.java
@@ -28,6 +28,8 @@
import java.util.Collection;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.client.AuthorizationException;
@@ -111,36 +113,17 @@
   *          The definition of the test child to be created.
   * @param name
   *          The name of the new test child.
   * @param p
   *          A property provider which can be used to initialize the
   *          property values of the new test child.
   * @param exceptions
   *          An optional collection in which to place any {@link
   *          DefaultBehaviorException}s that occurred whilst
   *          attempting to determine the default values of the test
   *          child. This argument can be <code>null<code>.
   * @return Returns a new test child instance representing the test
   *         child that was created.
   * @throws ManagedObjectDecodingException
   *           If the test child could not be created because one or
   *           more of its properties are invalid.
   * @throws ManagedObjectAlreadyExistsException
   *           If the test child cannot be created because it already
   *           exists on the server.
   * @throws ConcurrentModificationException
   *           If this test parent has been removed from the server by
   *           another client.
   * @throws OperationRejectedException
   *           If the server refuses to create the test child due to
   *           some server-side constraint which cannot be satisfied.
   * @throws AuthorizationException
   *           If the server refuses to create the test child because
   *           the client does not have the correct privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <C extends TestChildCfgClient> C createTestChild(
      ManagedObjectDefinition<C, ?> d, String name, PropertyProvider p)
      throws ManagedObjectDecodingException,
      ManagedObjectAlreadyExistsException, ConcurrentModificationException,
      OperationRejectedException, AuthorizationException,
      CommunicationException;
      ManagedObjectDefinition<C, ?> d, String name,
      Collection<DefaultBehaviorException> exceptions);
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/TestParentCfgDefn.java
@@ -28,11 +28,14 @@
import java.util.Collection;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.server.ServerManagedObject;
import org.opends.server.admin.std.meta.RootCfgDefn;
@@ -266,7 +269,8 @@
     */
    public void commit() throws ConcurrentModificationException,
        OperationRejectedException, AuthorizationException,
        CommunicationException {
        CommunicationException, ManagedObjectAlreadyExistsException,
        MissingMandatoryPropertiesException {
      impl.commit();
    }
@@ -276,13 +280,10 @@
     * {@inheritDoc}
     */
    public <C extends TestChildCfgClient> C createTestChild(
        ManagedObjectDefinition<C, ?> d, String name, PropertyProvider p)
        throws ManagedObjectDecodingException,
        ManagedObjectAlreadyExistsException, ConcurrentModificationException,
        OperationRejectedException, AuthorizationException,
        CommunicationException {
        ManagedObjectDefinition<C, ?> d, String name,
        Collection<DefaultBehaviorException> exceptions) {
      return impl.createChild(INSTANCE.getTestChildrenRelationDefinition(), d,
          name, p).getConfiguration();
          name, exceptions).getConfiguration();
    }
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/PropertySetTest.java
@@ -140,16 +140,6 @@
  }
  /**
   * Tests basic property set creation with a string
   * property provider
   */
  @Test
  public void testCreate1() {
    PropertySet ps = createTestPropertySet(StringPropertyProvider.DEFAULT_PROVIDER);
    assertNotNull(ps);
  }
  /**
   * Tests setting and getting property values
   * @param pd PropertyDefinition for which values are set and gotten
   * @param values property values to test
@@ -229,9 +219,9 @@
    PropertySet ps = createTestPropertySet();
    Property<T> p = ps.getProperty(pd);
    SortedSet<T> ss = p.getDefaultValues();
    assertTrue(ss.size() == expected.size());
    assertEquals(ss.size(), expected.size());
    for (T v : expected) {
      ss.contains(v);
      assertTrue(ss.contains(v), "does not contain " + v);
    }
  }
@@ -282,9 +272,9 @@
    // before setting any values, the effective data
    // is supposed to just be the defaults
    Set<T> ev1 = p.getEffectiveValues();
    assertTrue(ev1.size() == expectedDefaults.size());
    assertEquals(ev1.size(), expectedDefaults.size());
    for(T v : ev1) {
      assertTrue(expectedDefaults.contains(v));
      assertTrue(expectedDefaults.contains(v), "does not contain " + v);
    }
    // now set some data and make sure the effective
@@ -292,9 +282,9 @@
    ps.setPropertyValues(pd, newValues);
    Set<T> ev2 = p.getEffectiveValues();
    assertTrue(ev2.size() == newValues.size());
    assertEquals(ev2.size(), newValues.size());
    for(T v : ev2) {
      assertTrue(newValues.contains(v));
      assertTrue(newValues.contains(v), "does not contain " + v);
    }
  }
@@ -391,21 +381,27 @@
  }
  private PropertySet createTestPropertySet(PropertyProvider pp) {
    return PropertySet.create(
            new TestManagedObjectDefinition<ConfigurationClient, Configuration>("test-mod",null),
            pp,
            new InheritedDefaultValueProvider() {
              public ManagedObjectPath getManagedObjectPath() {
                System.out.println("getManagedObjectPath");
                return null;
              }
              public Collection<?> getDefaultPropertyValues(ManagedObjectPath path, String propertyName) throws OperationsException {
                System.out.println("getDefPrV path=" + path + " pn=" + propertyName);
                return null;
              }
            },
            new HashSet<PropertyException>()
    );
    ManagedObjectDefinition<?, ?> d = new TestManagedObjectDefinition<ConfigurationClient, Configuration>("test-mod", null);
    PropertySet ps = new PropertySet();
    for (PropertyDefinition<?> pd : d.getPropertyDefinitions()) {
      addProperty(ps, pd, pp);
    }
    return ps;
  }
  private <T> void addProperty(PropertySet ps, PropertyDefinition<T> pd, PropertyProvider pp) {
    Collection<T> defaultValues = new LinkedList<T>();
    DefaultBehaviorProvider<T> dbp = pd.getDefaultBehaviorProvider();
    if (dbp instanceof DefinedDefaultBehaviorProvider) {
      DefinedDefaultBehaviorProvider<T> ddbp = (DefinedDefaultBehaviorProvider<T>) dbp;
      Collection<String> stringValues = ddbp.getDefaultValues();
      for (String sv : stringValues) {
        defaultValues.add(pd.decodeValue(sv));
      }
    }
    Collection<T> activeValues = pp.getPropertyValues(pd);
    ps.addProperty(pd, defaultValues, activeValues);
  }
  private class TestManagedObjectDefinition<C extends ConfigurationClient,S extends Configuration> extends
@@ -476,23 +472,5 @@
    return createTestPropertySet(PropertyProvider.DEFAULT_PROVIDER);
  }
  private PropertySet createTestPropertySet(StringPropertyProvider pp) {
    return PropertySet.create(
            new TestManagedObjectDefinition<ConfigurationClient, Configuration>("test-mod", null),
            pp,
            new InheritedDefaultValueProvider() {
              public ManagedObjectPath getManagedObjectPath() {
                System.out.println("getManagedObjectPath");
                return null;
              }
              public Collection<?> getDefaultPropertyValues(ManagedObjectPath path, String propertyName) throws OperationsException {
                System.out.println("getDefPrV path=" + path + " pn=" + propertyName);
                return null;
              }
            },
            new HashSet<PropertyException>()
    );
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/LDAPClientTest.java
@@ -41,7 +41,6 @@
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.MockLDAPProfile;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.TestChildCfgClient;
import org.opends.server.admin.TestChildCfgDefn;
import org.opends.server.admin.TestParentCfgClient;
@@ -77,13 +76,16 @@
      "ds-cfg-maximum-length: 20000", "",
      "dn:cn=test-children,cn=test parent 1,cn=test-parents",
      "objectclass: top", "objectclass: ds-cfg-branch", "cn: test-children",
      "", "dn:cn=test-children,cn=test parent 2,cn=test-parents",
      "objectclass: top", "objectclass: ds-cfg-branch", "cn: test-children",
      "",
      "dn: cn=test child 1,cn=test-children,cn=test parent 1,cn=test-parents",
      "objectclass: top", "objectclass: ds-cfg-test-child", "cn: test child 1",
      "",
      "dn: cn=test child 2,cn=test-children,cn=test parent 1,cn=test-parents",
      "objectclass: top", "objectclass: ds-cfg-test-child", "cn: test child 2",
      "ds-cfg-heartbeat-interval: 12345s", "" };
      "ds-cfg-heartbeat-interval: 12345s", "ds-cfg-minimum-length: 11111",
      "ds-cfg-maximum-length: 22222", "" };
@@ -159,7 +161,6 @@
   */
  @Test
  public void testCreateChildManagedObject() throws Exception {
    MockPropertyProvider p = new MockPropertyProvider();
    CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
        "cn=test child 3,cn=test-children,cn=test parent 1,cn=test-parents");
    c.importLDIF(TEST_LDIF);
@@ -167,13 +168,16 @@
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child");
    // LDAP encoding uses base unit.
    p.addProperty("heartbeat-interval", "10s");
    c.addExpectedAttribute("ds-cfg-heartbeat-interval", "10000ms");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    parent.createTestChild(TestChildCfgDefn.getInstance(), "test child 3", p);
    TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
        .getInstance(), "test child 3", null);
    child.setHeartbeatInterval(10000L);
    child.commit();
    c.assertEntryIsCreated();
  }
@@ -196,8 +200,10 @@
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    parent.createTestChild(TestChildCfgDefn.getInstance(), "test child 3",
        new MockPropertyProvider());
    TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
        .getInstance(), "test child 3", null);
    child.commit();
    c.assertEntryIsCreated();
  }
@@ -231,7 +237,8 @@
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    try {
      createTestParent(ctx, "test parent 3", new MockPropertyProvider());
      TestParentCfgClient parent = createTestParent(ctx, "test parent 3");
      parent.commit();
    } catch (Exception e) {
      Assert.assertEquals(e.getClass(), expected);
    }
@@ -247,21 +254,20 @@
   */
  @Test
  public void testCreateTopLevelManagedObject() throws Exception {
    MockPropertyProvider p = new MockPropertyProvider();
    CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
        "cn=test parent 3,cn=test-parents");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test parent 3");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-parent");
    p.addProperty("maximum-length", "54321");
    c.addExpectedAttribute("ds-cfg-maximum-length", "54321");
    p.addProperty("minimum-length", "12345");
    c.addExpectedAttribute("ds-cfg-minimum-length", "12345");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    createTestParent(ctx, "test parent 3", p);
    TestParentCfgClient parent = createTestParent(ctx, "test parent 3");
    parent.setMaximumLength(54321);
    parent.setMinimumLength(12345);
    parent.commit();
    c.assertEntryIsCreated();
  }
@@ -283,7 +289,8 @@
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    createTestParent(ctx, "test parent 3", new MockPropertyProvider());
    TestParentCfgClient parent = createTestParent(ctx, "test parent 3");
    parent.commit();
    c.assertEntryIsCreated();
  }
@@ -302,6 +309,8 @@
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 2");
    Assert.assertEquals(child.getHeartbeatInterval(), 12345000);
    Assert.assertEquals(child.getMinimumLength(), 11111);
    Assert.assertEquals(child.getMaximumLength(), 22222);
  }
@@ -318,6 +327,8 @@
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.getTestChild("test child 1");
    Assert.assertEquals(child.getHeartbeatInterval(), 1000);
    Assert.assertEquals(child.getMinimumLength(), 10000);
    Assert.assertEquals(child.getMaximumLength(), 456);
  }
@@ -565,16 +576,84 @@
  /**
   * Tests retrieval of relative inherited default values.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test
  public void testInheritedDefaultValues1() throws Exception {
    CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
        "cn=test child 3,cn=test-children,cn=test parent 1,cn=test-parents");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child 3");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    TestParentCfgClient parent = getTestParent(ctx, "test parent 1");
    TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
        .getInstance(), "test child 3", null);
    // Inherits from parent (test parent 1).
    Assert.assertEquals(child.getMinimumLength(), 10000);
    // Inherits from test parent 2.
    Assert.assertEquals(child.getMaximumLength(), 456);
    // Check that the default values are not committed.
    child.commit();
    c.assertEntryIsCreated();
  }
  /**
   * Tests retrieval of relative inherited default values.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test
  public void testInheritedDefaultValues2() throws Exception {
    CreateEntryMockLDAPConnection c = new CreateEntryMockLDAPConnection(
        "cn=test child 3,cn=test-children,cn=test parent 2,cn=test-parents");
    c.importLDIF(TEST_LDIF);
    c.addExpectedAttribute("cn", "test child 3");
    c.addExpectedAttribute("objectclass", "top", "ds-cfg-test-child");
    ManagementContext ctx = LDAPManagementContext.createFromContext(c,
        new MockLDAPProfile());
    TestParentCfgClient parent = getTestParent(ctx, "test parent 2");
    TestChildCfgClient child = parent.createTestChild(TestChildCfgDefn
        .getInstance(), "test child 3", null);
    // Inherits from parent (test parent 2).
    Assert.assertEquals(child.getMinimumLength(), 10000);
    // Inherits from test parent 2.
    Assert.assertEquals(child.getMaximumLength(), 20000);
    // Check that the default values are not committed.
    child.commit();
    c.assertEntryIsCreated();
  }
  // Create the named test parent managed object.
  private void createTestParent(ManagementContext context, String name,
      PropertyProvider p) throws ManagedObjectDecodingException,
  private TestParentCfgClient createTestParent(ManagementContext context,
      String name) throws ManagedObjectDecodingException,
      AuthorizationException, ManagedObjectAlreadyExistsException,
      ConcurrentModificationException, OperationRejectedException,
      CommunicationException {
    ManagedObject<RootCfgClient> root = context
        .getRootConfigurationManagedObject();
    root.createChild(TestParentCfgDefn.RD_TEST_PARENT, TestParentCfgDefn
        .getInstance(), name, p);
    return root.createChild(TestParentCfgDefn.RD_TEST_PARENT,
        TestParentCfgDefn.getInstance(), name, null).getConfiguration();
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/client/ldap/MockPropertyProvider.java
File was deleted