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

matthew_swift
03.37.2009 95df5cfdba474acb03076953e992b898fbb277a8
opends/resource/admin/abbreviations.xsl
@@ -22,7 +22,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2008 Sun Microsystems, Inc.
  !      Copyright 2008-2009 Sun Microsystems, Inc.
  ! -->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -52,7 +52,7 @@
              or $value = 'md5' or $value = 'sha1' or $value = 'sha256'
              or $value = 'sha384' or $value = 'sha512' or $value = 'tls'
              or $value = 'des' or $value = 'aes' or $value = 'rc4'
              or $value = 'db' or $value = 'snmp'
              or $value = 'db' or $value = 'snmp' or $value = 'qos'
             "/>
  </xsl:template>
</xsl:stylesheet>
opends/resource/admin/admin.xsd
@@ -791,6 +791,19 @@
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
            <xsd:attribute name="unique" type="xsd:boolean"
              use="optional" default="false">
              <xsd:annotation>
                <xsd:documentation>
                  Indicates whether or not this relation contains
                  unique members. If set to true then each
                  referenced managed object must have a distinct type.
                  In other words, there must not be more than one
                  referenced managed object having the same type. By
                  default, properties are single-valued.
                </xsd:documentation>
              </xsd:annotation>
            </xsd:attribute>
            <xsd:attribute name="plural-name" type="tns:name-type"
              use="optional">
              <xsd:annotation>
opends/resource/admin/clientMO.xsl
@@ -22,7 +22,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2008 Sun Microsystems, Inc.
  !      Copyright 2008-2009 Sun Microsystems, Inc.
  ! -->
<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
  xmlns:admpp="http://www.opends.org/admin-preprocessor"
@@ -285,34 +285,64 @@
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:call-template name="add-java-comment2">
          <xsl:with-param name="indent" select="2" />
          <xsl:with-param name="content"
            select="concat(
                       '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 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 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;',
                       '@throws IllegalManagedObjectNameException&#xa;',
                       '         If the name of the new ', $ufn,' is invalid.&#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, ? extends ', $java-class-name,'Cfg&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions) throws IllegalManagedObjectNameException;&#xa;')" />
        <xsl:choose>
          <xsl:when test="string(adm:one-to-many/@unique) != 'true'">
            <xsl:call-template name="add-java-comment2">
              <xsl:with-param name="indent" select="2" />
              <xsl:with-param name="content"
                select="concat(
                           '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 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 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;',
                           '@throws IllegalManagedObjectNameException&#xa;',
                           '         If the name of the new ', $ufn,' is invalid.&#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, ? extends ', $java-class-name,'Cfg&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions) throws IllegalManagedObjectNameException;&#xa;')" />
          </xsl:when>
          <xsl:when test="string(adm:one-to-many/@unique) = 'true'">
            <xsl:call-template name="add-java-comment2">
              <xsl:with-param name="indent" select="2" />
              <xsl:with-param name="content"
                select="concat(
                           '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 created.&#xa;',
                           '@param d&#xa;',
                           '         The definition of the ', $ufn,' to be created.&#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, ? extends ', $java-class-name,'Cfg&gt; d, Collection&lt;DefaultBehaviorException&gt; exceptions);&#xa;')" />
          </xsl:when>
        </xsl:choose>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -424,7 +454,7 @@
            org.opends.server.admin.client.OperationRejectedException
          </import>
        </xsl:if>
        <xsl:if test="$this-local-relations/adm:one-to-many">
        <xsl:if test="$this-local-relations/adm:one-to-many[not(@unique = 'true')]">
          <import>
            org.opends.server.admin.client.IllegalManagedObjectNameException
          </import>
opends/resource/admin/metaMO.xsl
@@ -22,7 +22,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<xsl:stylesheet version="1.0" xmlns:adm="http://www.opends.org/admin"
  xmlns:admpp="http://www.opends.org/admin-preprocessor"
@@ -848,9 +848,12 @@
      <xsl:when test="adm:one-to-zero-or-one">
        <xsl:text>OptionalRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:when test="adm:one-to-many">
      <xsl:when test="string(adm:one-to-many/@unique) != 'true'">
        <xsl:text>InstantiableRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:when test="string(adm:one-to-many/@unique) = 'true'">
        <xsl:text>SetRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes">
          <xsl:value-of
@@ -893,9 +896,12 @@
        <xsl:when test="adm:one-to-zero-or-one">
          <xsl:text>OptionalRelationDefinition</xsl:text>
        </xsl:when>
        <xsl:when test="adm:one-to-many">
        <xsl:when test="string(adm:one-to-many/@unique) != 'true'">
          <xsl:text>InstantiableRelationDefinition</xsl:text>
        </xsl:when>
        <xsl:when test="string(adm:one-to-many/@unique) = 'true'">
          <xsl:text>SetRelationDefinition</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:message terminate="yes">
            <xsl:value-of
@@ -918,6 +924,12 @@
    <xsl:value-of
      select="concat($java-managed-object-name, 'CfgDefn.getInstance());&#xa;')" />
    <xsl:if test="adm:one-to-many/@naming-property">
      <xsl:if test="string(adm:one-to-many/@unique) = 'true'">
        <xsl:message terminate="yes">
          <xsl:value-of
            select="concat('Naming properties found in unique one-to-many relation &quot;', @name, '&quot;.')" />
        </xsl:message>
      </xsl:if>
      <xsl:variable name="java-property-name">
        <xsl:call-template name="name-to-java">
          <xsl:with-param name="value"
@@ -1111,9 +1123,12 @@
      <xsl:when test="adm:one-to-zero-or-one">
        <xsl:text>OptionalRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:when test="adm:one-to-many">
      <xsl:when test="string(adm:one-to-many/@unique) != 'true'">
        <xsl:text>InstantiableRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:when test="string(adm:one-to-many/@unique) = 'true'">
        <xsl:text>SetRelationDefinition&lt;</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes">
          <xsl:value-of
@@ -1259,14 +1274,29 @@
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:value-of
          select="concat('    /**&#xa;',
                         '     * {@inheritDoc}&#xa;',
                         '     */&#xa;',
                         '    public &lt;M extends ', $java-class-name, 'CfgClient&gt; M create', $java-relation-name, '(&#xa;',
                         '        ManagedObjectDefinition&lt;M, ? extends ', $java-class-name,'Cfg&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions) throws IllegalManagedObjectNameException {&#xa;',
                         '      return impl.createChild(INSTANCE.get', $java-relation-plural-name,'RelationDefinition(), d, name, exceptions).getConfiguration();&#xa;',
                         '    }&#xa;')" />
        <xsl:choose>
          <xsl:when test="string(adm:one-to-many/@unique) != 'true'">
            <xsl:value-of
              select="concat('    /**&#xa;',
                             '     * {@inheritDoc}&#xa;',
                             '     */&#xa;',
                             '    public &lt;M extends ', $java-class-name, 'CfgClient&gt; M create', $java-relation-name, '(&#xa;',
                             '        ManagedObjectDefinition&lt;M, ? extends ', $java-class-name,'Cfg&gt; d, String name, Collection&lt;DefaultBehaviorException&gt; exceptions) throws IllegalManagedObjectNameException {&#xa;',
                             '      return impl.createChild(INSTANCE.get', $java-relation-plural-name,'RelationDefinition(), d, name, exceptions).getConfiguration();&#xa;',
                             '    }&#xa;')" />
          </xsl:when>
          <xsl:when test="string(adm:one-to-many/@unique) = 'true'">
            <!--  Unique one-to-many children are named implicitly by their definition -->
            <xsl:value-of
              select="concat('    /**&#xa;',
                             '     * {@inheritDoc}&#xa;',
                             '     */&#xa;',
                             '    public &lt;M extends ', $java-class-name, 'CfgClient&gt; M create', $java-relation-name, '(&#xa;',
                             '        ManagedObjectDefinition&lt;M, ? extends ', $java-class-name,'Cfg&gt; d, Collection&lt;DefaultBehaviorException&gt; exceptions) {&#xa;',
                             '      return impl.createChild(INSTANCE.get', $java-relation-plural-name,'RelationDefinition(), d, exceptions).getConfiguration();&#xa;',
                             '    }&#xa;')" />
          </xsl:when>
        </xsl:choose>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
        <xsl:text>&#xa;</xsl:text>
@@ -1841,11 +1871,16 @@
        <xsl:if test="$this-is-hidden or $this-is-advanced">
          <import>org.opends.server.admin.ManagedObjectOption</import>
        </xsl:if>
        <xsl:if test="$this-all-relations/adm:one-to-many">
        <xsl:if test="$this-all-relations/adm:one-to-many[not(@unique = 'true')]">
          <import>
            org.opends.server.admin.InstantiableRelationDefinition
          </import>
        </xsl:if>
        <xsl:if test="$this-all-relations/adm:one-to-many[@unique = 'true']">
          <import>
            org.opends.server.admin.SetRelationDefinition
          </import>
        </xsl:if>
        <xsl:if test="$this-all-relations/adm:one-to-zero-or-one">
          <import>
            org.opends.server.admin.OptionalRelationDefinition
@@ -1912,9 +1947,11 @@
            </xsl:if>
            <xsl:if test="$this-all-relations/adm:one-to-many">
              <import>java.util.Collection</import>
              <import>
                org.opends.server.admin.client.IllegalManagedObjectNameException
              </import>
              <xsl:if test="$this-all-relations/adm:one-to-many[not(@unique = 'true')]">
                <import>
                  org.opends.server.admin.client.IllegalManagedObjectNameException
                </import>
              </xsl:if>
              <import>
                org.opends.server.admin.DefaultBehaviorException
              </import>
opends/resource/config/config.ldif
@@ -20,7 +20,7 @@
#
# CDDL HEADER END
#
#      Copyright 2006-2008 Sun Microsystems, Inc.
#      Copyright 2006-2009 Sun Microsystems, Inc.
#
#
# This file contains the primary Directory Server configuration.  It must not
@@ -1796,7 +1796,7 @@
objectClass: ds-cfg-plugin
objectClass: ds-cfg-network-group-plugin
cn: Network Group
ds-cfg-java-class: org.opends.server.plugins.NetworkGroupPlugin
ds-cfg-java-class: org.opends.server.core.networkgroups.NetworkGroupPlugin
ds-cfg-enabled: true
ds-cfg-invoke-for-internal-operations: false
ds-cfg-plugin-type: postConnect
opends/resource/schema/02-config.ldif
@@ -2103,11 +2103,6 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.435
  NAME 'ds-cfg-network-group-id'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.436
  NAME 'ds-cfg-workflow-id'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
@@ -2225,15 +2220,11 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.462
  NAME 'ds-cfg-allowed-ldap-port'
  NAME 'ds-cfg-allowed-protocol'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.463
  NAME 'ds-cfg-bind-dn-filter'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.464
  NAME 'ds-cfg-ip-address-filter'
  NAME 'ds-cfg-allowed-bind-dn'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.465
@@ -2241,11 +2232,6 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.466
  NAME 'ds-cfg-user-entry-filter'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.467
  NAME 'ds-cfg-priority'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
@@ -2271,36 +2257,11 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.472
  NAME 'ds-cfg-search-size-limit'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.473
  NAME 'ds-cfg-search-time-limit'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.474
  NAME 'ds-cfg-min-substring-length'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.475
  NAME 'ds-cfg-referral-policy'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.476
  NAME 'ds-cfg-referral-bind-policy'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.477
  NAME 'ds-cfg-referral-hop-limit'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.478
  NAME 'ds-cfg-assured-type'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
@@ -2377,16 +2338,6 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.516
  NAME 'ds-cfg-affinity-policy'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.517
  NAME 'ds-cfg-affinity-timeout'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.518
  NAME 'ds-cfg-index-extensible-matching-rule'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
@@ -3876,12 +3827,16 @@
  NAME 'ds-cfg-network-group'
  SUP top
  STRUCTURAL
  MUST ( ds-cfg-network-group-id $
  MUST ( cn $
         ds-cfg-priority $
         ds-cfg-enabled )
  MAY ( ds-cfg-workflow $
         ds-cfg-affinity-policy $
         ds-cfg-affinity-timeout )
        ds-cfg-allowed-auth-method $
        ds-cfg-allowed-protocol $
        ds-cfg-allowed-bind-dn $
        ds-cfg-allowed-client $
        ds-cfg-denied-client $
        ds-cfg-is-security-mandatory )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.177
  NAME 'ds-cfg-workflow'
@@ -3941,33 +3896,24 @@
         ds-cfg-ssl-cert-nickname )
  MAY ( ds-cfg-listen-address )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.184
  NAME 'ds-cfg-network-group-criteria'
objectClasses: ( 1.3.6.1.4.1.26027.1.2.199
  NAME 'ds-cfg-qos-policy'
  SUP top
  STRUCTURAL
  MUST (cn)
  MAY ( ds-cfg-allowed-auth-method $
        ds-cfg-allowed-ldap-port $
        ds-cfg-bind-dn-filter $
        ds-cfg-ip-address-filter $
        ds-cfg-is-security-mandatory $
        ds-cfg-user-entry-filter)
  MUST ( cn $
         ds-cfg-java-class)
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.185
  NAME 'ds-cfg-network-group-resource-limits'
  SUP top
  NAME 'ds-cfg-resource-limits-qos-policy'
  SUP ds-cfg-qos-policy
  STRUCTURAL
  MUST (cn)
  MAY ( ds-cfg-max-connections $
        ds-cfg-max-connections-from-same-ip $
        ds-cfg-max-ops-per-connection $
        ds-cfg-max-concurrent-ops-per-connection $
        ds-cfg-search-size-limit $
        ds-cfg-search-time-limit $
        ds-cfg-min-substring-length $
        ds-cfg-referral-policy $
        ds-cfg-referral-bind-policy $
        ds-cfg-referral-hop-limit)
        ds-cfg-size-limit $
        ds-cfg-time-limit $
        ds-cfg-min-substring-length)
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.186
  NAME 'ds-cfg-network-group-plugin'
@@ -3980,10 +3926,9 @@
  STRUCTURAL
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.188
  NAME 'ds-cfg-network-group-request-filtering-policy'
  SUP top
  NAME 'ds-cfg-request-filtering-qos-policy'
  SUP ds-cfg-qos-policy
  STRUCTURAL
  MUST (cn)
  MAY ( ds-cfg-allowed-operations $
        ds-cfg-allowed-attributes $
        ds-cfg-prohibited-attributes $
opends/src/admin/defn/org/opends/server/admin/std/ConnectionHandlerConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="connection-handler"
  plural-name="connection-handlers"
@@ -84,82 +84,6 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-client" multi-valued="true">
    <adm:synopsis>
      Specifies a set of host names or address masks that determine the
      clients that are allowed to establish connections to this connection
      handler.
    </adm:synopsis>
    <adm:description>
      Valid values include a host name, a fully qualified domain name, a
      domain name, an IP address, or a subnetwork with subnetwork mask.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this configuration attribute take effect
          immediately and do not interfere with connections that may
          have already been established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          All clients with addresses that do not match an address on the
          deny list are allowed. If there is no deny list, then all
          clients are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:ip-address-mask />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-allowed-client</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="denied-client" multi-valued="true">
    <adm:synopsis>
      Specifies a set of host names or address masks that determine
      the clients that are not allowed to establish connections to this
      connection handler.
    </adm:synopsis>
    <adm:description>
      Valid values include a host name, a fully qualified domain name, a
      domain name, an IP address, or a subnetwork with subnetwork mask.
      If both allowed and denied client masks are defined and a client
      connection matches one or more masks in both lists, then the
      connection is denied. If only a denied list is specified,
      then any client not matching a mask in that list is allowed.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this configuration attribute take effect
          immediately and do not interfere with connections that may
          have already been established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          If an allow list is specified, then only clients with
          addresses on the allow list are allowed. Otherwise, all
          clients are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:ip-address-mask />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-denied-client</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-reference name="allowed-client" />
  <adm:property-reference name="denied-client" />
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="global" plural-name="globals"
  package="org.opends.server.admin.std"
@@ -266,8 +266,8 @@
  </adm:property>
  <adm:property name="size-limit">
    <adm:synopsis>
      Specifies the maximum number of entries that the Directory Server
      should return to the client durin a search operation.
      Specifies the maximum number of entries that can be returned
      to the client during a single search operation.
    </adm:synopsis>
    <adm:description>
      A value of 0 indicates that no size limit is enforced. Note
@@ -291,8 +291,8 @@
  </adm:property>
  <adm:property name="time-limit">
    <adm:synopsis>
      Specifies the maximum length of time that the Directory Server
      should spend processing a search operation.
      Specifies the maximum length of time that should be spent processing
      a single search operation.
    </adm:synopsis>
    <adm:description>
      A value of 0 seconds indicates that no time limit is
opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml
@@ -25,84 +25,57 @@
  !
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="network-group" plural-name="network-groups"
<adm:managed-object name="network-group"
  plural-name="network-groups"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name />
    is used to classify incoming connections and route requests to
    <adm:user-friendly-name/>
    is used to classify incoming client connections and route requests to
    workflows.
  </adm:synopsis>
  <adm:tag name="core-server" />
  <adm:tag name="core-server"/>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-network-group</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:relation name="network-group-criteria"
    managed-object-name="network-group-criteria">
  <adm:relation name="network-group-qos-policy"
                managed-object-name="qos-policy">
    <adm:synopsis>
      Specifies the set of criteria associated to this network group.
      Specifies the set of quality of service (QoS) policies enforced by
      the
      <adm:user-friendly-name/>
      .
    </adm:synopsis>
    <adm:description>
      A client connection can belong to a <adm:user-friendly-name /> only
      if it matches all the criteria defined for this
      <adm:user-friendly-name />.
      All client connections belonging to the
      <adm:user-friendly-name/>
      will comply with its policies.
    </adm:description>
    <adm:one-to-zero-or-one />
    <adm:one-to-many unique="true"
      plural-name="network-group-qos-policies"/>
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=Criteria</ldap:rdn-sequence>
      <ldap:rdn-sequence>cn=QoS Policies</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="network-group-resource-limits"
    managed-object-name="network-group-resource-limits">
    <adm:synopsis>
      Specifies the set of resource limits enforced by this
      <adm:user-friendly-name />.
    </adm:synopsis>
    <adm:description>
      All client connections belonging to a <adm:user-friendly-name />
      must comply with the resource limits policy.
    </adm:description>
    <adm:one-to-zero-or-one />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=ResourceLimits</ldap:rdn-sequence>
    </adm:profile>
  </adm:relation>
  <adm:relation name="network-group-request-filtering-policy"
    managed-object-name="network-group-request-filtering-policy">
    <adm:synopsis>
      Specifies the request filtering policy enforced by this
      <adm:user-friendly-name />.
    </adm:synopsis>
    <adm:description>
      All client connections belonging to a <adm:user-friendly-name />
      must comply with the request filtering policy.
    </adm:description>
    <adm:one-to-zero-or-one />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=RequestFilteringPolicy</ldap:rdn-sequence>
     </adm:profile>
   </adm:relation>
  <adm:property name="enabled" mandatory="true">
    <adm:synopsis>
      Indicates whether the
      <adm:user-friendly-name />
      <adm:user-friendly-name/>
      is enabled for use in the server.
    </adm:synopsis>
    <adm:description>
      If a network group is not enabled, its workflows will not be
      accessible when processing operations.
      If a
      <adm:user-friendly-name/>
      is not enabled then its workflows will not be accessible when
      processing operations.
    </adm:description>
    <adm:syntax>
      <adm:boolean />
      <adm:boolean/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
@@ -110,36 +83,20 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="network-group-id" mandatory="true"
    read-only="true">
    <adm:synopsis>
      Specifies the name that is used to identify the associated
      <adm:user-friendly-name />
      .
    </adm:synopsis>
    <adm:description>
      The name must be unique among all the
      <adm:user-friendly-plural-name />
      in the server.
    </adm:description>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-network-group-id</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="priority" mandatory="true">
    <adm:synopsis>
      Specifies the order in which the network groups are evaluated.
      Specifies the priority for this <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      A client connection is first compared against network group with
      priority 1. If the client connection does not match the network group
      criteria, the client connection is compared against network group
      with priority 2 etc...
      A client connection is first compared against the
      <adm:user-friendly-name/>
      with the lowest priority. If the client connection does not match
      its connection criteria, then the client connection is compared against
      the
      <adm:user-friendly-name/>
      with next lowest priority, and so on. If no
      <adm:user-friendly-name/>
      is selected then the client connection is rejected.
    </adm:description>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
@@ -152,19 +109,24 @@
  </adm:property>
  <adm:property name="workflow" multi-valued="true">
    <adm:synopsis>
      Identifies the workflows in the network group.
      Specifies a set of workflows which should be accessible from this
      <adm:user-friendly-name/>
      .
    </adm:synopsis>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>No workflows will be accessible.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:aggregation relation-name="workflow" parent-path="/">
      <adm:aggregation relation-name="workflow"
        parent-path="/">
        <adm:constraint>
          <adm:synopsis>
            The referenced workflows must be enabled.
          </adm:synopsis>
          <adm:target-is-enabled-condition>
            <adm:contains property="enabled" value="true" />
            <adm:contains property="enabled" value="true"/>
          </adm:target-is-enabled-condition>
        </adm:constraint>
      </adm:aggregation>
@@ -175,93 +137,162 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="affinity-policy" mandatory="false" advanced="true">
  <adm:property name="allowed-auth-method" multi-valued="true">
    <adm:synopsis>
      Defines the client connection affinity policy.
      Specifies a set of allowed authorization methods that clients
      must use in order to establish connections to this
      <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      A client connection affinity allows some requests to be routed
      to a specific data source regardless the regular routing
      process. For example, we can requires all the requests to be
      routed to a data source after a write has been complete on
      that data source. That way, a read request would return data
      that are consistent with a previous write request. By default,
      the client connection affinity is disabled.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>none</adm:value>
      </adm:defined>
      <adm:alias>
        <adm:synopsis>
          All authorization methods are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:enumeration>
        <adm:value name="none">
        <adm:value name="anonymous">
          <adm:synopsis>
            Disables the client connection affinity.
            Unauthorized clients.
          </adm:synopsis>
        </adm:value>
        <adm:value name="first-read-request-after-write-request">
        <adm:value name="simple">
          <adm:synopsis>
            Routes the first read request to the data source to which
            a previous write request has been routed to. This affinity
            is useful when a client application performs a read request
            after a write request and the read request should return
            consistent data.
            Clients who bind using simple authentication (name and password).
          </adm:synopsis>
        </adm:value>
        <adm:value name="all-requests-after-first-write-request">
        <adm:value name="sasl">
          <adm:synopsis>
            Routes all the requests to the data source to which a
            previous write request has been routed to.
          </adm:synopsis>
        </adm:value>
        <adm:value name="all-write-requests-after-first-write-request">
          <adm:synopsis>
            Routes all the write requests to the data source to which
            a previous write request has been routed to. This affinity
            policy is useful for batch update where a parent entry and
            its subordinates must be sent to the same data source.
          </adm:synopsis>
        </adm:value>
        <adm:value name="all-requests-after-first-request">
          <adm:synopsis>
            Routes all the requests to the data source to which a
            previous request has been routed to. This affinity policy
            allows to create a kind of tunnel between a client application
            and a data source.
            Clients who bind using SASL/external certificate based
            authentication.
          </adm:synopsis>
        </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-affinity-policy</ldap:name>
        <ldap:name>ds-cfg-allowed-auth-method</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="affinity-timeout" mandatory="false" advanced="true">
  <adm:property name="allowed-protocol" multi-valued="true">
    <adm:synopsis>
      The period of time by which an affinity route remains active.
      The timeout value is a number of seconds and when the value is
      set to 0s (default value) then the route remains active forever.
      Specifies a set of allowed supported protocols that clients
      must use in order to establish connections to this
      <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      When the client connection affinity is enabled, an affinity route
      might be elected in accordance with the affinity policy. The affinity
      route is then used until the timeout value expires unless the timeout
      value is 0s in which case the route remains active forever.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0s</adm:value>
      </adm:defined>
      <adm:alias>
        <adm:synopsis>
          All supported protocols are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="s" lower-limit="0" />
      <adm:enumeration>
        <adm:value name="ldap">
          <adm:synopsis>
            Clients using LDAP are allowed.
          </adm:synopsis>
        </adm:value>
        <adm:value name="ldaps">
          <adm:synopsis>
            Clients using LDAPS are allowed.
          </adm:synopsis>
        </adm:value>
      </adm:enumeration>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-affinity-timeout</ldap:name>
        <ldap:name>ds-cfg-allowed-protocol</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-bind-dn" multi-valued="true">
    <adm:synopsis>
      Specifies a set of bind DN patterns that determine the
      clients that are allowed to establish connections to this
      <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      Valid bind DN filters are strings composed of zero or more
      wildcards. A double wildcard ** replaces one or more RDN
      components (as in uid=dmiller,**,dc=example,dc=com). A simple
      wildcard * replaces either a whole RDN, or a whole type, or a
      value substring (as in uid=bj*,ou=people,dc=example,dc=com).
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          All bind DNs are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-allowed-bind-dn</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property-reference name="allowed-client" />
  <adm:property-reference name="denied-client" />
  <adm:property name="is-security-mandatory">
    <adm:synopsis>
      Specifies whether or not a secured client connection
      is required in order for clients to establish connections
      to this <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>false</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:boolean />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-is-security-mandatory</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupCriteriaConfiguration.xml
File was deleted
opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupPluginConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="network-group-plugin"
  plural-name="network-group-plugins" package="org.opends.server.admin.std"
@@ -55,7 +55,9 @@
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>org.opends.server.plugins.NetworkGroupPlugin</adm:value>
        <adm:value>
          org.opends.server.core.networkgroups.NetworkGroupPlugin
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupResourceLimitsConfiguration.xml
File was deleted
opends/src/admin/defn/org/opends/server/admin/std/Package.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:package name="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
@@ -405,4 +405,82 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-client" multi-valued="true">
    <adm:synopsis>
      Specifies a set of host names or address masks that determine the
      clients that are allowed to establish connections to this
      <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      Valid values include a host name, a fully qualified domain name, a
      domain name, an IP address, or a subnetwork with subnetwork mask.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          All clients with addresses that do not match an address on the
          deny list are allowed. If there is no deny list, then all
          clients are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:ip-address-mask />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-allowed-client</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="denied-client" multi-valued="true">
    <adm:synopsis>
      Specifies a set of host names or address masks that determine
      the clients that are not allowed to establish connections to this
      <adm:user-friendly-name/>.
    </adm:synopsis>
    <adm:description>
      Valid values include a host name, a fully qualified domain name, a
      domain name, an IP address, or a subnetwork with subnetwork mask.
      If both allowed and denied client masks are defined and a client
      connection matches one or more masks in both lists, then the
      connection is denied. If only a denied list is specified,
      then any client not matching a mask in that list is allowed.
    </adm:description>
    <adm:requires-admin-action>
      <adm:none>
        <adm:synopsis>
          Changes to this property take effect immediately and do not
          interfere with connections that may have already been
          established.
        </adm:synopsis>
      </adm:none>
    </adm:requires-admin-action>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>
          If an allow list is specified, then only clients with
          addresses on the allow list are allowed. Otherwise, all
          clients are allowed.
        </adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:ip-address-mask />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-denied-client</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:package>
opends/src/admin/defn/org/opends/server/admin/std/QOSPolicyConfiguration.xml
New file
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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
  !
  !
  !      Copyright 2009 Sun Microsystems, Inc.
  !
-->
<adm:managed-object name="qos-policy"
  plural-name="qos-policies"
  abstract="true"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    <adm:user-friendly-plural-name/>
    determine the quality of service (QoS) clients receive when
    interacting with the server.
  </adm:synopsis>
  <adm:tag name="core-server"/>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-qos-policy</ldap:name>
      <ldap:superior>top</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="java-class" mandatory="true">
    <adm:synopsis>
      Specifies the fully-qualified name of the Java class that provides the
      <adm:user-friendly-name />
      implementation.
    </adm:synopsis>
  <adm:requires-admin-action>
    <adm:component-restart />
  </adm:requires-admin-action>
    <adm:syntax>
      <adm:java-class>
        <adm:instance-of>
          org.opends.server.api.QOSPolicyFactory
        </adm:instance-of>
      </adm:java-class>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-java-class</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RequestFilteringQOSPolicyConfiguration.xml
File was renamed from opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupRequestFilteringPolicyConfiguration.xml
@@ -23,40 +23,42 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2008 Sun Microsystems, Inc.
  !      Copyright 2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="network-group-request-filtering-policy"
  plural-name="network-group-request-filtering-policies"
<adm:managed-object name="request-filtering-qos-policy"
  plural-name="request-filtering-qos-policies"
  extends="qos-policy"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name /> is used to define the type of requests
    allowed in the network group.
    <adm:user-friendly-name/>
    is used to define the type of requests allowed by the server.
  </adm:synopsis>
  <adm:tag name="core-server" />
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-network-group-request-filtering-policy</ldap:name>
      <ldap:superior>top</ldap:superior>
      <ldap:name>ds-cfg-request-filtering-qos-policy</ldap:name>
      <ldap:superior>ds-cfg-qos-policy</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="allowed-operations" mandatory="false" multi-valued="true">
    <adm:synopsis>
      Specifies which operations are allowed in the network group.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, only the listed operations are
      allowed in the network group. If the attribute is not defined, all
      the operations are allowed.
    </adm:description>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:undefined />
      <adm:defined>
        <adm:value>
          org.opends.server.core.networkgroups.RequestFilteringPolicyFactory
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="allowed-operations" multi-valued="true">
    <adm:synopsis>
      Specifies which operations are allowed by the server.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:alias>
        <adm:synopsis>All operations are allowed.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:enumeration>
@@ -76,7 +78,8 @@
          <adm:synopsis>Extended operations</adm:synopsis>
        </adm:value>
        <adm:value name="inequality-search">
          <adm:synopsis>Inequality Search operations</adm:synopsis>
          <adm:synopsis>Inequality Search operations
          </adm:synopsis>
        </adm:value>
        <adm:value name="modify">
          <adm:synopsis>Modify operations</adm:synopsis>
@@ -95,21 +98,18 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-attributes" mandatory="false" multi-valued="true">
  <adm:property name="allowed-attributes" multi-valued="true">
    <adm:synopsis>
      Specifies which attributes are allowed in search and compare operations.
      Specifies which attributes are allowed in search and
      compare operations.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, only the listed attributes are allowed
      in search and compare operations. If it is not set, all the attributes
      are allowed, except those listed in ds-cfg-prohibited-attributes.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>All non-prohibited attributes.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
      <adm:string/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
@@ -117,24 +117,19 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="prohibited-attributes" mandatory="false"
  multi-valued="true">
    multi-valued="true">
    <adm:synopsis>
      Specifies which attributes are not allowed in search and compare
      operations.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, all the listed attributes are prohibited
      in search and compare operations. It should not be used in conjunction
      with ds-cfg-allowed-attributes.
    </adm:description>
      Specifies which attributes are not allowed in search
      and compare operations.
    </adm:synopsis>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>All allowed attributes.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:string />
      <adm:string/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
@@ -142,19 +137,14 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-search-scopes" mandatory="false"
  multi-valued="true">
  <adm:property name="allowed-search-scopes" multi-valued="true">
    <adm:synopsis>
      Specifies which search scopes are allowed in the network group.
      Specifies which search scopes are allowed by the server.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, only the listed scopes are
      allowed in the network group. If it is not set, all search scopes are
      allowed.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>All search scopes are allowed.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:enumeration>
@@ -178,22 +168,17 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="allowed-subtrees" mandatory="false"
  multi-valued="true">
  <adm:property name="allowed-subtrees" multi-valued="true">
    <adm:synopsis>
      Specifies which subtrees are exposed to clients.
      Specifies which subtrees are accessible to clients.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, only the listed subtrees are exposed. If
      it is not set, all the substrees are exposed. Note that
      ds-cfg-prohibited-subtrees restricts the list of exposed subtrees.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>All non-prohibited subtrees.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
      <adm:dn/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
@@ -201,22 +186,18 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="prohibited-subtrees" mandatory="false"
  multi-valued="true">
  <adm:property name="prohibited-subtrees" multi-valued="true">
    <adm:synopsis>
      Specifies which subtrees are not exposed to clients. Each prohibited
      subtree must be subordinate to an allowed subtree.
      Specifies which subtrees must be hidden from clients. Each
      prohibited subtree must be subordinate to an allowed subtree.
    </adm:synopsis>
    <adm:description>
      When this attribute is specified, all the listed subtrees cannot be
      accessed.
    </adm:description>
    <adm:default-behavior>
      <adm:undefined />
      <adm:alias>
        <adm:synopsis>All allowed subtrees.</adm:synopsis>
      </adm:alias>
    </adm:default-behavior>
    <adm:syntax>
      <adm:dn />
      <adm:dn/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
@@ -224,5 +205,4 @@
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/ResourceLimitsQOSPolicyConfiguration.xml
New file
@@ -0,0 +1,225 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
  ! 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
  !
  !
  !      Copyright 2009 Sun Microsystems, Inc.
  ! -->
<adm:managed-object name="resource-limits-qos-policy"
  plural-name="resource-limits-qos-policies"
  extends="qos-policy"
  package="org.opends.server.admin.std"
  xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name/>
    are used to define resource limits enforced by the server.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:name>ds-cfg-resource-limits-qos-policy</ldap:name>
      <ldap:superior>ds-cfg-qos-policy</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property-override name="java-class" advanced="true">
    <adm:default-behavior>
      <adm:defined>
        <adm:value>
          org.opends.server.core.networkgroups.ResourceLimitsPolicyFactory
        </adm:value>
      </adm:defined>
    </adm:default-behavior>
  </adm:property-override>
  <adm:property name="max-connections">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum number of concurrent client connections
      to the server.
    </adm:synopsis>
    <adm:description>
      A value of 0 means that no limit is enforced.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-max-connections</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-connections-from-same-ip">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum number of client connections from the
      same source address.
    </adm:synopsis>
    <adm:description>
      A value of 0 means that no limit is enforced.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-max-connections-from-same-ip</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-ops-per-connection">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum number of operations per
      client connection.
    </adm:synopsis>
    <adm:description>
      A value of 0 means that no limit is enforced.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-max-ops-per-connection</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="max-concurrent-ops-per-connection">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum number of concurrent operations
      per client connection.
    </adm:synopsis>
    <adm:description>
      A value of 0 means that no limit is enforced.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-max-concurrent-ops-per-connection</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="size-limit">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum number of entries that can be returned
      to the client during a single search operation.
    </adm:synopsis>
    <adm:description>
      A value of 0 indicates that no size limit is enforced. Note
      that this is the default for the server, but it may be
      overridden on a per-user basis using the ds-rlim-size-limit
      operational attribute.
    </adm:description>
    <adm:default-behavior>
      <adm:inherited>
        <adm:absolute property-name="size-limit"
          path="/relation=global-configuration"/>
      </adm:inherited>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-size-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="time-limit">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the maximum length of time that should be spent processing
      a search operation.
    </adm:synopsis>
    <adm:description>
      A value of 0 seconds indicates that no time limit is
      enforced. Note that this is the default for the server,
      but it may be overridden on a per-user basis using the
      ds-rlim-time-limit operational attribute.
    </adm:description>
    <adm:default-behavior>
      <adm:inherited>
        <adm:absolute property-name="time-limit"
          path="/relation=global-configuration"/>
      </adm:inherited>
    </adm:default-behavior>
    <adm:syntax>
      <adm:duration base-unit="s" lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-time-limit</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
  <adm:property name="min-substring-length">
    <adm:TODO>Make use of unlimited.</adm:TODO>
    <adm:synopsis>
      Specifies the minimum length for a search filter substring.
    </adm:synopsis>
    <adm:description>
      Search operations with short search filter substring are
      likely to match a high number of entries and might degrade
      performance overall. A value of 0 indicates that no limit is
      enforced.
    </adm:description>
    <adm:default-behavior>
      <adm:defined>
        <adm:value>0</adm:value>
      </adm:defined>
    </adm:default-behavior>
    <adm:syntax>
      <adm:integer lower-limit="0"/>
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:name>ds-cfg-min-substring-length</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
@@ -23,7 +23,7 @@
  ! CDDL HEADER END
  !
  !
  !      Copyright 2007-2008 Sun Microsystems, Inc.
  !      Copyright 2007-2009 Sun Microsystems, Inc.
  ! -->
<adm:root-managed-object xmlns:adm="http://www.opends.org/admin"
  xmlns:ldap="http://www.opends.org/admin-ldap"
@@ -416,13 +416,15 @@
    </adm:profile>
  </adm:relation>
  <adm:relation name="network-group">
    <adm:one-to-many naming-property="network-group-id" />
    <adm:one-to-many />
    <adm:profile name="ldap">
      <ldap:rdn-sequence>cn=Network Groups,cn=config</ldap:rdn-sequence>
    </adm:profile>
    <adm:profile name="cli">
      <cli:relation>
        <cli:default-property name="enabled" />
        <cli:default-property name="priority" />
        <cli:default-property name="workflow" />
      </cli:relation>
    </adm:profile>
  </adm:relation>
opends/src/messages/messages/config.properties
@@ -20,7 +20,7 @@
#
# CDDL HEADER END
#
#      Copyright 2006-2008 Sun Microsystems, Inc.
#      Copyright 2006-2009 Sun Microsystems, Inc.
@@ -2150,3 +2150,8 @@
SEVERE_ERR_CONFIG_NETWORKGROUPREQUESTFILTERINGPOLICY_INVALID_SUBTREE_720=The \
 allowed subtree %s specified in configuration entry %s is also defined as \
 a prohibited subtree
SEVERE_ERR_CONFIG_NETWORK_GROUP_CONFIG_NOT_ACCEPTABLE_721=The configuration \
 for the network group defined in configuration entry %s was not acceptable:  %s
SEVERE_ERR_CONFIG_NETWORK_GROUP_POLICY_CANNOT_INITIALIZE_722=An error occurred \
 while trying to initialize a network group policy loaded from class %s with \
 the information in configuration entry %s:  %s
opends/src/messages/messages/dsconfig.properties
@@ -470,4 +470,8 @@
 server at %s on port %s. Check this port is an administration port
SEVERE_ERR_DSCFG_ERROR_LDAP_FAILED_TO_CONNECT_NOT_TRUSTED_158=Unable to connect to the \
 server at %s on port %s. In non-interactive mode, you must use the '--trustAll' option
SEVERE_ERR_DSCFG_ERROR_VALUE_DOES_NOT_EXIST_159=The value %s for the %s property does not exist
SEVERE_ERR_DSCFG_ERROR_VALUE_DOES_NOT_EXIST_159=The value %s for the %s property does not exist
SEVERE_ERR_DSCFG_ERROR_NO_AVAILABLE_TYPES_160=Unable to continue since there are \
 no available types of %s to choose from
INFO_DSCFG_TYPE_PROMPT_SINGLE_161=>>>> There is only one type of %s available: "%s". \
 Are you sure that this is the correct one?
opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -869,7 +869,7 @@
  public final boolean isTop() {
    // Casting to Object and instanceof check are required
    // to workaround a bug in JDK versions prior to 1.5.0_08.
    return ((Object) this instanceof TopCfgDefn);
    return (this instanceof TopCfgDefn);
  }
opends/src/server/org/opends/server/admin/AdminException.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -41,6 +41,13 @@
public abstract class AdminException extends OpenDsException {
  /**
   * Fake serialization ID.
   */
  private static final long serialVersionUID = 1L;
  /**
   * Create an admin exception with a message and cause.
   *
   * @param message
opends/src/server/org/opends/server/admin/AdminRuntimeException.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -39,6 +39,13 @@
 */
public abstract class AdminRuntimeException extends RuntimeException {
  /**
   * Fake serialization ID.
   */
  private static final long serialVersionUID = 1L;
  // Message that explains the problem.
  private final Message message;
opends/src/server/org/opends/server/admin/DecodingException.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -39,6 +39,13 @@
public abstract class DecodingException extends OperationsException {
  /**
   * Fake serialization ID.
   */
  private static final long serialVersionUID = 1L;
  /**
   * Create a decoding exception with a message.
   *
   * @param message
opends/src/server/org/opends/server/admin/InstantiableRelationDefinition.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -284,13 +284,10 @@
  public void toString(StringBuilder builder) {
    builder.append("name=");
    builder.append(getName());
    builder.append(" type=composition parent=");
    builder.append(" type=collection parent=");
    builder.append(getParentDefinition().getName());
    builder.append(" child=");
    builder.append(getChildDefinition().getName());
    builder.append(" child=");
    builder.append(getChildDefinition().getName());
    builder.append(" minOccurs=0");
  }
opends/src/server/org/opends/server/admin/LDAPProfile.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -104,7 +104,7 @@
     *         the instantiable relation is not handled by this LDAP
     *         profile wrapper.
     */
    public String getInstantiableRelationChildRDNType(
    public String getRelationChildRDNType(
        InstantiableRelationDefinition<?, ?> r) {
      return null;
    }
@@ -112,6 +112,26 @@
    /**
     * Gets the LDAP RDN attribute type for child entries of an set
     * relation.
     * <p>
     * The default implementation of this method is to return
     * <code>null</code>.
     *
     * @param r
     *          The set relation.
     * @return Returns the LDAP RDN attribute type for child entries of
     *         an set relation, or <code>null</code> if the set relation
     *         is not handled by this LDAP profile wrapper.
     */
    public String getRelationChildRDNType(SetRelationDefinition<?, ?> r)
    {
      return null;
    }
    /**
     * Get the principle object class associated with the specified
     * definition.
     * <p>
@@ -217,7 +237,7 @@
   *           If the LDAP profile properties file associated with the
   *           provided managed object definition could not be loaded.
   */
  public String getInstantiableRelationChildRDNType(
  public String getRelationChildRDNType(
      InstantiableRelationDefinition<?, ?> r) throws MissingResourceException {
    if (r.getNamingPropertyDefinition() != null) {
      // Use the attribute associated with the naming property.
@@ -225,7 +245,7 @@
          .getNamingPropertyDefinition());
    } else {
      for (Wrapper profile : profiles) {
        String rdnType = profile.getInstantiableRelationChildRDNType(r);
        String rdnType = profile.getRelationChildRDNType(r);
        if (rdnType != null) {
          return rdnType;
        }
@@ -238,23 +258,52 @@
  /**
   * Gets the LDAP object classes associated with an instantiable
   * Gets the LDAP object classes associated with an instantiable or set
   * relation branch. The branch is the parent entry of child managed
   * objects.
   *
   * @param r
   *          The instantiable relation.
   *          The instantiable or set relation.
   * @return Returns the LDAP object classes associated with an
   *         instantiable relation branch.
   *         instantiable or set relation branch.
   */
  public List<String> getInstantiableRelationObjectClasses(
      InstantiableRelationDefinition<?, ?> r) {
  public List<String> getRelationObjectClasses(
      RelationDefinition<?, ?> r) {
    return Arrays.asList(new String[] { "top", "ds-cfg-branch" });
  }
  /**
   * Gets the LDAP RDN attribute type for child entries of an set
   * relation.
   *
   * @param r
   *          The set relation.
   * @return Returns the LDAP RDN attribute type for child entries of an
   *         set relation.
   * @throws MissingResourceException
   *           If the LDAP profile properties file associated with the
   *           provided managed object definition could not be loaded.
   */
  public String getRelationChildRDNType(SetRelationDefinition<?, ?> r)
      throws MissingResourceException
  {
    for (Wrapper profile : profiles)
    {
      String rdnType = profile.getRelationChildRDNType(r);
      if (rdnType != null)
      {
        return rdnType;
      }
    }
    return resource.getString(r.getParentDefinition(),
        "naming-attribute." + r.getName());
  }
  /**
   * Get the principle object class associated with the specified
   * definition.
   *
opends/src/server/org/opends/server/admin/ManagedObjectPath.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -84,7 +84,14 @@
 * <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.
 * instance
 * <li>an element representing a managed object associated with a
 * one-to-many (set) 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.
 * </ul>
 * The following path string representation identifies a connection
 * handler instance (note that the <code>type</code> is not
@@ -149,10 +156,10 @@
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
      appendManagedObjectPathElement(r);
      // Now add the single RDN representing the named instance.
      String type = profile.getInstantiableRelationChildRDNType(r);
      String type = profile.getRelationChildRDNType(r);
      AttributeType atype = DirectoryServer.getAttributeType(
          type.toLowerCase(), true);
      AttributeValue avalue = new AttributeValue(atype, name);
@@ -166,10 +173,30 @@
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement(r);
      // Now add the single RDN representing the instance.
      String type = profile.getRelationChildRDNType(r);
      AttributeType atype = DirectoryServer.getAttributeType(
          type.toLowerCase(), true);
      AttributeValue avalue = new AttributeValue(atype, d.getName());
      dn = dn.concat(RDN.create(atype, avalue));
    }
    /**
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
      appendManagedObjectPathElement(r);
    }
@@ -182,7 +209,7 @@
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
      appendManagedObjectPathElement(r);
    }
@@ -400,6 +427,60 @@
  /**
   * A path element representing an set managed object.
   */
  private static final class SetElement
      <C extends ConfigurationClient, S extends Configuration>
      extends Element<C, S> {
    // Factory method.
    private static final <C extends ConfigurationClient,
        S extends Configuration>
        SetElement<C, S> create(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new SetElement<C, S>(r, d);
    }
    // The set relation.
    private final SetRelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private SetElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      super(d);
      this.r = r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SetRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer.appendManagedObjectPathElement(r,
          getManagedObjectDefinition());
    }
  }
  /**
   * A path element representing a singleton managed object.
   */
  private static final class SingletonElement
@@ -511,6 +592,18 @@
    /**
     * {@inheritDoc}
     */
    public <M extends ConfigurationClient, N extends Configuration>
        void appendManagedObjectPathElement(
        SetRelationDefinition<? super M, ? super N> r,
        AbstractManagedObjectDefinition<M, N> d) {
      serializeElement(r, d);
    }
    // Common element serialization.
    private <M, N> void serializeElement(RelationDefinition<?, ?> r,
        AbstractManagedObjectDefinition<?, ?> d) {
@@ -702,6 +795,16 @@
      }
      return InstantiableElement.create(ir, d, name);
    } else if (r instanceof SetRelationDefinition) {
      SetRelationDefinition<C, S> ir = (SetRelationDefinition<C, S>) r;
      if (name != null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path
            + "\": instance name specified for set relation");
      }
      return SetElement.create(ir, d);
    } else if (r instanceof OptionalRelationDefinition) {
      OptionalRelationDefinition<C, S> or =
        (OptionalRelationDefinition<C, S>) r;
@@ -782,6 +885,10 @@
        return parent().child(ir, nd,
            elements.get(elements.size() - 1).getName());
      }
    } else if (r instanceof SetRelationDefinition) {
      SetRelationDefinition<? super C, ? super S> sr =
        (SetRelationDefinition<? super C, ? super S>) r;
      return parent().child(sr, nd);
    } else if (r instanceof OptionalRelationDefinition) {
      OptionalRelationDefinition<? super C, ? super S> or =
        (OptionalRelationDefinition<? super C, ? super S>) r;
@@ -967,6 +1074,99 @@
  /**
   * Creates a new child managed object path beneath the provided
   * parent path having the specified managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The set relation referencing the child.
   * @param d
   *          The managed object definition associated with the child
   *          (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SetRelationDefinition<? super M, ? super N> r,
      AbstractManagedObjectDefinition<M, N> d)
      throws IllegalArgumentException {
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new SetElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
  /**
   * Creates a new child managed object path beneath the provided parent
   * path having the managed object definition indicated by
   * <code>name</code>.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          path references.
   * @param r
   *          The set relation referencing the child.
   * @param name
   *          The name of the managed object definition associated with
   *          the child (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank or specifies a
   *           managed object definition which is not a sub-type of the
   *           relation's child definition.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<? extends M, ? extends N> child(
      SetRelationDefinition<M, N> r,
      String name)
      throws IllegalArgumentException {
    AbstractManagedObjectDefinition<M, N> d = r.getChildDefinition();
    return child(r, d.getChild(name));
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path using the relation's child managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The set relation referencing the child.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SetRelationDefinition<M, N> r)
      throws IllegalArgumentException {
    return child(r, r.getChildDefinition());
  }
  /**
   * {@inheritDoc}
   */
  @Override
opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -110,4 +110,26 @@
      SingletonRelationDefinition<? super C, ? super S> r,
      AbstractManagedObjectDefinition<C, S> d);
  /**
   * Append a managed object path element identified by a
   * set relation.
   *
   * @param <C>
   *          The type of client managed object configuration that
   *          this path element references.
   * @param <S>
   *          The type of server managed object configuration that
   *          this path element references.
   * @param r
   *          The set relation.
   * @param d
   *          The managed object definition.
   */
  <C extends ConfigurationClient, S extends Configuration>
      void appendManagedObjectPathElement(
      SetRelationDefinition<? super C, ? super S> r,
      AbstractManagedObjectDefinition<C, S> d);
}
opends/src/server/org/opends/server/admin/OptionalRelationDefinition.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -162,11 +162,10 @@
  public void toString(StringBuilder builder) {
    builder.append("name=");
    builder.append(getName());
    builder.append(" type=composition parent=");
    builder.append(" type=optional parent=");
    builder.append(getParentDefinition().getName());
    builder.append(" child=");
    builder.append(getChildDefinition().getName());
    builder.append(" minOccurs=0 maxOccurs=1");
  }
opends/src/server/org/opends/server/admin/RelationDefinition.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -68,7 +68,11 @@
 * <li>instantiable relations (one to many): the relation is
 * represented using a child entry directly beneath the parent.
 * Referenced managed objects are represented using child entries of
 * this "relation entry".
 * this "relation entry" and are named by the user
 * <li>set relations (one to many): the relation is
 * represented using a child entry directly beneath the parent.
 * Referenced managed objects are represented using child entries of
 * this "relation entry" whose name is the type of the managed object.
 * </ul>
 * Whereas, aggregations are represented by storing the DNs of the
 * referenced managed objects in an attribute of the aggregating
opends/src/server/org/opends/server/admin/RelationDefinitionVisitor.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -70,6 +70,26 @@
  /**
   * Visit a set relation definition.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param rd
   *          The set relation definition to visit.
   * @param p
   *          A visitor specified parameter.
   * @return Returns a visitor specified result.
   */
  <C extends ConfigurationClient, S extends Configuration> R visitSet(
      SetRelationDefinition<C, S> rd, P p);
  /**
   * Visit an optional relation definition.
   *
   * @param <C>
opends/src/server/org/opends/server/admin/SetRelationDefinition.java
New file
@@ -0,0 +1,289 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.opends.messages.Message;
/**
 * A managed object composite relationship definition which represents a
 * composition of zero or more managed objects each of which must have a
 * different type. The manage objects are named using their type name.
 *
 * @param <C>
 *          The type of client managed object configuration that this
 *          relation definition refers to.
 * @param <S>
 *          The type of server managed object configuration that this
 *          relation definition refers to.
 */
public final class SetRelationDefinition
    <C extends ConfigurationClient, S extends Configuration>
    extends RelationDefinition<C, S>
{
  /**
   * An interface for incrementally constructing set relation
   * definitions.
   *
   * @param <C>
   *          The type of client managed object configuration that this
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that this
   *          relation definition refers to.
   */
  public static final class Builder
      <C extends ConfigurationClient, S extends Configuration>
      extends AbstractBuilder<C, S, SetRelationDefinition<C, S>>
  {
    // The plural name of the relation.
    private final String pluralName;
    // The optional default managed objects associated with this
    // set relation definition.
    private final Map<String,
                      DefaultManagedObject<? extends C, ? extends S>>
      defaultManagedObjects =
        new HashMap<String, DefaultManagedObject<? extends C, ? extends S>>();
    /**
     * Creates a new builder which can be used to incrementally build a
     * set relation definition.
     *
     * @param pd
     *          The parent managed object definition.
     * @param name
     *          The name of the relation.
     * @param pluralName
     *          The plural name of the relation.
     * @param cd
     *          The child managed object definition.
     */
    public Builder(AbstractManagedObjectDefinition<?, ?> pd,
        String name, String pluralName,
        AbstractManagedObjectDefinition<C, S> cd)
    {
      super(pd, name, cd);
      this.pluralName = pluralName;
    }
    /**
     * Adds the default managed object to this set relation definition.
     *
     * @param defaultManagedObject
     *          The default managed object.
     */
    public void setDefaultManagedObject(
        DefaultManagedObject<? extends C, ? extends S> defaultManagedObject)
    {
      this.defaultManagedObjects
          .put(defaultManagedObject.getManagedObjectDefinition()
              .getName(), defaultManagedObject);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected SetRelationDefinition<C, S> buildInstance(
        Common<C, S> common)
    {
      return new SetRelationDefinition<C, S>(common, pluralName,
          defaultManagedObjects);
    }
  }
  // The plural name of the relation.
  private final String pluralName;
  // The optional default managed objects associated with this
  // set relation definition.
  private final Map<String,
                    DefaultManagedObject<? extends C, ? extends S>>
    defaultManagedObjects;
  // Private constructor.
  private SetRelationDefinition(
      Common<C, S> common,
      String pluralName,
      Map<String,
          DefaultManagedObject<? extends C, ? extends S>> defaultManagedObjects)
  {
    super(common);
    this.pluralName = pluralName;
    this.defaultManagedObjects = defaultManagedObjects;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R, P> R accept(RelationDefinitionVisitor<R, P> v, P p)
  {
    return v.visitSet(this, p);
  }
  /**
   * Gets the named default managed object associated with this set
   * relation definition.
   *
   * @param name
   *          The name of the default managed object (for set relations
   *          this is the type of the default managed object).
   * @return The named default managed object.
   * @throws IllegalArgumentException
   *           If there is no default managed object associated with the
   *           provided name.
   */
  public DefaultManagedObject<? extends C, ? extends S> getDefaultManagedObject(
      String name) throws IllegalArgumentException
  {
    if (!defaultManagedObjects.containsKey(name))
    {
      throw new IllegalArgumentException(
          "unrecognized default managed object \"" + name + "\"");
    }
    return defaultManagedObjects.get(name);
  }
  /**
   * Gets the names of the default managed objects associated with this
   * set relation definition.
   *
   * @return An unmodifiable set containing the names of the default
   *         managed object.
   */
  public Set<String> getDefaultManagedObjectNames()
  {
    return Collections.unmodifiableSet(defaultManagedObjects.keySet());
  }
  /**
   * Gets the plural name of the relation.
   *
   * @return The plural name of the relation.
   */
  public String getPluralName()
  {
    return pluralName;
  }
  /**
   * Gets the user friendly plural name of this relation definition in
   * the default locale.
   *
   * @return Returns the user friendly plural name of this relation
   *         definition in the default locale.
   */
  public Message getUserFriendlyPluralName()
  {
    return getUserFriendlyPluralName(Locale.getDefault());
  }
  /**
   * Gets the user friendly plural name of this relation definition in
   * the specified locale.
   *
   * @param locale
   *          The locale.
   * @return Returns the user friendly plural name of this relation
   *         definition in the specified locale.
   */
  public Message getUserFriendlyPluralName(Locale locale)
  {
    String property =
        "relation." + getName() + ".user-friendly-plural-name";
    return ManagedObjectDefinitionI18NResource.getInstance()
        .getMessage(getParentDefinition(), property, locale);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void toString(StringBuilder builder)
  {
    builder.append("name=");
    builder.append(getName());
    builder.append(" type=set parent=");
    builder.append(getParentDefinition().getName());
    builder.append(" child=");
    builder.append(getChildDefinition().getName());
  }
  /**
   * {@inheritDoc}
   */
  @Override
  protected void initialize() throws Exception
  {
    for (DefaultManagedObject<?, ?> dmo : defaultManagedObjects
        .values())
    {
      dmo.initialize();
    }
  }
}
opends/src/server/org/opends/server/admin/SingletonRelationDefinition.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -163,11 +163,10 @@
  public void toString(StringBuilder builder) {
    builder.append("name=");
    builder.append(getName());
    builder.append(" type=composition parent=");
    builder.append(" type=singleton parent=");
    builder.append(getParentDefinition().getName());
    builder.append(" child=");
    builder.append(getChildDefinition().getName());
    builder.append(" minOccurs=1 maxOccurs=1");
  }
opends/src/server/org/opends/server/admin/client/AdminSecurityException.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
@@ -42,6 +42,13 @@
public abstract class AdminSecurityException extends AdminClientException {
  /**
   * Fake serialization ID.
   */
  private static final long serialVersionUID = 1L;
  /**
   * Create a security exception with a message and cause.
   *
   * @param message
opends/src/server/org/opends/server/admin/client/ManagedObject.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
@@ -49,6 +49,7 @@
import org.opends.server.admin.PropertyIsReadOnlyException;
import org.opends.server.admin.PropertyIsSingleValuedException;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
@@ -227,6 +228,45 @@
  /**
   * Creates a new child managed object bound to the specified
   * set 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 <C>
   *          The expected type of the child managed object
   *          configuration client.
   * @param <S>
   *          The expected type of the child managed object
   *          server configuration.
   * @param <CC>
   *          The actual type of the added managed object
   *          configuration client.
   * @param r
   *          The set relation definition.
   * @param d
   *          The definition of the managed object to be created.
   * @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
   *         set relation.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   */
  <C extends ConfigurationClient, S extends Configuration, CC extends C>
  ManagedObject<CC> createChild(SetRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException;
  /**
   * Retrieves an instantiable child managed object.
   *
   * @param <C>
@@ -361,6 +401,52 @@
  /**
   * Retrieves a set child managed object.
   *
   * @param <C>
   *          The requested type of the child managed object
   *          configuration client.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param r
   *          The set relation definition.
   * @param name
   *          The name of the child managed object.
   * @return Returns the set child managed object.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
   * @throws ManagedObjectDecodingException
   *           If the managed object was found but one or more of its
   *           properties could not be decoded.
   * @throws ManagedObjectNotFoundException
   *           If the requested managed object could not be found on
   *           the server.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws AuthorizationException
   *           If the server refuses to retrieve the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(SetRelationDefinition<C, S> r,
      String name) throws IllegalArgumentException, DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException;
  /**
   * Creates a client configuration view of this managed object.
   * Modifications made to this managed object will be reflected in
   * the client configuration view and vice versa.
@@ -582,6 +668,80 @@
  /**
   * Lists the child managed objects associated with the specified set
   * relation.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param r
   *          The set relation definition.
   * @return Returns the names of the child managed objects which for
   *         set relations are the definition names of each managed
   *         object.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects because
   *           the client does not have the correct privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(SetRelationDefinition<C, S> r)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException;
  /**
   * Lists the child managed objects associated with the specified set
   * relation which are a sub-type of the specified managed object
   * definition.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param r
   *          The set relation definition.
   * @param d
   *          The managed object definition.
   * @return Returns the names of the child managed objects which for
   *         set relations are the definition names of each managed
   *         object.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects because
   *           the client does not have the correct privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(SetRelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException;
  /**
   * Removes the named instantiable child managed object.
   *
   * @param <C>
@@ -666,6 +826,49 @@
  /**
   * Removes s set child managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param r
   *          The set relation definition.
   * @param name
   *          The name of the child managed object to be removed.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the managed object could not be removed because it
   *           could not found on the server.
   * @throws OperationRejectedException
   *           If the managed object cannot be removed due to some
   *           client-side or server-side constraint which cannot be
   *           satisfied (for example, if it is referenced by another
   *           managed object).
   * @throws ConcurrentModificationException
   *           If this managed object has been removed from the server
   *           by another client.
   * @throws AuthorizationException
   *           If the server refuses to remove the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  <C extends ConfigurationClient, S extends Configuration>
  void removeChild(SetRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, ConcurrentModificationException,
      AuthorizationException, CommunicationException;
  /**
   * Sets a new pending value for the specified property.
   * <p>
   * See the class description for more information regarding pending
opends/src/server/org/opends/server/admin/client/ManagementContext.java
@@ -22,13 +22,14 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client;
import java.util.Set;
import java.util.SortedSet;
import org.opends.server.admin.AbstractManagedObjectDefinition;
@@ -41,6 +42,7 @@
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.client.spi.Driver;
import org.opends.server.admin.std.client.RootCfgClient;
@@ -155,6 +157,54 @@
  /**
   * Deletes s set child managed object from the
   * named parent managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The set relation definition.
   * @param name
   *          The name of the child managed object to be removed.
   * @return Returns <code>true</code> if the set
   *         child managed object was found, or <code>false</code>
   *         if it was not found.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws OperationRejectedException
   *           If the managed object cannot be removed due to some
   *           client-side or server-side constraint which cannot be
   *           satisfied (for example, if it is referenced by another
   *           managed object).
   * @throws AuthorizationException
   *           If the server refuses to remove the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
      String name) throws IllegalArgumentException,
      ManagedObjectNotFoundException, OperationRejectedException,
      AuthorizationException, CommunicationException {
    return getDriver().deleteManagedObject(parent, rd, name);
  }
  /**
   * Gets the named managed object.
   *
   * @param <C>
@@ -236,7 +286,12 @@
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
      PropertyException {
    return getDriver().getPropertyValue(path, pd);
    Set<PD> values = getPropertyValues(path, pd);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
@@ -343,7 +398,7 @@
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    return getDriver().listManagedObjects(parent, rd);
    return listManagedObjects(parent, rd, rd.getChildDefinition());
  }
@@ -392,6 +447,44 @@
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The set relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    return getDriver().listManagedObjects(parent, rd, rd.getChildDefinition());
  }
  /**
   * Determines whether or not the named managed object exists.
   *
   * @param path
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java
@@ -22,15 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.cli;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.server.tools.ToolConstants.*;
import java.io.OutputStream;
@@ -50,6 +51,8 @@
import org.opends.admin.ads.ADSContextException;
import org.opends.admin.ads.ADSContext.AdministratorProperty;
import org.opends.admin.ads.ADSContextException.ErrorType;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
import org.opends.server.types.Privilege;
import org.opends.server.util.args.Argument;
@@ -59,8 +62,6 @@
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
/**
 * This class is handling user Admin CLI.
 */
@@ -88,11 +89,6 @@
  private DsFrameworkCliParser argParser;
  /**
   * The verbose argument.
   */
  private BooleanArgument verboseArg;
  /**
   * The enumeration containing the different subCommand names.
   */
  private enum SubCommandNameEnum
@@ -140,6 +136,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
      return name;
@@ -238,7 +235,7 @@
  /**
   * The subcommand list.
   */
  private HashSet<SubCommand> subCommands = new HashSet<SubCommand>();
  private final HashSet<SubCommand> subCommands = new HashSet<SubCommand>();
  /**
   * Indicates whether this subCommand should be hidden in the usage
@@ -281,7 +278,6 @@
  public void initializeCliGroup(DsFrameworkCliParser argParser,
      BooleanArgument verboseArg) throws ArgumentException
  {
    this.verboseArg = verboseArg;
    isHidden = false;
    groupName = "admin-user";
    this.argParser = argParser;
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java
@@ -22,17 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.cli;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import org.opends.messages.Message;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.server.tools.ToolConstants.*;
import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
import static org.opends.server.util.StaticUtils.wrapText;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.IOException;
import java.io.OutputStream;
@@ -41,37 +40,23 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Logger;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import org.opends.admin.ads.ADSContextException;
import org.opends.admin.ads.util.ConnectionUtils;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.messages.Message;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.SubCommand;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
/**
 * This class will parse CLI arguments for the dsframework command lines.
 */
public class DsFrameworkCliParser extends SecureConnectionCliParser
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  /**
   * The Logger.
   */
  static private final Logger LOG =
    Logger.getLogger(DsFrameworkCliParser.class.getName());
  /**
   * The different CLI group.
   */
  public HashSet<DsFrameworkCliSubCommandGroup> cliGroup;
opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java
@@ -22,15 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.cli;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import static org.opends.messages.AdminMessages.*;
import static org.opends.messages.DSConfigMessages.*;
import static org.opends.messages.ToolMessages.*;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.server.tools.ToolConstants.*;
import java.io.OutputStream;
@@ -50,6 +51,8 @@
import org.opends.admin.ads.ADSContextException;
import org.opends.admin.ads.ADSContext.ServerProperty;
import org.opends.admin.ads.ADSContextException.ErrorType;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
import org.opends.server.util.args.Argument;
import org.opends.server.util.args.ArgumentException;
@@ -59,8 +62,6 @@
import org.opends.server.util.args.SubCommand;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
/**
 * This class is handling server group CLI.
 */
@@ -88,11 +89,6 @@
  private DsFrameworkCliParser argParser;
  /**
   * The verbose argument.
   */
  private BooleanArgument verboseArg;
  /**
   * The enumeration containing the different subCommand names.
   */
  private enum SubCommandNameEnum
@@ -139,6 +135,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
      return name;
@@ -235,7 +232,7 @@
  /**
   * The subcommand list.
   */
  private HashSet<SubCommand> subCommands = new HashSet<SubCommand>();
  private final HashSet<SubCommand> subCommands = new HashSet<SubCommand>();
  /**
   * Indicates whether this subCommand should be hidden in the usage
@@ -278,7 +275,6 @@
  public void initializeCliGroup(DsFrameworkCliParser argParser,
      BooleanArgument verboseArg) throws ArgumentException
  {
    this.verboseArg = verboseArg;
    isHidden = false;
    groupName = "server";
    this.argParser = argParser;
opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliParser.java
@@ -22,42 +22,39 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.cli;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
import static org.opends.messages.ToolMessages.*;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.tools.ToolConstants.*;
import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
import static org.opends.server.util.StaticUtils.wrapText;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.logging.Logger;
import javax.net.ssl.KeyManager;
import org.opends.admin.ads.util.ApplicationTrustManager;
import org.opends.messages.Message;
import org.opends.messages.MessageBuilder;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.util.PasswordReader;
import org.opends.server.util.args.Argument;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.ArgumentGroup;
import org.opends.server.util.args.BooleanArgument;
import org.opends.server.util.args.FileBasedArgument;
import org.opends.server.util.args.StringArgument;
import org.opends.server.util.args.SubCommandArgumentParser;
import org.opends.server.util.args.ArgumentGroup;
/**
 * This is a commodity class that can be used to check the arguments required
@@ -105,12 +102,6 @@
  public static String EOL = System.getProperty("line.separator");
  /**
   * The Logger.
   */
  static private final Logger LOG =
    Logger.getLogger(SecureConnectionCliParser.class.getName());
  /**
   * Creates a new instance of this argument parser with no arguments.
   *
   * @param mainClassName
opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.ldap;
@@ -69,6 +69,7 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.client.AuthorizationException;
@@ -400,6 +401,51 @@
   * {@inheritDoc}
   */
  @Override
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(parent, rd);
    if (!managedObjectExists(parent)) {
      throw new ManagedObjectNotFoundException();
    }
    // Get the search base DN.
    LdapName dn = LDAPNameBuilder.create(parent, rd, profile);
    // Retrieve only those entries which are sub-types of the
    // specified definition.
    StringBuilder builder = new StringBuilder();
    builder.append("(objectclass=");
    builder.append(profile.getObjectClass(d));
    builder.append(')');
    String filter = builder.toString();
    List<String> children = new ArrayList<String>();
    try {
      for (LdapName child : connection.listEntries(dn, filter)) {
        children.add(child.getRdn(child.size() - 1).getValue().toString());
      }
    } catch (NameNotFoundException e) {
      // Ignore this - it means that the base entry does not exist
      // (which it might not if this managed object has just been
      // created.
    } catch (NamingException e) {
      adaptNamingException(e);
    }
    return children.toArray(new String[children.size()]);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean managedObjectExists(ManagedObjectPath<?, ?> path)
      throws ManagedObjectNotFoundException, AuthorizationException,
      CommunicationException {
opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.ldap;
@@ -54,6 +54,7 @@
import org.opends.server.admin.PropertyValueVisitor;
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
@@ -174,16 +175,23 @@
    }
    // We may need to create the parent "relation" entry if this is a
    // child of an instantiable relation.
    // child of an instantiable or set relation.
    RelationDefinition<?, ?> r = path.getRelationDefinition();
    if (r instanceof InstantiableRelationDefinition) {
      InstantiableRelationDefinition<?, ?> ir =
        (InstantiableRelationDefinition<?, ?>) r;
    if (r instanceof InstantiableRelationDefinition
        || r instanceof SetRelationDefinition) {
      // 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, driver.getLDAPProfile());
      LdapName dn;
      if (r instanceof InstantiableRelationDefinition) {
        dn = LDAPNameBuilder.create(parent,
            (InstantiableRelationDefinition) r, driver.getLDAPProfile());
      } else {
        dn = LDAPNameBuilder.create(parent,
            (SetRelationDefinition) r, driver.getLDAPProfile());
      }
      if (!driver.entryExists(dn)) {
        // We need to create the entry.
        Attributes attributes = new BasicAttributes();
@@ -191,7 +199,7 @@
        // Create the branch's object class attribute.
        Attribute oc = new BasicAttribute("objectClass");
        for (String objectClass : driver.getLDAPProfile()
            .getInstantiableRelationObjectClasses(ir)) {
            .getRelationObjectClasses(r)) {
          oc.add(objectClass);
        }
        attributes.put(oc);
opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.ldap;
@@ -44,6 +44,7 @@
import org.opends.server.admin.ManagedObjectPathSerializer;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
@@ -96,6 +97,30 @@
    return builder.getInstance();
  }
  /**
   * Creates a new LDAP name representing the specified managed object
   * path and set relation.
   *
   * @param path
   *          The managed object path.
   * @param relation
   *          The child set relation.
   * @param profile
   *          The LDAP profile which should be used to construct LDAP
   *          names.
   * @return Returns a new LDAP name representing the specified
   *         managed object path and set relation.
   */
  public static LdapName create(ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, ?> relation, LDAPProfile profile) {
    LDAPNameBuilder builder = new LDAPNameBuilder(profile);
    path.serialize(builder);
    builder.appendManagedObjectPathElement(relation);
    return builder.getInstance();
  }
  // The list of RDNs in big-endian order.
  private final LinkedList<Rdn> rdns;
@@ -126,10 +151,10 @@
          InstantiableRelationDefinition<? super C, ? super S> r,
          AbstractManagedObjectDefinition<C, S> d, String name) {
    // Add the RDN sequence representing the relation.
    appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
    appendManagedObjectPathElement(r);
    // Now add the single RDN representing the named instance.
    String type = profile.getInstantiableRelationChildRDNType(r);
    String type = profile.getRelationChildRDNType(r);
    try {
      Rdn rdn = new Rdn(type, name.trim());
      rdns.add(rdn);
@@ -168,7 +193,7 @@
          OptionalRelationDefinition<? super C, ? super S> r,
          AbstractManagedObjectDefinition<C, S> d) {
    // Add the RDN sequence representing the relation.
    appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
    appendManagedObjectPathElement(r);
  }
@@ -181,7 +206,30 @@
          SingletonRelationDefinition<? super C, ? super S> r,
          AbstractManagedObjectDefinition<C, S> d) {
    // Add the RDN sequence representing the relation.
    appendManagedObjectPathElement((RelationDefinition<?, ?>) r);
    appendManagedObjectPathElement(r);
  }
  /**
   * {@inheritDoc}
   */
  public <C extends ConfigurationClient, S extends Configuration>
      void appendManagedObjectPathElement(
          SetRelationDefinition<? super C, ? super S> r,
          AbstractManagedObjectDefinition<C, S> d) {
    // Add the RDN sequence representing the relation.
    appendManagedObjectPathElement(r);
    // Now add the single RDN representing the named instance.
    String type = profile.getRelationChildRDNType(r);
    try {
      Rdn rdn = new Rdn(type, d.getName());
      rdns.add(rdn);
    } catch (InvalidNameException e1) {
      // Should not happen.
      throw new RuntimeException(e1);
    }
  }
opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.spi;
@@ -59,7 +59,9 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelationDefinitionVisitor;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.ClientConstraintHandler;
import org.opends.server.admin.client.CommunicationException;
@@ -163,6 +165,25 @@
    /**
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
        Void visitSet(
        SetRelationDefinition<C, S> rd, Void p) {
      for (String name : rd.getDefaultManagedObjectNames()) {
        DefaultManagedObject<? extends C, ? extends S> dmo = rd
            .getDefaultManagedObject(name);
        ManagedObjectDefinition<? extends C, ? extends S> d = dmo
            .getManagedObjectDefinition();
        ManagedObject<? extends C> child = createChild(rd, d, null);
        createDefaultManagedObject(d, child, dmo);
      }
      return null;
    }
    // Create the child managed object.
    private void createDefaultManagedObject(ManagedObjectDefinition<?, ?> d,
        ManagedObject<?> child, DefaultManagedObject<?, ?> dmo) {
@@ -411,6 +432,24 @@
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration,
                CC extends C>
  ManagedObject<CC> createChild(
      SetRelationDefinition<C, S> r,
      ManagedObjectDefinition<CC, ? extends S> d,
      Collection<DefaultBehaviorException> exceptions)
      throws IllegalArgumentException {
    validateRelationDefinition(r);
    ManagedObjectPath<CC, ? extends S> childPath = path.child(r, d);
    return createNewManagedObject(d, childPath, null, null, exceptions);
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      InstantiableRelationDefinition<C, S> r, String name)
@@ -463,6 +502,40 @@
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  ManagedObject<? extends C> getChild(
      SetRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, DefinitionDecodingException,
      ManagedObjectDecodingException, ManagedObjectNotFoundException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    validateRelationDefinition(r);
    ensureThisManagedObjectExists();
    Driver ctx = getDriver();
    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
    AbstractManagedObjectDefinition<? extends C, ? extends S> cd;
    try
    {
      cd = d.getChild(name);
    }
    catch (IllegalArgumentException e)
    {
      // Unrecognized definition name - report this as a decoding
      // exception.
      throw new DefinitionDecodingException(d,
          Reason.WRONG_TYPE_INFORMATION);
    }
    return ctx.getManagedObject(path.child(r, cd));
  }
  /**
   * {@inheritDoc}
   */
  public final T getConfiguration() {
    return definition.createClientConfiguration(this);
  }
@@ -590,6 +663,39 @@
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(
      SetRelationDefinition<C, S> r) throws IllegalArgumentException,
      ConcurrentModificationException, AuthorizationException,
      CommunicationException {
    return listChildren(r, r.getChildDefinition());
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listChildren(
      SetRelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    try {
      return ctx.listManagedObjects(path, r, d);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  void removeChild(
      InstantiableRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
@@ -641,6 +747,32 @@
  /**
   * {@inheritDoc}
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  void removeChild(
      SetRelationDefinition<C, S> r, String name)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      OperationRejectedException, ConcurrentModificationException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(r);
    Driver ctx = getDriver();
    boolean found;
    try {
      found = ctx.deleteManagedObject(path, r, name);
    } catch (ManagedObjectNotFoundException e) {
      throw new ConcurrentModificationException();
    }
    if (!found) {
      throw new ManagedObjectNotFoundException();
    }
  }
  /**
   * {@inheritDoc}
   */
  public final <PD> void setPropertyValue(PropertyDefinition<PD> pd, PD value)
      throws IllegalPropertyValueException, PropertyIsReadOnlyException,
      PropertyIsMandatoryException, IllegalArgumentException {
opends/src/server/org/opends/server/admin/client/spi/Driver.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.client.spi;
@@ -34,7 +34,6 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import org.opends.messages.Message;
@@ -60,6 +59,7 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.admin.client.AuthorizationException;
@@ -403,6 +403,56 @@
  /**
   * Deletes the named instantiable child managed object from the
   * named parent managed object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @param name
   *          The name of the child managed object to be removed.
   * @return Returns <code>true</code> if the named instantiable
   *         child managed object was found, or <code>false</code>
   *         if it was not found.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws OperationRejectedException
   *           If the managed object cannot be removed due to some
   *           client-side or server-side constraint which cannot be
   *           satisfied (for example, if it is referenced by another
   *           managed object).
   * @throws AuthorizationException
   *           If the server refuses to remove the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  boolean deleteManagedObject(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
      String name) throws IllegalArgumentException,
      ManagedObjectNotFoundException, OperationRejectedException,
      AuthorizationException, CommunicationException {
    validateRelationDefinition(parent, rd);
    ManagedObjectPath<?, ?> child = parent.child(rd, name);
    return doDeleteManagedObject(child);
  }
  /**
   * Gets the named managed object. The path is guaranteed to be
   * non-empty, so implementations do not need to worry about handling
   * this special case.
@@ -442,60 +492,6 @@
  /**
   * Gets the effective value of a property in the named managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          path definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          path definition refers to.
   * @param <PD>
   *          The type of the property to be retrieved.
   * @param path
   *          The path of the managed object containing the property.
   * @param pd
   *          The property to be retrieved.
   * @return Returns the property's effective value, or
   *         <code>null</code> if there are no values defined.
   * @throws IllegalArgumentException
   *           If the property definition is not associated with the
   *           referenced managed object's definition.
   * @throws DefinitionDecodingException
   *           If the managed object was found but its type could not
   *           be determined.
   * @throws PropertyException
   *           If the managed object was found but the requested
   *           property could not be decoded.
   * @throws ManagedObjectNotFoundException
   *           If the requested managed object could not be found on
   *           the server.
   * @throws AuthorizationException
   *           If the server refuses to retrieve the managed object
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration, PD>
  PD getPropertyValue(ManagedObjectPath<C, S> path,
      PropertyDefinition<PD> pd) throws IllegalArgumentException,
      DefinitionDecodingException, AuthorizationException,
      ManagedObjectNotFoundException, CommunicationException,
      PropertyException {
    Set<PD> values = getPropertyValues(path, pd);
    if (values.isEmpty()) {
      return null;
    } else {
      return values.iterator().next();
    }
  }
  /**
   * Gets the effective values of a property in the named managed
   * object.
   * <p>
@@ -565,44 +561,6 @@
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The instantiable relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public final <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException {
    return listManagedObjects(parent, rd, rd.getChildDefinition());
  }
  /**
   * Lists the child managed objects of the named parent managed
   * object which are a sub-type of the specified managed object
   * definition.
   *
@@ -643,6 +601,47 @@
  /**
   * Lists the child managed objects of the named parent managed
   * object which are a sub-type of the specified managed object
   * definition.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The set relation definition.
   * @param d
   *          The managed object definition.
   * @return Returns the names of the child managed objects which are
   *         a sub-type of the specified managed object definition.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   * @throws ManagedObjectNotFoundException
   *           If the parent managed object could not be found.
   * @throws AuthorizationException
   *           If the server refuses to list the managed objects
   *           because the client does not have the correct
   *           privileges.
   * @throws CommunicationException
   *           If the client cannot contact the server due to an
   *           underlying communication problem.
   */
  public abstract <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws IllegalArgumentException, ManagedObjectNotFoundException,
      AuthorizationException, CommunicationException;
  /**
   * Determines whether or not the named managed object exists.
   * <p>
   * Implementations should always return <code>true</code> when the
opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.doc;
@@ -38,6 +38,7 @@
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.AdministratorAction.Type;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.AliasDefaultBehaviorProvider;
import org.opends.server.admin.AttributeTypePropertyDefinition;
@@ -64,7 +65,6 @@
import org.opends.server.admin.Tag;
import org.opends.server.admin.TopCfgDefn;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.std.meta.RootCfgDefn;
import org.opends.server.types.InitializationException;
import org.opends.server.util.EmbeddedUtils;
@@ -727,13 +727,13 @@
      Message synopsis = prop.getAdministratorAction().getSynopsis();
      Type actionType = prop.getAdministratorAction().getType();
      String actionStr = "";
      if (actionType == actionType.COMPONENT_RESTART) {
      if (actionType == Type.COMPONENT_RESTART) {
        actionStr = "The " + mo.getUserFriendlyName() +
          " must be disabled and re-enabled for changes to this setting " +
          "to take effect";
      } else if (actionType == actionType.SERVER_RESTART) {
      } else if (actionType == Type.SERVER_RESTART) {
        actionStr = "Restart the server";
      } else if (actionType == actionType.NONE) {
      } else if (actionType == Type.NONE) {
        actionStr = "None";
      }
      String dot = (actionStr.equals("") ? "" : ". ");
@@ -1448,12 +1448,6 @@
    }
  }
  private void paragraph(Message description, TextStyle style) {
    if (description != null) {
      paragraph(description.toString(), style, null);
    }
  }
  private void paragraph(String description) {
    paragraph(description, TextStyle.STANDARD, null);
  }
@@ -1469,9 +1463,9 @@
    if (getIndentPixels() > 0) {
      indentStr = "style=\"margin-left: " + getIndentPixels() + "px;\"";
    }
    if (style == style.BOLD) {
    if (style == TextStyle.BOLD) {
      styleStr = "style=\"font-weight: bold;\"";
    } else if (style == style.ITALIC) {
    } else if (style == TextStyle.ITALIC) {
      styleStr = "style=\"font-style: italic;\"";
    }
    if (pClass != null) {
@@ -1548,22 +1542,6 @@
    STANDARD, BOLD, ITALIC, UNDERLINE, FIXED_WIDTH
  }
  /**
   * List style.
   */
  private enum ListStyle {
    STANDARD, BULLET, NUMBER
  }
  private void indent() {
    ind++;
  }
  private void outdent() {
    ind--;
  }
  private void beginList() {
    inList = true;
    listLevel++;
@@ -1586,12 +1564,6 @@
      "</html>\n");
  }
  private static void usage() {
    System.err.println(
      "Usage : Provide the argument : output generation directory.");
    System.exit(1);
  }
  private void viewHelp(String helpStr) {
    htmlBuff.append(
      "<p class=\"view-help\" >" +
@@ -1617,21 +1589,22 @@
  }
  // Relation List from RootConfiguration
  private TreeMap<String, RelationDefinition> topRelList =
  private final TreeMap<String, RelationDefinition> topRelList =
    new TreeMap<String, RelationDefinition>();
  private TreeMap<String, RelationDefinition> relList =
  private final TreeMap<String, RelationDefinition> relList =
    new TreeMap<String, RelationDefinition>();
  private TreeMap<String, TreeMap<String, RelationDefinition>> catTopRelList =
    new TreeMap<String, TreeMap<String, RelationDefinition>>();
  private final TreeMap<String, TreeMap<String, RelationDefinition>>
    catTopRelList = new TreeMap<String, TreeMap<String, RelationDefinition>>();
  // managed object list
  private TreeMap<String, AbstractManagedObjectDefinition> moList =
  private final TreeMap<String, AbstractManagedObjectDefinition> moList =
    new TreeMap<String, AbstractManagedObjectDefinition>();
  private TreeMap<String, AbstractManagedObjectDefinition> topMoList =
  private final TreeMap<String, AbstractManagedObjectDefinition> topMoList =
    new TreeMap<String, AbstractManagedObjectDefinition>();
  private TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>
  private final TreeMap<String,
                        TreeMap<String, AbstractManagedObjectDefinition>>
    catTopMoList =
    new TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>();
  private int ind = 0;
      new TreeMap<String, TreeMap<String, AbstractManagedObjectDefinition>>();
  private final int ind = 0;
  private StringBuffer htmlBuff = new StringBuffer();
  private static String generationDir;
  private static boolean ldapMapping = false;
opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
@@ -38,10 +38,13 @@
import org.opends.server.admin.Configuration;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
@@ -76,6 +79,9 @@
  // The instantiable relation.
  private final InstantiableRelationDefinition<?, S> instantiableRelation;
  // The set relation.
  private final SetRelationDefinition<?, S> setRelation;
  // The underlying add listener.
  private final ServerManagedObjectAddListener<S> listener;
@@ -104,6 +110,7 @@
    this.path = path;
    this.instantiableRelation = relation;
    this.optionalRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
@@ -127,6 +134,31 @@
    this.path = path;
    this.optionalRelation = relation;
    this.instantiableRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration add listener adaptor for a
   * set relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The set relation.
   * @param listener
   *          The underlying add listener.
   */
  public ConfigAddListenerAdaptor(ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, S> relation,
      ServerManagedObjectAddListener<S> listener) {
    this.path = path;
    this.instantiableRelation = null;
    this.optionalRelation = null;
    this.setRelation = relation;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
@@ -186,22 +218,29 @@
    AttributeValue av = dn.getRDN().getAttributeValue(0);
    String name = av.getStringValue().trim();
    ManagedObjectPath<?, S> childPath;
    if (instantiableRelation != null) {
      childPath = path.child(instantiableRelation, name);
    } else {
      // Optional managed objects are located directly beneath the
      // parent and have a well-defined name. We need to make sure
      // that we are handling the correct entry.
      childPath = path.child(optionalRelation);
      DN expectedDN = DNBuilder.create(childPath);
      if (!dn.equals(expectedDN)) {
        // Doesn't apply to us.
        return true;
      }
    }
    try {
      ManagedObjectPath<?, ? extends S> childPath;
      if (instantiableRelation != null) {
        childPath = path.child(instantiableRelation, name);
      } else if (setRelation != null) {
        try {
          childPath = path.child(setRelation, name);
        } catch (IllegalArgumentException e) {
          throw new DefinitionDecodingException(setRelation
              .getChildDefinition(), Reason.WRONG_TYPE_INFORMATION);
        }
      } else {
        // Optional managed objects are located directly beneath the
        // parent and have a well-defined name. We need to make sure
        // that we are handling the correct entry.
        childPath = path.child(optionalRelation);
        DN expectedDN = DNBuilder.create(childPath);
        if (!dn.equals(expectedDN)) {
          // Doesn't apply to us.
          return true;
        }
      }
      ServerManagementContext context = ServerManagementContext.getInstance();
      cachedManagedObject = context.decode(childPath, configEntry, configEntry);
    } catch (DecodingException e) {
opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
@@ -39,10 +39,13 @@
import org.opends.server.admin.Configuration;
import org.opends.server.admin.Constraint;
import org.opends.server.admin.DecodingException;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.DefinitionDecodingException.Reason;
import org.opends.server.api.ConfigDeleteListener;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
@@ -77,6 +80,9 @@
  // The instantiable relation.
  private final InstantiableRelationDefinition<?, S> instantiableRelation;
  // The set relation.
  private final SetRelationDefinition<?, S> setRelation;
  // The underlying delete listener.
  private final ServerManagedObjectDeleteListener<S> listener;
@@ -105,6 +111,7 @@
    this.path = path;
    this.optionalRelation = null;
    this.instantiableRelation = relation;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
@@ -128,6 +135,31 @@
    this.path = path;
    this.optionalRelation = relation;
    this.instantiableRelation = null;
    this.setRelation = null;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
  /**
   * Create a new configuration delete listener adaptor for an
   * set relation.
   *
   * @param path
   *          The managed object path of the parent.
   * @param relation
   *          The set relation.
   * @param listener
   *          The underlying delete listener.
   */
  public ConfigDeleteListenerAdaptor(ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, S> relation,
      ServerManagedObjectDeleteListener<S> listener) {
    this.path = path;
    this.optionalRelation = null;
    this.instantiableRelation = null;
    this.setRelation = relation;
    this.listener = listener;
    this.cachedManagedObject = null;
  }
@@ -187,22 +219,29 @@
    AttributeValue av = dn.getRDN().getAttributeValue(0);
    String name = av.getStringValue().trim();
    ManagedObjectPath<?, S> childPath;
    if (instantiableRelation != null) {
      childPath = path.child(instantiableRelation, name);
    } else {
      // Optional managed objects are located directly beneath the
      // parent and have a well-defined name. We need to make sure
      // that we are handling the correct entry.
      childPath = path.child(optionalRelation);
      DN expectedDN = DNBuilder.create(childPath);
      if (!dn.equals(expectedDN)) {
        // Doesn't apply to us.
        return true;
      }
    }
    try {
      ManagedObjectPath<?, ? extends S> childPath;
      if (instantiableRelation != null) {
        childPath = path.child(instantiableRelation, name);
      } else if (setRelation != null) {
        try {
          childPath = path.child(setRelation, name);
        } catch (IllegalArgumentException e) {
          throw new DefinitionDecodingException(setRelation
              .getChildDefinition(), Reason.WRONG_TYPE_INFORMATION);
        }
      } else {
        // Optional managed objects are located directly beneath the
        // parent and have a well-defined name. We need to make sure
        // that we are handling the correct entry.
        childPath = path.child(optionalRelation);
        DN expectedDN = DNBuilder.create(childPath);
        if (!dn.equals(expectedDN)) {
          // Doesn't apply to us.
          return true;
        }
      }
      ServerManagementContext context = ServerManagementContext.getInstance();
      cachedManagedObject = context.decode(childPath, configEntry);
    } catch (DecodingException e) {
opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
@@ -40,7 +40,6 @@
import java.util.Set;
import java.util.SortedSet;
import org.opends.messages.AdminMessages;
import org.opends.messages.Message;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.Constraint;
@@ -51,6 +50,7 @@
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.api.ConfigAddListener;
import org.opends.server.api.ConfigChangeListener;
@@ -223,6 +223,55 @@
  /**
   * Deregisters an existing configuration add listener.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The configuration add listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   */
  public <M extends Configuration> void deregisterAddListener(
      SetRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    deregisterAddListener(baseDN, listener);
  }
  /**
   * Deregisters an existing server managed object add listener.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The server managed object add listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   */
  public <M extends Configuration> void deregisterAddListener(
      SetRelationDefinition<?, M> d,
      ServerManagedObjectAddListener<M> listener)
      throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    deregisterAddListener(baseDN, listener);
  }
  /**
   * Deregisters an existing configuration change listener.
   *
   * @param listener
@@ -371,6 +420,55 @@
  /**
   * Deregisters an existing configuration delete listener.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The configuration delete listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   */
  public <M extends Configuration> void deregisterDeleteListener(
      SetRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    deregisterDeleteListener(baseDN, listener);
  }
  /**
   * Deregisters an existing server managed object delete listener.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The server managed object delete listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   */
  public <M extends Configuration> void deregisterDeleteListener(
      SetRelationDefinition<?, M> d,
      ServerManagedObjectDeleteListener<M> listener)
      throws IllegalArgumentException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    deregisterDeleteListener(baseDN, listener);
  }
  /**
   * Retrieve an instantiable child managed object.
   *
   * @param <M>
@@ -423,6 +521,37 @@
  /**
   * Retrieve a set child managed object.
   *
   * @param <M>
   *          The requested type of the child server managed object
   *          configuration.
   * @param d
   *          The set relation definition.
   * @param name
   *          The name of the child managed object.
   * @return Returns the set child managed object.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition or if {@code name} specifies
   *           a managed object definition which is not a sub-type of
   *           the relation's child definition.
   * @throws ConfigException
   *           If the child managed object could not be found or if it
   *           could not be decoded.
   */
  public <M extends Configuration> ServerManagedObject<? extends M> getChild(
      SetRelationDefinition<?, M> d, String name)
      throws IllegalArgumentException, ConfigException
  {
    validateRelationDefinition(d);
    return context.getManagedObject(path.child(d, name));
  }
  /**
   * Retrieve a singleton child managed object.
   *
   * @param <M>
@@ -599,6 +728,25 @@
  /**
   * Lists the child managed objects associated with the specified
   * set relation.
   *
   * @param d
   *          The set relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with this
   *           managed object's definition.
   */
  public String[] listChildren(SetRelationDefinition<?, ?> d)
      throws IllegalArgumentException {
    validateRelationDefinition(d);
    return context.listManagedObjects(path, d);
  }
  /**
   * Register to be notified when new child configurations are added
   * beneath an instantiable relation.
   *
@@ -712,6 +860,63 @@
  /**
   * Register to be notified when new child configurations are added
   * beneath a set relation.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The configuration add listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   * @throws ConfigException
   *           If the configuration entry associated with the
   *           set relation could not be retrieved.
   */
  public <M extends Configuration> void registerAddListener(
      SetRelationDefinition<?, M> d,
      ConfigurationAddListener<M> listener) throws IllegalArgumentException,
      ConfigException {
    registerAddListener(d, new ServerManagedObjectAddListenerAdaptor<M>(
        listener));
  }
  /**
   * Register to be notified when new child server managed object are
   * added beneath a set relation.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The server managed object add listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   * @throws ConfigException
   *           If the configuration entry associated with the
   *           set relation could not be retrieved.
   */
  public <M extends Configuration> void registerAddListener(
      SetRelationDefinition<?, M> d,
      ServerManagedObjectAddListener<M> listener)
      throws IllegalArgumentException, ConfigException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigAddListener adaptor = new ConfigAddListenerAdaptor<M>(path, d,
        listener);
    registerAddListener(baseDN, adaptor);
  }
  /**
   * Register to be notified when this server managed object is
   * changed.
   *
@@ -876,6 +1081,63 @@
  /**
   * Register to be notified when existing child configurations are
   * deleted beneath a set relation.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The configuration delete listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   * @throws ConfigException
   *           If the configuration entry associated with the
   *           set relation could not be retrieved.
   */
  public <M extends Configuration> void registerDeleteListener(
      SetRelationDefinition<?, M> d,
      ConfigurationDeleteListener<M> listener) throws IllegalArgumentException,
      ConfigException {
    registerDeleteListener(d, new ServerManagedObjectDeleteListenerAdaptor<M>(
        listener));
  }
  /**
   * Register to be notified when existing child server managed
   * objects are deleted beneath a set relation.
   *
   * @param <M>
   *          The type of the child server configuration object.
   * @param d
   *          The set relation definition.
   * @param listener
   *          The server managed objects delete listener.
   * @throws IllegalArgumentException
   *           If the set relation definition is not
   *           associated with this managed object's definition.
   * @throws ConfigException
   *           If the configuration entry associated with the
   *           set relation could not be retrieved.
   */
  public <M extends Configuration> void registerDeleteListener(
      SetRelationDefinition<?, M> d,
      ServerManagedObjectDeleteListener<M> listener)
      throws IllegalArgumentException, ConfigException {
    validateRelationDefinition(d);
    DN baseDN = DNBuilder.create(path, d);
    ConfigDeleteListener adaptor = new ConfigDeleteListenerAdaptor<M>(path, d,
        listener);
    registerDeleteListener(baseDN, adaptor);
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -1079,7 +1341,7 @@
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
      Message message = ERR_ADMIN_CANNOT_GET_LISTENER_BASE.get(
          String.valueOf(dn), stackTraceToSingleLineString(e));
      throw new ConfigException(message, e);
    }
@@ -1125,7 +1387,7 @@
    }
    // No parent entry could be found.
    Message message = AdminMessages.ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
    Message message = ERR_ADMIN_UNABLE_TO_REGISTER_LISTENER
        .get(String.valueOf(baseDN));
    throw new ConfigException(message);
  }
opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
@@ -22,13 +22,14 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin.server;
import static org.opends.messages.AdminMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
@@ -43,7 +44,6 @@
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.messages.AdminMessages;
import org.opends.messages.Message;
import org.opends.server.admin.AbsoluteInheritedDefaultBehaviorProvider;
import org.opends.server.admin.AbstractManagedObjectDefinition;
@@ -72,6 +72,7 @@
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.DefinitionDecodingException.Reason;
@@ -111,7 +112,7 @@
    // Optional new configuration entry which does not yet exist in
    // the configuration back-end.
    private ConfigEntry newConfigEntry;
    private final ConfigEntry newConfigEntry;
    // The path of the managed object containing the next property.
    private ManagedObjectPath<?, ?> nextPath = null;
@@ -672,6 +673,58 @@
  /**
   * Lists the child managed objects of the named parent managed
   * object.
   *
   * @param <C>
   *          The type of client managed object configuration that the
   *          relation definition refers to.
   * @param <S>
   *          The type of server managed object configuration that the
   *          relation definition refers to.
   * @param parent
   *          The path of the parent managed object.
   * @param rd
   *          The set relation definition.
   * @return Returns the names of the child managed objects.
   * @throws IllegalArgumentException
   *           If the relation definition is not associated with the
   *           parent managed object's definition.
   */
  public <C extends ConfigurationClient, S extends Configuration>
  String[] listManagedObjects(
      ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd)
      throws IllegalArgumentException {
    validateRelationDefinition(parent, rd);
    // Get the target entry.
    DN targetDN = DNBuilder.create(parent, rd);
    ConfigEntry configEntry;
    try {
      configEntry = DirectoryServer.getConfigEntry(targetDN);
    } catch (ConfigException e) {
      return new String[0];
    }
    if (configEntry == null) {
      return new String[0];
    }
    // Retrieve the children.
    Set<DN> children = configEntry.getChildren().keySet();
    ArrayList<String> names = new ArrayList<String>(children.size());
    for (DN child : children) {
      // Assume that RDNs are single-valued and can be trimmed.
      AttributeValue av = child.getRDN().getAttributeValue(0);
      names.add(av.getStringValue().trim());
    }
    return names.toArray(new String[names.size()]);
  }
  /**
   * Determines whether or not the named managed object exists.
   *
   * @param path
@@ -902,7 +955,7 @@
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message = AdminMessages.ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
      Message message = ERR_ADMIN_CANNOT_GET_MANAGED_OBJECT.get(
          String.valueOf(dn), stackTraceToSingleLineString(e));
      throw new ConfigException(message, e);
    }
@@ -910,7 +963,7 @@
    // The configuration handler is free to return null indicating
    // that the entry does not exist.
    if (configEntry == null) {
      Message message = AdminMessages.ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
      Message message = ERR_ADMIN_MANAGED_OBJECT_DOES_NOT_EXIST
          .get(String.valueOf(dn));
      throw new ConfigException(message);
    }
opends/src/server/org/opends/server/api/ClientConnection.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.api;
@@ -71,8 +71,6 @@
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
/**
 * This class defines the set of methods and structures that must be
 * implemented by a Directory Server client connection.
@@ -114,7 +112,7 @@
  private int lookthroughLimit;
  // The time that this client connection was established.
  private long connectTime;
  private final long connectTime;
  // The idle time limit for this client connection.
  private long idleTimeLimit;
@@ -125,10 +123,11 @@
  // A string representation of the time that this client connection
  // was established.
  private String connectTimeString;
  private final String connectTimeString;
  // A set of persistent searches registered for this client.
  private CopyOnWriteArrayList<PersistentSearch> persistentSearches;
  private final CopyOnWriteArrayList<PersistentSearch>
      persistentSearches;
  // The network group to which the connection belongs to.
  private NetworkGroup networkGroup;
@@ -1099,14 +1098,8 @@
  public void setUnauthenticated()
  {
    setAuthenticationInfo(new AuthenticationInfo());
    this.sizeLimit = networkGroup.getSearchSizeLimit();
    if (this.sizeLimit == -1) {
        this.sizeLimit = DirectoryServer.getSizeLimit();
    }
    this.timeLimit = networkGroup.getSearchDurationLimit();
    if (this.timeLimit == -1) {
      this.timeLimit = DirectoryServer.getTimeLimit();
    }
    this.sizeLimit = networkGroup.getSizeLimit();
    this.timeLimit = networkGroup.getTimeLimit();
  }
@@ -1746,6 +1739,7 @@
   *
   * @return  A string representation of this client connection.
   */
  @Override
  public final String toString()
  {
    StringBuilder buffer = new StringBuilder();
@@ -1774,6 +1768,7 @@
   * processing, then it should override this method and make sure to
   * invoke {@code super.finalize} as its first call.
   */
  @Override
  protected void finalize()
  {
    finalizeConnectionInternal();
@@ -1818,14 +1813,8 @@
      this.networkGroup.addConnection(this);
      // The client connection inherits the resource limits
      sizeLimit = networkGroup.getSearchSizeLimit();
      if (sizeLimit == -1) {
        sizeLimit = DirectoryServer.getSizeLimit();
      }
      timeLimit = networkGroup.getSearchDurationLimit();
      if (timeLimit == -1) {
        timeLimit = DirectoryServer.getTimeLimit();
      }
      sizeLimit = networkGroup.getSizeLimit();
      timeLimit = networkGroup.getTimeLimit();
    }
  }
opends/src/server/org/opends/server/api/QOSPolicy.java
File was renamed from opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPolicy.java
@@ -22,25 +22,35 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
package org.opends.server.api;
/**
 * This class defines the network group policy. A client connection
 * that belongs to a network group has to comply with the policies
 * attach to the network group.
 * An abstract QOS policy.
 */
public class NetworkGroupPolicy
public abstract class QOSPolicy
{
  /**
   * Creates a new instance of the network group policy.
   * Creates a new abstract QOS Policy.
   */
  public NetworkGroupPolicy()
  protected QOSPolicy()
  {
    // No implementation is required.
    // No implementation required.
  }
  /**
   * Performs any necessary work to finalize this QOS policy.
   * <p>
   * The default implementation is to do nothing.
   */
  public void finalizeQOSPolicy()
  {
    // Do nothing by default.
  }
}
opends/src/server/org/opends/server/api/QOSPolicyFactory.java
New file
@@ -0,0 +1,89 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.api;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.admin.std.server.QOSPolicyCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.types.InitializationException;
/**
 * A factory for creating configurable quality of service (QOS)
 * policies.
 * <p>
 * All implementations must have a default constructor, i.e. one that
 * does not require and arguments.
 *
 * @param <T>
 *          The type of QOS policy configuration handled by this
 *          factory.
 */
public interface QOSPolicyFactory<T extends QOSPolicyCfg>
{
  /**
   * Creates a new QOS policy using the provided configuration.
   *
   * @param configuration
   *          The configuration.
   * @return The new QOS policy configured using the provided
   *         configuration.
   * @throws ConfigException
   *           If an unrecoverable problem arises during
   *           initialization of the QOS policy as a result
   *           of the server configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization of the QOS
   *           policy.
   */
  QOSPolicy createQOSPolicy(T configuration) throws ConfigException,
      InitializationException;
  /**
   * Indicates whether the provided QOS policy configuration is
   * acceptable.
   *
   * @param configuration
   *          The QOS policy configuration.
   * @param unacceptableReasons
   *          A list that can be used to hold messages about why the
   *          provided configuration is not acceptable.
   * @return Returns <code>true</code> if the provided QOS policy
   *         configuration is acceptable, or <code>false</code> if
   *         it is not.
   */
  boolean isConfigurationAcceptable(T configuration,
      List<Message> unacceptableReasons);
}
opends/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteria.java
New file
@@ -0,0 +1,104 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * A connection criteria which matches connections if and only if all
 * the sub-criteria match. If there are no sub-criteria then the
 * connection criteria will always match.
 */
final class ANDConnectionCriteria implements ConnectionCriteria
{
  // The list of underlying connection criteria.
  private final List<ConnectionCriteria> subCriteria;
  /**
   * Creates a new AND connection criteria using the provided
   * sub-criteria.
   *
   * @param subCriteria
   *          The sub-criteria.
   */
  public ANDConnectionCriteria(
      Collection<? extends ConnectionCriteria> subCriteria)
  {
    this.subCriteria = new ArrayList<ConnectionCriteria>(subCriteria);
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    for (ConnectionCriteria filter : subCriteria)
    {
      if (!filter.matches(connection))
      {
        return false;
      }
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    for (ConnectionCriteria filter : subCriteria)
    {
      if (!filter.willMatchAfterBind(connection, bindDN, authType,
          isSecure))
      {
        return false;
      }
    }
    return true;
  }
}
opends/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteria.java
New file
@@ -0,0 +1,142 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * A connection criteria which matches connections authenticated using a
 * permitted authentication method.
 */
final class AuthMethodConnectionCriteria implements ConnectionCriteria
{
  // The set of allowed authentication methods.
  private final Set<AllowedAuthMethod> authMethods;
  /**
   * Creates a new authentication method connection criteria using the
   * provided allowed authentication methods.
   *
   * @param authMethods
   *          The allowed authentication methods.
   */
  public AuthMethodConnectionCriteria(
      Collection<AllowedAuthMethod> authMethods)
  {
    this.authMethods = EnumSet.copyOf(authMethods);
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    AuthenticationInfo authInfo = connection.getAuthenticationInfo();
    for (AllowedAuthMethod method : authMethods)
    {
      switch (method)
      {
      case ANONYMOUS:
        if (!authInfo.isAuthenticated())
        {
          return true;
        }
        break;
      case SIMPLE:
        if (authInfo.hasAuthenticationType(AuthenticationType.SIMPLE))
        {
          return true;
        }
        break;
      case SASL:
        if (authInfo.hasAuthenticationType(AuthenticationType.SASL))
        {
          return true;
        }
        break;
      }
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    for (AllowedAuthMethod method : authMethods)
    {
      switch (method)
      {
      case ANONYMOUS:
        if (bindDN.toNormalizedString().length() == 0)
        {
          return true;
        }
        break;
      case SIMPLE:
        if (authType == AuthenticationType.SIMPLE
            && bindDN.toNormalizedString().length() > 0)
        {
          return true;
        }
        break;
      case SASL:
        if (authType == AuthenticationType.SASL)
        {
          return true;
        }
        break;
      }
    }
    return false;
  }
}
opends/src/server/org/opends/server/core/networkgroups/AuthMethodCriteria.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteria.java
New file
@@ -0,0 +1,138 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.opends.server.api.ClientConnection;
import org.opends.server.authorization.dseecompat.PatternDN;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
/**
 * A connection criteria which matches connections authenticated using a
 * permitted bind DN.
 */
final class BindDNConnectionCriteria implements ConnectionCriteria
{
  /**
   * Creates a new bind DN connection criteria using the provided DN
   * patterns.
   *
   * @param patterns
   *          The DN patterns.
   * @return The new bind DN connection criteria.
   */
  public static BindDNConnectionCriteria create(
      Collection<PatternDN> patterns)
  {
    return new BindDNConnectionCriteria(new ArrayList<PatternDN>(
        patterns));
  }
  /**
   * Creates a new bind DN connection criteria using the provided DN
   * pattern string representations.
   *
   * @param patternStrings
   *          The string representation of the DN patterns.
   * @return The new bind DN connection criteria.
   * @throws DirectoryException
   *           If one of the pattern strings is not valid.
   */
  public static BindDNConnectionCriteria decode(
      Collection<String> patternStrings) throws DirectoryException
  {
    List<PatternDN> patterns =
        new ArrayList<PatternDN>(patternStrings.size());
    for (String s : patternStrings)
    {
      patterns.add(PatternDN.decode(s));
    }
    return new BindDNConnectionCriteria(patterns);
  }
  // The list of permitted bind DN patterns.
  private final List<PatternDN> patterns;
  // Private constructor.
  private BindDNConnectionCriteria(List<PatternDN> patterns)
  {
    this.patterns = patterns;
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    DN dn = connection.getAuthenticationInfo().getAuthenticationDN();
    return willMatchAfterBind(connection, dn, null, false);
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    if (bindDN == null)
    {
      return false;
    }
    for (PatternDN pattern : patterns)
    {
      if (pattern.matchesDN(bindDN))
      {
        return true;
      }
    }
    return false;
  }
}
opends/src/server/org/opends/server/core/networkgroups/BindDnCriteria.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/ClientConnectionAffinityPolicy.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/ConnectionCriteria.java
New file
@@ -0,0 +1,139 @@
/*
 * 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
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * An interface for filtering connections based on implementation
 * specific criteria. Connection criteria are used by network groups to
 * determine whether a client connection should be associated with a
 * network group or not.
 */
interface ConnectionCriteria
{
  /**
   * A connection criteria which does not match any connections.
   */
  public static final ConnectionCriteria FALSE =
      new ConnectionCriteria()
        {
          /**
           * {@inheritDoc}
           */
          public boolean matches(ClientConnection connection)
          {
            return false;
          }
          /**
           * {@inheritDoc}
           */
          public boolean willMatchAfterBind(
              ClientConnection connection, DN bindDN,
              AuthenticationType authType, boolean isSecure)
          {
            return false;
          }
        };
  /**
   * A connection criteria which matches all connections.
   */
  public static final ConnectionCriteria TRUE =
      new ConnectionCriteria()
        {
          /**
           * {@inheritDoc}
           */
          public boolean matches(ClientConnection connection)
          {
            return true;
          }
          /**
           * {@inheritDoc}
           */
          public boolean willMatchAfterBind(
              ClientConnection connection, DN bindDN,
              AuthenticationType authType, boolean isSecure)
          {
            return true;
          }
        };
  /**
   * Indicates whether or not the provided client connection matches
   * this connection criteria.
   *
   * @param connection
   *          The client connection.
   * @return <code>true</code> if the provided client connection matches
   *         this connection criteria.
   */
  boolean matches(ClientConnection connection);
  /**
   * Indicates whether or not the provided client connection will match
   * this connection criteria using the provided authentication
   * parameters.
   *
   * @param connection
   *          The client connection.
   * @param bindDN
   *          The bind DN which will be used to authenticate.
   * @param authType
   *          The type of authentication which will be performed.
   * @param isSecure
   *          <code>true</code> if the connection will be secured.
   * @return <code>true</code> if the provided client connection will
   *         match this connection criteria using the provided
   *         authentication parameters.
   */
  boolean willMatchAfterBind(ClientConnection connection, DN bindDN,
      AuthenticationType authType, boolean isSecure);
}
opends/src/server/org/opends/server/core/networkgroups/IPConnectionCriteria.java
New file
@@ -0,0 +1,114 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.net.InetAddress;
import java.util.Collection;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AddressMask;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * A connection criteria which matches connections coming from a allowed
 * client address.
 */
final class IPConnectionCriteria implements ConnectionCriteria
{
  // The list of allowed client address masks.
  private final AddressMask[] allowedClients;
  // The list of denied client address masks.
  private final AddressMask[] deniedClients;
  /**
   * Creates a new IP connection criteria using the provided allowed and
   * denied address masks.
   *
   * @param allowedClients
   *          The list of allowed client address masks.
   * @param deniedClients
   *          The list of denied client address masks.
   */
  public IPConnectionCriteria(Collection<AddressMask> allowedClients,
      Collection<AddressMask> deniedClients)
  {
    this.allowedClients = allowedClients.toArray(new AddressMask[0]);
    this.deniedClients = deniedClients.toArray(new AddressMask[0]);
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    InetAddress ipAddr = connection.getRemoteAddress();
    byte[] address = ipAddr.getAddress();
    String hostName = ipAddr.getHostName();
    if (deniedClients.length > 0)
    {
      if (AddressMask
          .maskListContains(address, hostName, deniedClients))
      {
        return false;
      }
    }
    if (allowedClients.length > 0)
    {
      if (!AddressMask.maskListContains(address, hostName,
          allowedClients))
      {
        return false;
      }
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    return matches(connection);
  }
}
opends/src/server/org/opends/server/core/networkgroups/IpFilterCriteria.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
@@ -26,177 +26,1375 @@
 */
package org.opends.server.core.networkgroups;
import org.opends.messages.Message;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.util.Validator.ensureNotNull;
import static org.opends.server.loggers.ErrorLogger.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.server.admin.std.meta.
        NetworkGroupResourceLimitsCfgDefn.ReferralBindPolicy;
import org.opends.server.admin.std.meta.
        NetworkGroupResourceLimitsCfgDefn.ReferralPolicy;
import org.opends.messages.Message;
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.meta.QOSPolicyCfgDefn;
import org.opends.server.admin.std.server.NetworkGroupCfg;
import org.opends.server.admin.std.server.QOSPolicyCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.core.*;
import org.opends.server.api.QOSPolicy;
import org.opends.server.api.QOSPolicyFactory;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.RootDseWorkflowTopology;
import org.opends.server.core.Workflow;
import org.opends.server.core.WorkflowImpl;
import org.opends.server.core.WorkflowTopologyNode;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.protocols.ldap.LDAPMessage;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PreParseOperation;
import org.opends.server.workflowelement.WorkflowElement;
/**
 * This class defines the network group. A network group is used to categorize
 * client connections. A network group is defined by a set of criteria, a
 * set of policies and a set of workflow nodes. A client connection belongs to
 * a network group whenever it satisfies all the network group criteria. As
 * soon as a client connection belongs to a network group, it has to comply
 * with all the network group policies. Any cleared client operation can be
 * routed to one the network group workflow nodes.
 * This class defines the network group. A network group is used to
 * categorize client connections. A network group is defined by a set of
 * criteria, a set of policies and a set of workflow nodes. A client
 * connection belongs to a network group whenever it satisfies all the
 * network group criteria. As soon as a client connection belongs to a
 * network group, it has to comply with all the network group policies.
 * Any cleared client operation can be routed to one the network group
 * workflow nodes.
 */
public class NetworkGroup
{
  // Workflow nodes registered with the current network group.
  // Keys are workflowIDs.
  private TreeMap<String, WorkflowTopologyNode> registeredWorkflowNodes =
      new TreeMap<String, WorkflowTopologyNode>();
  /**
   * Configuration change listener for user network groups.
   */
  private final class ChangeListener implements
      ConfigurationChangeListener<NetworkGroupCfg>
  {
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationChange(
        NetworkGroupCfg configuration)
    {
      ResultCode resultCode = ResultCode.SUCCESS;
      boolean adminActionRequired = false;
      List<Message> messages = new ArrayList<Message>();
      // Update the priority.
      setNetworkGroupPriority(configuration.getPriority());
      // Deregister any workflows that have been removed.
      SortedSet<String> configWorkflows = configuration.getWorkflow();
      for (String id : getRegisteredWorkflows())
      {
        if (!configWorkflows.contains(id))
        {
          deregisterWorkflow(id);
        }
      }
      // Register any workflows that have been added.
      List<String> ngWorkflows = getRegisteredWorkflows();
      for (String id : configuration.getWorkflow())
      {
        if (!ngWorkflows.contains(id))
        {
          WorkflowImpl workflowImpl =
              (WorkflowImpl) WorkflowImpl.getWorkflow(id);
          try
          {
            registerWorkflow(workflowImpl);
          }
          catch (DirectoryException e)
          {
            if (resultCode == ResultCode.SUCCESS)
            {
              resultCode = e.getResultCode();
            }
            messages.add(e.getMessageObject());
          }
        }
      }
      try
      {
        criteria = decodeConnectionCriteriaConfiguration(configuration);
      }
      catch (ConfigException e)
      {
        resultCode = DirectoryServer.getServerErrorResultCode();
        messages.add(e.getMessageObject());
      }
      // Update the configuration.
      NetworkGroup.this.configuration = configuration;
      return new ConfigChangeResult(resultCode, adminActionRequired,
          messages);
    }
  // A lock to protect concurrent access to the registered Workflow nodes.
  private Object registeredWorkflowNodesLock = new Object();
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationChangeAcceptable(
        NetworkGroupCfg configuration, List<Message> unacceptableReasons)
    {
      return isConfigurationAcceptable(configuration,
          unacceptableReasons);
    }
  }
  /**
   * Configuration change listener for user network group QOS policies.
   */
  private final class QOSPolicyListener implements
      ConfigurationAddListener<QOSPolicyCfg>,
      ConfigurationDeleteListener<QOSPolicyCfg>
  {
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationAdd(
        QOSPolicyCfg configuration)
    {
      ResultCode resultCode = ResultCode.SUCCESS;
      boolean adminActionRequired = false;
      List<Message> messages = new ArrayList<Message>();
      try
      {
        createNetworkGroupQOSPolicy(configuration);
      }
      catch (ConfigException e)
      {
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      catch (InitializationException e)
      {
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      return new ConfigChangeResult(resultCode, adminActionRequired,
          messages);
    }
  // The workflow node for the rootDSE entry. The RootDSE workflow node
  // is not stored in the list of registered workflow nodes.
  private RootDseWorkflowTopology rootDSEWorkflowNode = null;
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationDelete(
        QOSPolicyCfg configuration)
    {
      QOSPolicy policy = policies.remove(configuration.dn());
      if (policy != null)
      {
        if (requestFilteringPolicy == policy)
        {
          requestFilteringPolicy = null;
        }
        else if (resourceLimitsPolicy == policy)
        {
          resourceLimitsPolicy = null;
        }
        policy.finalizeQOSPolicy();
      }
      return new ConfigChangeResult(ResultCode.SUCCESS, false);
    }
  // List of naming contexts handled by the network group.
  private NetworkGroupNamingContexts namingContexts =
      new NetworkGroupNamingContexts();
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationAddAcceptable(
        QOSPolicyCfg configuration, List<Message> unacceptableReasons)
    {
      return isNetworkGroupQOSPolicyConfigurationAcceptable(
          configuration, unacceptableReasons);
    }
  // The default network group (singleton).
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationDeleteAcceptable(
        QOSPolicyCfg configuration, List<Message> unacceptableReasons)
    {
      // Always ok.
      return true;
    }
  }
  // The admin network group has no criterion, no policy,
  // and gives access to all the workflows.
  private static final String ADMIN_NETWORK_GROUP_NAME = "admin";
  private static NetworkGroup adminNetworkGroup =
      new NetworkGroup(ADMIN_NETWORK_GROUP_NAME);
  // The default network group has no criterion, no policy, and gives
  // access to all the workflows. The purpose of the default network
  // group is to allow new clients to perform a first operation before
  // they can be attached to a specific network group.
  private static final String DEFAULT_NETWORK_GROUP_NAME = "default";
  private final boolean isDefaultNetworkGroup;
  private static NetworkGroup defaultNetworkGroup =
      new NetworkGroup (DEFAULT_NETWORK_GROUP_NAME);
      new NetworkGroup(DEFAULT_NETWORK_GROUP_NAME);
  // The admin network group (singleton).
  // The admin network group has no criterion, no policy, and gives
  // access to all the workflows.
  private static final String ADMIN_NETWORK_GROUP_NAME = "admin";
  private final boolean isAdminNetworkGroup;
  private static NetworkGroup adminNetworkGroup =
      new NetworkGroup (ADMIN_NETWORK_GROUP_NAME);
  // The internal network group (singleton).
  // The internal network group has no criterion, no policy, and gives
  // access to all the workflows. The purpose of the internal network
  // group is to allow internal connections to perform operations.
  private static final String INTERNAL_NETWORK_GROUP_NAME = "internal";
  private boolean isInternalNetworkGroup;
  private static NetworkGroup internalNetworkGroup =
      new NetworkGroup(INTERNAL_NETWORK_GROUP_NAME);
  // The ordered list of network groups.
  private static List<NetworkGroup> orderedNetworkGroups =
      new ArrayList<NetworkGroup>();
  // The list of all network groups that are registered with the server.
  // The defaultNetworkGroup is not in the list of registered network groups.
  // The defaultNetworkGroup is not in the list of registered network
  // groups.
  private static TreeMap<String, NetworkGroup> registeredNetworkGroups =
      new TreeMap<String, NetworkGroup>();
  // A lock to protect concurrent access to the registeredNetworkGroups.
  private static Object registeredNetworkGroupsLock = new Object();
  // The ordered list of network groups.
  private static List<NetworkGroup> orderedNetworkGroups =
      new ArrayList<NetworkGroup>();
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // The network group internal identifier.
  private String networkGroupID = null;
  // The network group priority
  private int priority = 100;
  // The network group criteria.
  private NetworkGroupCriteria criteria = null;
  // The network group resource limits
  private ResourceLimits resourceLimits = null;
  // The network group request filtering policy
  private RequestFilteringPolicy requestFilteringPolicy = null;
  // The statistics
  private NetworkGroupStatistics stats;
  // The client connection affinity policy.
  private ClientConnectionAffinityPolicy affinityPolicy =
    ClientConnectionAffinityPolicy.NONE;
  // The client connection affinity timeout (number of seconds).
  private long affinityTimeout = 0;
  /**
   * Creates a new instance of the network group.
   *
   * @param networkGroupID  the network group internal identifier
   * Deregisters all network groups that have been registered. This
   * should be called when the server is shutting down.
   */
  public NetworkGroup(
      String networkGroupID
      )
  public static void deregisterAllOnShutdown()
  {
    synchronized (registeredNetworkGroupsLock)
    {
      // Invalidate all NetworkGroups so they cannot accidentally be
      // used after a restart.
      Collection<NetworkGroup> networkGroups =
          registeredNetworkGroups.values();
      for (NetworkGroup networkGroup : networkGroups)
      {
        networkGroup.invalidate();
      }
      defaultNetworkGroup.invalidate();
      adminNetworkGroup.invalidate();
      internalNetworkGroup.invalidate();
      registeredNetworkGroups = new TreeMap<String, NetworkGroup>();
      orderedNetworkGroups = new ArrayList<NetworkGroup>();
      defaultNetworkGroup = new NetworkGroup("default");
      adminNetworkGroup = new NetworkGroup("admin");
      internalNetworkGroup = new NetworkGroup("internal");
    }
  }
  /**
   * Gets the highest priority matching network group for a BIND op.
   *
   * @param connection
   *          the client connection
   * @param dn
   *          the operation bindDN
   * @param authType
   *          the operation authentication type
   * @param isSecure
   *          a boolean indicating whether the operation is secured
   * @return matching network group
   */
  static NetworkGroup findBindMatchingNetworkGroup(
      ClientConnection connection, DN dn, AuthenticationType authType,
      boolean isSecure)
  {
    for (NetworkGroup ng : orderedNetworkGroups)
    {
      if (ng.matchAfterBind(connection, dn, authType, isSecure))
      {
        return ng;
      }
    }
    return defaultNetworkGroup;
  }
  /**
   * Gets the highest priority matching network group.
   *
   * @param connection
   *          the client connection
   * @return matching network group
   */
  static NetworkGroup findMatchingNetworkGroup(
      ClientConnection connection)
  {
    for (NetworkGroup ng : orderedNetworkGroups)
    {
      if (ng.match(connection))
      {
        return ng;
      }
    }
    return defaultNetworkGroup;
  }
  /**
   * Returns the admin network group.
   *
   * @return the admin network group
   */
  public static NetworkGroup getAdminNetworkGroup()
  {
    return adminNetworkGroup;
  }
  /**
   * Returns the default network group. The default network group is
   * always defined and has no criterion, no policy and provide full
   * access to all the registered workflows.
   *
   * @return the default network group
   */
  public static NetworkGroup getDefaultNetworkGroup()
  {
    return defaultNetworkGroup;
  }
  /**
   * Returns the internal network group.
   *
   * @return the internal network group
   */
  public static NetworkGroup getInternalNetworkGroup()
  {
    return internalNetworkGroup;
  }
  /**
   * Gets the network group having the specified ID.
   * <p>
   * This method is for testing only.
   *
   * @param networkGroupID
   *          The network group ID.
   * @return The network group, of <code>null</code> if no match was found.
   */
  public static NetworkGroup getNetworkGroup(String networkGroupID)
  {
    return registeredNetworkGroups.get(networkGroupID);
  }
  /**
   * Resets the configuration of all the registered network groups.
   */
  public static void resetConfig()
  {
    // Reset the default network group
    defaultNetworkGroup.reset();
    adminNetworkGroup.reset();
    internalNetworkGroup.reset();
    // Reset all the registered network group
    synchronized (registeredNetworkGroupsLock)
    {
      registeredNetworkGroups = new TreeMap<String, NetworkGroup>();
      orderedNetworkGroups = new ArrayList<NetworkGroup>();
    }
  }
  /**
   * Initializes this network group as a user network group using the
   * provided configuration. The network group will monitor the
   * configuration and update its configuration when necessary.
   *
   * @param configuration
   *          The network group configuration.
   * @return The new user network group.
   * @throws ConfigException
   *           If an unrecoverable problem arises during initialization
   *           of the user network group as a result of the server
   *           configuration.
   * @throws InitializationException
   *           If a problem occurs during initialization of the user
   *           network group that is not related to the server
   *           configuration.
   */
  static NetworkGroup createUserNetworkGroup(
      NetworkGroupCfg configuration) throws InitializationException,
      ConfigException
  {
    NetworkGroup networkGroup = new NetworkGroup(configuration);
    try
    {
      // Set the priority.
      networkGroup.priority = configuration.getPriority();
      // Initialize the network group criteria.
      networkGroup.criteria =
          decodeConnectionCriteriaConfiguration(configuration);
      // Initialize the network group policies.
      for (String policyName : configuration
          .listNetworkGroupQOSPolicies())
      {
        QOSPolicyCfg policyConfiguration =
            configuration.getNetworkGroupQOSPolicy(policyName);
        networkGroup.createNetworkGroupQOSPolicy(policyConfiguration);
      }
      // Register the root DSE workflow with the network group.
      WorkflowImpl rootDSEworkflow =
          (WorkflowImpl) WorkflowImpl.getWorkflow("__root.dse__#");
      networkGroup.registerWorkflow(rootDSEworkflow);
      // Register the workflows with the network group.
      for (String workflowID : configuration.getWorkflow())
      {
        WorkflowImpl workflowImpl =
            (WorkflowImpl) WorkflowImpl.getWorkflow(workflowID);
        if (workflowImpl == null)
        {
          // The workflow does not exist, log an error message
          // and skip the workflow.
          Message message =
              INFO_ERR_WORKFLOW_DOES_NOT_EXIST.get(workflowID,
                  networkGroup.getID());
          logError(message);
        }
        else
        {
          networkGroup.registerWorkflow(workflowImpl);
        }
      }
      // Register all configuration change listeners.
      configuration.addChangeListener(networkGroup.changeListener);
      configuration
          .addNetworkGroupQOSPolicyAddListener(networkGroup.policyListener);
      configuration
          .addNetworkGroupQOSPolicyDeleteListener(networkGroup.policyListener);
      // Register the network group with the server.
      networkGroup.register();
    }
    catch (DirectoryException e)
    {
      networkGroup.finalizeNetworkGroup();
      throw new InitializationException(e.getMessageObject());
    }
    catch (InitializationException e)
    {
      networkGroup.finalizeNetworkGroup();
      throw e;
    }
    catch (ConfigException e)
    {
      networkGroup.finalizeNetworkGroup();
      throw e;
    }
    return networkGroup;
  }
  /**
   * Indicates whether the provided network group configuration is
   * acceptable.
   *
   * @param configuration
   *          The network group configuration.
   * @param unacceptableReasons
   *          A list that can be used to hold messages about why the
   *          provided configuration is not acceptable.
   * @return Returns <code>true</code> if the provided network group
   *         configuration is acceptable, or <code>false</code> if it is
   *         not.
   */
  static boolean isConfigurationAcceptable(
      NetworkGroupCfg configuration, List<Message> unacceptableReasons)
  {
    // The configuration is always acceptable if disabled.
    if (!configuration.isEnabled())
    {
      return true;
    }
    // Check that all the workflows in the network group have a
    // different base DN.
    boolean isAcceptable = true;
    Set<String> allBaseDNs = new HashSet<String>();
    for (String workflowId : configuration.getWorkflow())
    {
      WorkflowImpl workflow =
          (WorkflowImpl) WorkflowImpl.getWorkflow(workflowId);
      String baseDN = workflow.getBaseDN().toNormalizedString();
      if (allBaseDNs.contains(baseDN))
      {
        // This baseDN is duplicated
        Message message =
            ERR_WORKFLOW_BASE_DN_DUPLICATED_IN_NG.get(baseDN,
                getNameFromConfiguration(configuration));
        unacceptableReasons.add(message);
        isAcceptable = false;
        break;
      }
      else
      {
        allBaseDNs.add(baseDN);
      }
    }
    // Validate any policy configurations.
    for (String policyName : configuration
        .listNetworkGroupQOSPolicies())
    {
      try
      {
        QOSPolicyCfg policyCfg =
            configuration.getNetworkGroupQOSPolicy(policyName);
        if (!isNetworkGroupQOSPolicyConfigurationAcceptable(policyCfg,
            unacceptableReasons))
        {
          isAcceptable = false;
        }
      }
      catch (ConfigException e)
      {
        // This is bad - give up immediately.
        unacceptableReasons.add(e.getMessageObject());
        return false;
      }
    }
    // The bind DN patterns may be malformed.
    if (!configuration.getAllowedBindDN().isEmpty())
    {
      try
      {
        BindDNConnectionCriteria.decode(configuration
            .getAllowedBindDN());
      }
      catch (DirectoryException e)
      {
        unacceptableReasons.add(e.getMessageObject());
        isAcceptable = false;
      }
    }
    return isAcceptable;
  }
  // Decodes connection criteria configuration.
  private static ConnectionCriteria decodeConnectionCriteriaConfiguration(
      NetworkGroupCfg configuration) throws ConfigException
  {
    List<ConnectionCriteria> filters =
        new LinkedList<ConnectionCriteria>();
    if (!configuration.getAllowedAuthMethod().isEmpty())
    {
      filters.add(new AuthMethodConnectionCriteria(configuration
          .getAllowedAuthMethod()));
    }
    if (!configuration.getAllowedBindDN().isEmpty())
    {
      try
      {
        filters.add(BindDNConnectionCriteria.decode(configuration
            .getAllowedBindDN()));
      }
      catch (DirectoryException e)
      {
        throw new ConfigException(e.getMessageObject());
      }
    }
    if (!configuration.getAllowedClient().isEmpty()
        || !configuration.getDeniedClient().isEmpty())
    {
      filters.add(new IPConnectionCriteria(configuration
          .getAllowedClient(), configuration.getDeniedClient()));
    }
    if (!configuration.getAllowedProtocol().isEmpty())
    {
      filters.add(new ProtocolConnectionCriteria(configuration
          .getAllowedProtocol()));
    }
    if (configuration.isIsSecurityMandatory())
    {
      filters.add(SecurityConnectionCriteria.SECURITY_REQUIRED);
    }
    if (filters.isEmpty())
    {
      return ConnectionCriteria.TRUE;
    }
    else
    {
      return new ANDConnectionCriteria(filters);
    }
  }
  /**
   * Gets the name of the network group configuration.
   *
   * @param configuration
   *          The configuration.
   * @return The network group name.
   */
  private static String getNameFromConfiguration(NetworkGroupCfg configuration)
  {
    DN dn = configuration.dn();
    return dn.getRDN().getAttributeValue(0).getStringValue();
  }
  // Determines whether or not the new network group configuration's
  // implementation class is acceptable.
  private static boolean isNetworkGroupQOSPolicyConfigurationAcceptable(
      QOSPolicyCfg policyConfiguration,
      List<Message> unacceptableReasons)
  {
    String className = policyConfiguration.getJavaClass();
    QOSPolicyCfgDefn d = QOSPolicyCfgDefn.getInstance();
    ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition();
    // Validate the configuration.
    try
    {
      // Load the class and cast it to a network group policy factory.
      Class<? extends QOSPolicyFactory> theClass;
      QOSPolicyFactory factory;
      theClass = pd.loadClass(className, QOSPolicyFactory.class);
      factory = theClass.newInstance();
      // Determine the initialization method to use: it must take a
      // single parameter which is the exact type of the configuration
      // object.
      Method method =
          theClass.getMethod("isConfigurationAcceptable",
              QOSPolicyCfg.class, List.class);
      Boolean acceptable =
          (Boolean) method.invoke(factory, policyConfiguration,
              unacceptableReasons);
      if (!acceptable)
      {
        return false;
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      unacceptableReasons
          .add(ERR_CONFIG_NETWORK_GROUP_POLICY_CANNOT_INITIALIZE.get(
              String.valueOf(className), String
                  .valueOf(policyConfiguration.dn()),
              stackTraceToSingleLineString(e)));
      return false;
    }
    // The configuration is valid as far as we can tell.
    return true;
  }
  // Change listener (active for user network groups).
  private final ChangeListener changeListener;
  // Current configuration (active for user network groups).
  private NetworkGroupCfg configuration = null;
  // The network group connection criteria.
  private ConnectionCriteria criteria = ConnectionCriteria.TRUE;
  private final boolean isAdminNetworkGroup;
  private final boolean isDefaultNetworkGroup;
  private final boolean isInternalNetworkGroup;
  // List of naming contexts handled by the network group.
  private NetworkGroupNamingContexts namingContexts =
      new NetworkGroupNamingContexts();
  // The network group internal identifier.
  private final String networkGroupID;
  // All network group policies mapping factory class name to policy.
  private final Map<DN, QOSPolicy> policies =
      new ConcurrentHashMap<DN, QOSPolicy>();
  // Add/delete policy listener (active for user network groups).
  private final QOSPolicyListener policyListener;
  // The network group priority.
  private int priority = 100;
  // Workflow nodes registered with the current network group.
  // Keys are workflowIDs.
  private TreeMap<String, WorkflowTopologyNode> registeredWorkflowNodes =
      new TreeMap<String, WorkflowTopologyNode>();
  // A lock to protect concurrent access to the registered Workflow
  // nodes.
  private final Object registeredWorkflowNodesLock = new Object();
  // The network group request filtering policy.
  private RequestFilteringPolicy requestFilteringPolicy = null;
  // The network group resource limits policy.
  private ResourceLimitsPolicy resourceLimitsPolicy = null;
  // The workflow node for the rootDSE entry. The RootDSE workflow node
  // is not stored in the list of registered workflow nodes.
  private RootDseWorkflowTopology rootDSEWorkflowNode = null;
  // The network group statistics.
  private final NetworkGroupStatistics statistics;
  /**
   * Creates a new system network group using the provided ID.
   *
   * @param networkGroupID
   *          The network group internal identifier.
   */
  public NetworkGroup(String networkGroupID)
  {
    this.networkGroupID = networkGroupID;
    isInternalNetworkGroup = INTERNAL_NETWORK_GROUP_NAME.equals(networkGroupID);
    isAdminNetworkGroup    = ADMIN_NETWORK_GROUP_NAME.equals(networkGroupID);
    isDefaultNetworkGroup  = DEFAULT_NETWORK_GROUP_NAME.equals(networkGroupID);
    stats = new NetworkGroupStatistics(this);
    this.isInternalNetworkGroup =
        INTERNAL_NETWORK_GROUP_NAME.equals(networkGroupID);
    this.isAdminNetworkGroup =
        ADMIN_NETWORK_GROUP_NAME.equals(networkGroupID);
    this.isDefaultNetworkGroup =
        DEFAULT_NETWORK_GROUP_NAME.equals(networkGroupID);
    this.statistics = new NetworkGroupStatistics(this);
    this.configuration = null;
    this.changeListener = null;
    this.policyListener = null;
  }
  /**
   * Creates a new user network group using the provided configuration.
   */
  private NetworkGroup(NetworkGroupCfg configuration)
  {
    this.networkGroupID = getNameFromConfiguration(configuration);
    this.isInternalNetworkGroup = false;
    this.isAdminNetworkGroup = false;
    this.isDefaultNetworkGroup = false;
    this.statistics = new NetworkGroupStatistics(this);
    this.configuration = configuration;
    this.changeListener = new ChangeListener();
    this.policyListener = new QOSPolicyListener();
  }
  /**
   * Adds a connection to the group.
   *
   * @param connection
   *          the ClientConnection
   */
  public void addConnection(ClientConnection connection)
  {
    if (resourceLimitsPolicy != null)
    {
      resourceLimitsPolicy.addConnection(connection);
    }
  }
  /**
   * Checks the request filtering policy.
   *
   * @param operation
   *          the operation to be checked
   * @param messages
   *          the error messages
   * @return boolean indicating whether the operation conforms to the
   *         network group request filtering policy
   */
  boolean checkRequestFilteringPolicy(
      PreParseOperation operation, List<Message> messages)
  {
    if (requestFilteringPolicy != null)
    {
      return requestFilteringPolicy.isAllowed(operation, messages);
    }
    else
    {
      return true;
    }
  }
  /**
   * Checks the resource limits policy.
   *
   * @param connection
   *          the client connection
   * @param operation
   *          the ongoing operation
   * @param fullCheck
   *          a boolean indicating the level of checking: full/partial
   * @param messages
   *          the messages indicating the cause of the failure.
   * @return a boolean indicating whether resource limits are exceeded
   */
  boolean checkResourceLimitsPolicy(ClientConnection connection,
      PreParseOperation operation, boolean fullCheck,
      List<Message> messages)
  {
    if (resourceLimitsPolicy != null)
    {
      return resourceLimitsPolicy.isAllowed(connection, operation,
          fullCheck, messages);
    }
    else
    {
      return true;
    }
  }
  /**
   * Deregisters a workflow with the network group. The workflow to
   * deregister is identified by its baseDN.
   *
   * @param baseDN
   *          the baseDN of the workflow to deregister, may be null
   * @return the deregistered workflow
   */
  public Workflow deregisterWorkflow(DN baseDN)
  {
    Workflow workflow = null;
    if (baseDN == null)
    {
      return workflow;
    }
    if (baseDN.isNullDN())
    {
      // deregister the rootDSE
      deregisterWorkflow(rootDSEWorkflowNode);
      workflow = rootDSEWorkflowNode.getWorkflowImpl();
    }
    else
    {
      // deregister a workflow node
      synchronized (registeredWorkflowNodesLock)
      {
        for (WorkflowTopologyNode node : registeredWorkflowNodes
            .values())
        {
          DN curDN = node.getBaseDN();
          if (curDN.equals(baseDN))
          {
            // Call deregisterWorkflow() instead of
            // deregisterWorkflowNode() because we want the naming
            // context list to be updated as well.
            deregisterWorkflow(node);
            workflow = node.getWorkflowImpl();
            // Only one workflow can match the baseDN, so we can break
            // the loop here.
            break;
          }
        }
      }
    }
    // Now that the workflow node has been deregistered with the network
    // group, update the reference counter of the workflow.
    if ((workflow != null) && !isAdminNetworkGroup
        && !isInternalNetworkGroup && !isDefaultNetworkGroup)
    {
      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
      workflowImpl.decrementReferenceCounter();
    }
    return workflow;
  }
  /**
   * Deregisters a workflow with the network group. The workflow to
   * deregister is identified by its workflow ID.
   *
   * @param workflowID
   *          the workflow identifier of the workflow to deregister
   * @return the deregistered workflow
   */
  public Workflow deregisterWorkflow(String workflowID)
  {
    Workflow workflow = null;
    String rootDSEWorkflowID = null;
    if (rootDSEWorkflowNode != null)
    {
      rootDSEWorkflowID =
          rootDSEWorkflowNode.getWorkflowImpl().getWorkflowId();
    }
    if (workflowID.equalsIgnoreCase(rootDSEWorkflowID))
    {
      // deregister the rootDSE
      deregisterWorkflow(rootDSEWorkflowNode);
      workflow = rootDSEWorkflowNode.getWorkflowImpl();
    }
    else
    {
      // deregister a workflow node
      synchronized (registeredWorkflowNodesLock)
      {
        for (WorkflowTopologyNode node : registeredWorkflowNodes
            .values())
        {
          String curID = node.getWorkflowImpl().getWorkflowId();
          if (curID.equals(workflowID))
          {
            // Call deregisterWorkflow() instead of
            // deregisterWorkflowNode() because we want the naming
            // context list to be updated as well.
            deregisterWorkflow(node);
            workflow = node.getWorkflowImpl();
            // Only one workflow can match the baseDN, so we can break
            // the loop here.
            break;
          }
        }
      }
    }
    // Now that the workflow node has been deregistered with the network
    // group, update the reference counter of the workflow.
    if ((workflow != null) && !isAdminNetworkGroup
        && !isInternalNetworkGroup && !isDefaultNetworkGroup)
    {
      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
      workflowImpl.decrementReferenceCounter();
    }
    return workflow;
  }
  /**
   * Performs any finalization that might be required when this network
   * group is unloaded. No action is taken in the default
   * implementation.
   */
  public void finalizeNetworkGroup()
  {
    if (configuration != null)
    {
      // Finalization specific to user network groups.
      deregister();
      // Remove all change listeners.
      configuration.removeChangeListener(changeListener);
      configuration
          .removeNetworkGroupQOSPolicyAddListener(policyListener);
      configuration
          .removeNetworkGroupQOSPolicyDeleteListener(policyListener);
      configuration = null;
    }
    // Clean up policies.
    for (QOSPolicy policy : policies.values())
    {
      policy.finalizeQOSPolicy();
    }
    requestFilteringPolicy = null;
    resourceLimitsPolicy = null;
    criteria = ConnectionCriteria.TRUE;
    policies.clear();
  }
  /**
   * Retrieves the network group ID.
   *
   * @return a string indicating the network group ID
   */
  public String getID() {
  public String getID()
  {
    return networkGroupID;
  }
  /**
   * Performs any finalization that might be required when this
   * network group is unloaded.  No action is taken in the
   * default implementation.
   * Gets the minimum string length of a substring filter in a search
   * operation.
   *
   * @return the minimum substring length
   */
  public void finalizeNetworkGroup()
  public int getMinSubstring()
  {
    // No action is required by default.
    if (resourceLimitsPolicy != null)
    {
      return resourceLimitsPolicy.getMinSubstring();
    }
    else
    {
      return 0;
    }
  }
  /**
   * Returns the list of naming contexts handled by the network group.
   *
   * @return the list of naming contexts
   */
  public NetworkGroupNamingContexts getNamingContexts()
  {
    return namingContexts;
  }
  /**
   * Returns the QOS policy associated with this network group having
   * the specified class.
   *
   * @param <T>
   *          The type of QOS policy.
   * @param clazz
   *          The class of QOS policy requested.
   * @return The QOS policy associated with this network group having
   *         the specified class, or <code>null</code> if none was
   *         found.
   */
  public <T extends QOSPolicy> T getNetworkGroupQOSPolicy(Class<T> clazz)
  {
    for (QOSPolicy policy : policies.values())
    {
      if (clazz.isAssignableFrom(policy.getClass()))
      {
        return clazz.cast(policy);
      }
    }
    return null;
  }
  /**
   * Gets the search size limit, i.e. the maximum number of entries
   * returned by a search.
   *
   * @return the maximum number of entries returned by a search
   */
  public int getSizeLimit()
  {
    if (resourceLimitsPolicy != null)
    {
      return resourceLimitsPolicy.getSizeLimit();
    }
    else
    {
      return DirectoryServer.getSizeLimit();
    }
  }
  /**
   * Gets the search duration limit, i.e. the maximum duration of a
   * search operation.
   *
   * @return the maximum duration in ms of a search operation
   */
  public int getTimeLimit()
  {
    if (resourceLimitsPolicy != null)
    {
      return resourceLimitsPolicy.getTimeLimit();
    }
    else
    {
      return DirectoryServer.getTimeLimit();
    }
  }
  /**
   * Gets the highest workflow in the topology that can handle the
   * baseDN.
   *
   * @param baseDN
   *          the base DN of the request
   * @return the highest workflow in the topology that can handle the
   *         base DN, <code>null</code> if none was found
   */
  public Workflow getWorkflowCandidate(DN baseDN)
  {
    // the top workflow to return
    Workflow workflowCandidate = null;
    // get the list of workflow candidates
    if (baseDN.isNullDN())
    {
      // The rootDSE workflow is the candidate.
      workflowCandidate = rootDSEWorkflowNode;
    }
    else
    {
      // Search the highest workflow in the topology that can handle
      // the baseDN.
      for (WorkflowTopologyNode curWorkflow : namingContexts
          .getNamingContexts())
      {
        workflowCandidate = curWorkflow.getWorkflowCandidate(baseDN);
        if (workflowCandidate != null)
        {
          break;
        }
      }
    }
    return workflowCandidate;
  }
  /**
   * Registers a workflow with the network group.
   *
   * @param workflow
   *          the workflow to register
   * @throws DirectoryException
   *           If the workflow ID for the provided workflow conflicts
   *           with the workflow ID of an existing workflow.
   */
  public void registerWorkflow(WorkflowImpl workflow)
      throws DirectoryException
  {
    // The workflow is registered with no pre/post workflow element.
    registerWorkflow(workflow, null, null);
  }
  /**
   * Removes a connection from the group.
   *
   * @param connection
   *          the ClientConnection
   */
  public void removeConnection(ClientConnection connection)
  {
    if (resourceLimitsPolicy != null)
    {
      resourceLimitsPolicy.removeConnection(connection);
    }
  }
  /**
   * Updates the operations statistics.
   *
   * @param message
   *          The LDAP message being processed
   */
  public void updateMessageRead(LDAPMessage message)
  {
    statistics.updateMessageRead(message);
  }
  /**
   * Deregisters the current network group (this) with the server. The
   * method also decrements the reference counter of the workflows so
   * that workflows can be disabled or deleted if needed.
   * <p>
   * This methods is package private for testing purposes.
   */
  void deregister()
  {
    // Finalization specific to user network groups.
    synchronized (registeredNetworkGroupsLock)
    {
      // Deregister this network group.
      TreeMap<String, NetworkGroup> networkGroups =
          new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
      networkGroups.remove(networkGroupID);
      registeredNetworkGroups = networkGroups;
      orderedNetworkGroups.remove(this);
      // Decrement the reference counter of the workflows registered
      // with this network group.
      synchronized (registeredWorkflowNodesLock)
      {
        for (WorkflowTopologyNode workflowNode : registeredWorkflowNodes
            .values())
        {
          WorkflowImpl workflowImpl = workflowNode.getWorkflowImpl();
          workflowImpl.decrementReferenceCounter();
        }
      }
    }
  }
  /**
   * Returns the request filtering policy statistics associated with
   * this network group.
   *
   * @return The request filtering policy statistics associated with
   *         this network group.
   */
  RequestFilteringPolicyStatistics getRequestFilteringPolicyStatistics()
  {
    if (requestFilteringPolicy != null)
    {
      return requestFilteringPolicy.getStatistics();
    }
    else
    {
      return null;
    }
  }
  /**
   * Returns the resource limits policy statistics associated with this
   * network group.
   *
   * @return The resource limits policy statistics associated with this
   *         network group.
   */
  ResourceLimitsPolicyStatistics getResourceLimitsPolicyStatistics()
  {
    if (resourceLimitsPolicy != null)
    {
      return resourceLimitsPolicy.getStatistics();
    }
    else
    {
      return null;
    }
  }
  /**
   * Registers the current network group (this) with the server.
   * <p>
   * This methods is package private for testing purposes.
   *
   * @throws  DirectoryException  If the network group ID for the provided
   *                              network group conflicts with the network
   *                              group ID of an existing network group.
   * @throws InitializationException
   *           If the network group ID for the provided network group
   *           conflicts with the network group ID of an existing
   *           network group.
   */
  public void register()
      throws DirectoryException
  void register() throws InitializationException
  {
    ensureNotNull(networkGroupID);
@@ -205,24 +1403,28 @@
      // The network group must not be already registered
      if (registeredNetworkGroups.containsKey(networkGroupID))
      {
        Message message = ERR_REGISTER_NETWORK_GROUP_ALREADY_EXISTS.get(
                          networkGroupID);
        throw new DirectoryException(
            ResultCode.UNWILLING_TO_PERFORM, message);
        Message message =
            ERR_REGISTER_NETWORK_GROUP_ALREADY_EXISTS
                .get(networkGroupID);
        throw new InitializationException(message);
      }
      TreeMap<String, NetworkGroup> newRegisteredNetworkGroups =
        new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
          new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
      newRegisteredNetworkGroups.put(networkGroupID, this);
      registeredNetworkGroups = newRegisteredNetworkGroups;
      // Insert the network group at the right position in the ordered list
      // Insert the network group at the right position in the ordered
      // list.
      int index = 0;
      for (NetworkGroup ng : registeredNetworkGroups.values()) {
        if (ng.equals(this)) {
      for (NetworkGroup ng : registeredNetworkGroups.values())
      {
        if (ng.equals(this))
        {
          continue;
        }
        if (this.priority > ng.priority) {
        if (this.priority > ng.priority)
        {
          index++;
        }
      }
@@ -231,103 +1433,474 @@
  }
  /**
   * Deregisters the current network group (this) with the server.
   * The method also decrements the reference counter of the workflows
   * so that workflows can be disabled or deleted if needed.
   */
  public void deregister()
  {
    synchronized (registeredNetworkGroupsLock)
    {
      TreeMap<String, NetworkGroup> networkGroups =
        new TreeMap<String, NetworkGroup>(registeredNetworkGroups);
      networkGroups.remove(networkGroupID);
      registeredNetworkGroups = networkGroups;
      orderedNetworkGroups.remove(this);
      // decrement the reference counter of the workflows registered with
      // this network group
      updateWorkflowReferenceCounters();
    }
  /**
   * Sets the network group connection criteria.
   * <p>
   * This method is intended for testing only.
   *
   * @param criteria
   *          The connection criteria.
   */
  void setConnectionCriteria(ConnectionCriteria criteria)
  {
    this.criteria = criteria;
  }
  /**
   * Decrements the workflow reference counters of all the workflows
   * registered with this network group.
   * Sets the network group priority.
   * <p>
   * This methods is package private for testing purposes.
   *
   * @param prio
   *          the network group priority
   */
  private void updateWorkflowReferenceCounters()
  void setNetworkGroupPriority(int prio)
  {
    synchronized (registeredWorkflowNodesLock)
    // Check whether the priority has changed
    if (priority != prio)
    {
      for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
      synchronized (registeredNetworkGroupsLock)
      {
        WorkflowImpl workflowImpl = workflowNode.getWorkflowImpl();
        workflowImpl.decrementReferenceCounter();
        priority = prio;
        // Nothing to do if the network group is not registered
        if (registeredNetworkGroups.containsKey(networkGroupID))
        {
          // If the network group was already registered, remove it from
          // the ordered list
          orderedNetworkGroups.remove(this);
          // Then insert it at the right position in the ordered list
          int index = 0;
          for (NetworkGroup ng : registeredNetworkGroups.values())
          {
            if (ng.equals(this))
            {
              continue;
            }
            if (this.priority > ng.priority)
            {
              index++;
            }
          }
          orderedNetworkGroups.add(index, this);
        }
      }
    }
  }
  /**
   * Registers a workflow with the network group.
   * Dumps info from the current network group for debug purpose.
   * <p>
   * This method is intended for testing only.
   *
   * @param workflow  the workflow to register
   *
   * @throws  DirectoryException  If the workflow ID for the provided
   *                              workflow conflicts with the workflow
   *                              ID of an existing workflow.
   * @param leftMargin
   *          white spaces used to indent traces
   * @return a string buffer that contains trace information
   */
  public void registerWorkflow(
      WorkflowImpl workflow
      ) throws DirectoryException
  StringBuilder toString(String leftMargin)
  {
    // The workflow is registered with no pre/post workflow element.
    registerWorkflow(workflow, null, null);
    StringBuilder sb = new StringBuilder();
    String newMargin = leftMargin + "   ";
    sb.append(leftMargin + "Networkgroup (" + networkGroupID + "\n");
    sb.append(leftMargin + "List of registered workflows:\n");
    for (WorkflowTopologyNode node : registeredWorkflowNodes.values())
    {
      sb.append(node.toString(newMargin));
    }
    namingContexts.toString(leftMargin);
    sb.append(leftMargin + "rootDSEWorkflow:\n");
    if (rootDSEWorkflowNode == null)
    {
      sb.append(newMargin + "null\n");
    }
    else
    {
      sb.append(rootDSEWorkflowNode.toString(newMargin));
    }
    return sb;
  }
  /**
   * Registers a workflow with the network group and the workflow may have
   * pre and post workflow element.
   * Checks whether the base DN of a new workflow to register is present
   * in a workflow already registered with the network group.
   *
   * @param workflow              the workflow to register
   * @param preWorkflowElements   the tasks to execute before the workflow
   * @param postWorkflowElements  the tasks to execute after the workflow
   *
   * @throws  DirectoryException  If the workflow ID for the provided
   *          workflow conflicts with the workflow ID of an existing
   *          workflow or if the base DN of the workflow is the same
   *          than the base DN of another workflow already registered
   * @param workflowNode
   *          the workflow to check
   * @throws DirectoryException
   *           If the base DN of the workflow is already present in the
   *           network group
   */
  private void registerWorkflow(
      WorkflowImpl workflow,
  private void checkWorkflowBaseDN(WorkflowTopologyNode workflowNode)
      throws DirectoryException
  {
    String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
    ensureNotNull(workflowID);
    // If the network group is the "internal" network group then bypass
    // the check because the internal network group may contain
    // duplicates of base DNs.
    if (isInternalNetworkGroup)
    {
      return;
    }
    // If the network group is the "admin" network group then bypass
    // the check because the internal network group may contain
    // duplicates of base DNs.
    if (isAdminNetworkGroup)
    {
      return;
    }
    // The workflow base DN should not be already present in the
    // network group. Bypass the check for the private workflows...
    for (WorkflowTopologyNode node : registeredWorkflowNodes.values())
    {
      DN nodeBaseDN = node.getBaseDN();
      if (nodeBaseDN.equals(workflowNode.getBaseDN()))
      {
        // The base DN is already registered in the network group,
        // we must reject the registration request
        Message message =
            ERR_REGISTER_WORKFLOW_BASE_DN_ALREADY_EXISTS.get(
                workflowID, networkGroupID, node.getWorkflowImpl()
                    .getWorkflowId(), workflowNode.getWorkflowImpl()
                    .getBaseDN().toString());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            message);
      }
    }
  }
  // Creates and registers the provided network group policy
  // configuration.
  private void createNetworkGroupQOSPolicy(
      QOSPolicyCfg policyConfiguration) throws ConfigException,
      InitializationException
  {
    String className = policyConfiguration.getJavaClass();
    QOSPolicyCfgDefn d = QOSPolicyCfgDefn.getInstance();
    ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition();
    // Load the class and cast it to a network group policy.
    Class<? extends QOSPolicyFactory> theClass;
    QOSPolicyFactory factory;
    try
    {
      theClass = pd.loadClass(className, QOSPolicyFactory.class);
      factory = theClass.newInstance();
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
          ERR_CONFIG_NETWORK_GROUP_POLICY_CANNOT_INITIALIZE.get(String
              .valueOf(className), String.valueOf(policyConfiguration
              .dn()), stackTraceToSingleLineString(e));
      throw new InitializationException(message, e);
    }
    // Perform the necessary initialization for the network group
    // policy.
    QOSPolicy policy;
    try
    {
      // Determine the initialization method to use: it must take a
      // single parameter which is the exact type of the configuration
      // object.
      Method method =
          theClass.getMethod("createQOSPolicy", policyConfiguration
              .configurationClass());
      policy = (QOSPolicy) method.invoke(factory, policyConfiguration);
    }
    catch (Exception e)
    {
      if (e instanceof InvocationTargetException)
      {
        Throwable t = e.getCause();
        if (t instanceof InitializationException)
        {
          throw (InitializationException) t;
        }
        else if (t instanceof ConfigException)
        {
          throw (ConfigException) t;
        }
      }
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
          ERR_CONFIG_NETWORK_GROUP_POLICY_CANNOT_INITIALIZE.get(String
              .valueOf(className), String.valueOf(policyConfiguration
              .dn()), stackTraceToSingleLineString(e));
      throw new InitializationException(message, e);
    }
    // The network group has been successfully initialized - so register
    // it.
    QOSPolicy oldPolicy =
        policies.put(policyConfiguration.dn(), policy);
    if (policy instanceof RequestFilteringPolicy)
    {
      requestFilteringPolicy = (RequestFilteringPolicy) policy;
    }
    else if (policy instanceof ResourceLimitsPolicy)
    {
      resourceLimitsPolicy = (ResourceLimitsPolicy) policy;
    }
    if (oldPolicy != null)
    {
      oldPolicy.finalizeQOSPolicy();
    }
  }
  /**
   * Deregisters a workflow node with the network group.
   *
   * @param workflow
   *          the workflow node to deregister
   * @return <code>true</code> when the workflow has been successfully
   *         deregistered
   */
  private boolean deregisterWorkflow(Workflow workflow)
  {
    // true as soon as the workflow has been deregistered
    boolean deregistered = false;
    // Is it the rootDSE workflow?
    if (workflow == rootDSEWorkflowNode)
    {
      rootDSEWorkflowNode = null;
      deregistered = true;
    }
    else
    {
      // Deregister the workflow with the network group.
      WorkflowTopologyNode workflowNode =
          (WorkflowTopologyNode) workflow;
      deregisterWorkflowNode(workflowNode);
      deregistered = true;
      // The workflow to deregister is not the root DSE workflow.
      // Remove it from the workflow topology.
      workflowNode.remove();
      // Rebuild the list of naming context handled by the network group
      rebuildNamingContextList();
    }
    return deregistered;
  }
  /**
   * Deregisters the current workflow (this) with the server.
   *
   * @param workflowNode
   *          the workflow node to deregister
   */
  private void deregisterWorkflowNode(WorkflowTopologyNode workflowNode)
  {
    synchronized (registeredWorkflowNodesLock)
    {
      TreeMap<String, WorkflowTopologyNode> newWorkflowNodes =
          new TreeMap<String, WorkflowTopologyNode>(
              registeredWorkflowNodes);
      newWorkflowNodes.remove(workflowNode.getWorkflowImpl()
          .getWorkflowId());
      registeredWorkflowNodes = newWorkflowNodes;
    }
  }
  /**
   * Retrieves the list of registered workflows.
   *
   * @return a list of workflow ids
   */
  private List<String> getRegisteredWorkflows()
  {
    List<String> workflowIDs = new ArrayList<String>();
    synchronized (registeredWorkflowNodesLock)
    {
      for (WorkflowTopologyNode node : registeredWorkflowNodes.values())
      {
        workflowIDs.add(node.getWorkflowImpl().getWorkflowId());
      }
    }
    return workflowIDs;
  }
  /**
   * We've seen parts of the server hold references to a NetworkGroup
   * during an in-core server restart. To help detect when this happens,
   * we null out the member variables, so we will fail fast with an NPE
   * if an invalidate NetworkGroup is used.
   */
  private void invalidate()
  {
    namingContexts = null;
    rootDSEWorkflowNode = null;
    registeredWorkflowNodes = null;
  }
  /**
   * Checks whether the connection matches the network group criteria.
   *
   * @param connection
   *          the client connection
   * @return a boolean indicating the match
   */
  private boolean match(ClientConnection connection)
  {
    if (criteria != null)
    {
      return criteria.matches(connection);
    }
    else
    {
      return true;
    }
  }
  /**
   * Checks whether the client connection matches the criteria after
   * bind.
   *
   * @param connection
   *          the ClientConnection
   * @param bindDN
   *          the DN used to bind
   * @param authType
   *          the authentication type
   * @param isSecure
   *          a boolean indicating whether the connection is secure
   * @return a boolean indicating whether the connection matches the
   *         criteria
   */
  private boolean matchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    if (criteria != null)
    {
      return criteria.willMatchAfterBind(connection, bindDN, authType,
          isSecure);
    }
    else
    {
      return true;
    }
  }
  /**
   * Rebuilds the list of naming contexts handled by the network group.
   * This operation should be performed whenever a workflow topology has
   * been updated (workflow registration or de-registration).
   */
  private void rebuildNamingContextList()
  {
    // reset lists of naming contexts
    namingContexts.resetLists();
    // a registered workflow with no parent is a naming context
    for (WorkflowTopologyNode workflowNode : registeredWorkflowNodes
        .values())
    {
      WorkflowTopologyNode parent = workflowNode.getParent();
      if (parent == null)
      {
        namingContexts.addNamingContext(workflowNode);
      }
    }
  }
  /**
   * Registers a workflow with the network group and the workflow may
   * have pre and post workflow element.
   *
   * @param workflow
   *          the workflow to register
   * @param preWorkflowElements
   *          the tasks to execute before the workflow
   * @param postWorkflowElements
   *          the tasks to execute after the workflow
   * @throws DirectoryException
   *           If the workflow ID for the provided workflow conflicts
   *           with the workflow ID of an existing workflow or if the
   *           base DN of the workflow is the same than the base DN of
   *           another workflow already registered
   */
  private void registerWorkflow(WorkflowImpl workflow,
      WorkflowElement<?>[] preWorkflowElements,
      WorkflowElement<?>[] postWorkflowElements
      ) throws DirectoryException
      WorkflowElement<?>[] postWorkflowElements)
      throws DirectoryException
  {
    // Is it the rootDSE workflow?
    DN baseDN = workflow.getBaseDN();
    if (baseDN.isNullDN())
    {
      // NOTE - The rootDSE workflow is stored with the registeredWorkflows.
      // NOTE - The rootDSE workflow is stored with the
      // registeredWorkflows.
      rootDSEWorkflowNode =
        new RootDseWorkflowTopology(workflow, namingContexts);
          new RootDseWorkflowTopology(workflow, namingContexts);
    }
    else
    {
      // This workflow is not the rootDSE workflow. Try to insert it in the
      // workflow topology.
      WorkflowTopologyNode workflowNode = new WorkflowTopologyNode(
          workflow, preWorkflowElements, postWorkflowElements);
      // This workflow is not the rootDSE workflow. Try to insert it in
      // the workflow topology.
      WorkflowTopologyNode workflowNode =
          new WorkflowTopologyNode(workflow, preWorkflowElements,
              postWorkflowElements);
      // Register the workflow node with the network group. If the workflow
      // ID is already existing then an exception is raised.
      // Register the workflow node with the network group. If the
      // workflow ID is already existing then an exception is raised.
      registerWorkflowNode(workflowNode);
      // Now add the workflow in the workflow topology...
      for (WorkflowTopologyNode curNode: registeredWorkflowNodes.values())
      for (WorkflowTopologyNode curNode : registeredWorkflowNodes
          .values())
      {
        // Try to insert the new workflow under an existing workflow...
        if (curNode.insertSubordinate(workflowNode))
@@ -350,10 +1923,9 @@
      // Now that the workflow node has been registered with the network
      // group, update the reference counter of the workflow, unless
      // the network group is either default, or administration, or internal
      // network group.
      if (!isAdminNetworkGroup
          && !isInternalNetworkGroup
      // the network group is either default, or administration, or
      // internal network group.
      if (!isAdminNetworkGroup && !isInternalNetworkGroup
          && !isDefaultNetworkGroup)
      {
        workflow.incrementReferenceCounter();
@@ -362,196 +1934,19 @@
  }
  /**
   * Deregisters a workflow with the network group. The workflow to
   * deregister is identified by its baseDN.
   *
   * @param baseDN  the baseDN of the workflow to deregister, may be null
   *
   * @return the deregistered workflow
   */
  public Workflow deregisterWorkflow(
      DN baseDN
      )
  {
    Workflow workflow = null;
    if (baseDN == null)
    {
      return workflow;
    }
    if (baseDN.isNullDN())
    {
      // deregister the rootDSE
      deregisterWorkflow(rootDSEWorkflowNode);
      workflow = rootDSEWorkflowNode.getWorkflowImpl();
    }
    else
    {
      // deregister a workflow node
      synchronized (registeredWorkflowNodesLock)
      {
        for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
        {
          DN curDN = node.getBaseDN();
          if (curDN.equals(baseDN))
          {
            // Call deregisterWorkflow() instead of deregisterWorkflowNode()
            // because we want the naming context list to be updated as well.
            deregisterWorkflow(node);
            workflow = node.getWorkflowImpl();
            // Only one workflow can match the baseDN, so we can break
            // the loop here.
            break;
          }
        }
      }
    }
    // Now that the workflow node has been deregistered with the network
    // group, update the reference counter of the workflow.
    if ((workflow != null)
        && !isAdminNetworkGroup
        && !isInternalNetworkGroup
        && !isDefaultNetworkGroup)
    {
      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
      workflowImpl.decrementReferenceCounter();
    }
    return workflow;
  }
  /**
   * Deregisters a workflow with the network group. The workflow to
   * deregister is identified by its workflow ID.
   *
   * @param workflowID the workflow identifier of the workflow to deregister
   * @return the deregistered workflow
   */
  public Workflow deregisterWorkflow(
      String workflowID
      )
  {
    Workflow workflow = null;
    String rootDSEWorkflowID = null;
    if (rootDSEWorkflowNode != null)
    {
      rootDSEWorkflowID = rootDSEWorkflowNode.getWorkflowImpl().getWorkflowId();
    }
    if (workflowID.equalsIgnoreCase(rootDSEWorkflowID))
    {
      // deregister the rootDSE
      deregisterWorkflow(rootDSEWorkflowNode);
      workflow = rootDSEWorkflowNode.getWorkflowImpl();
    }
    else
    {
      // deregister a workflow node
      synchronized (registeredWorkflowNodesLock)
      {
        for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
        {
          String curID = node.getWorkflowImpl().getWorkflowId();
          if (curID.equals(workflowID))
          {
            // Call deregisterWorkflow() instead of deregisterWorkflowNode()
            // because we want the naming context list to be updated as well.
            deregisterWorkflow(node);
            workflow = node.getWorkflowImpl();
            // Only one workflow can match the baseDN, so we can break
            // the loop here.
            break;
          }
        }
      }
    }
    // Now that the workflow node has been deregistered with the network
    // group, update the reference counter of the workflow.
    if ((workflow != null)
        && !isAdminNetworkGroup
        && !isInternalNetworkGroup
        && !isDefaultNetworkGroup)
    {
      WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
      workflowImpl.decrementReferenceCounter();
    }
    return workflow;
  }
  /**
   * Deregisters a workflow node with the network group.
   *
   * @param workflow  the workflow node to deregister
   * @return <code>true</code> when the workflow has been successfully
   *         deregistered
   */
  private boolean deregisterWorkflow(Workflow workflow)
  {
    // true as soon as the workflow has been deregistered
    boolean deregistered = false;
    // Is it the rootDSE workflow?
    if (workflow == rootDSEWorkflowNode)
    {
      rootDSEWorkflowNode = null;
      deregistered = true;
    }
    else
    {
      // Deregister the workflow with the network group.
      WorkflowTopologyNode workflowNode = (WorkflowTopologyNode) workflow;
      deregisterWorkflowNode(workflowNode);
      deregistered = true;
      // The workflow to deregister is not the root DSE workflow.
      // Remove it from the workflow topology.
      workflowNode.remove();
      // Rebuild the list of naming context handled by the network group
      rebuildNamingContextList();
    }
    return deregistered;
  }
  /**
   * Retrieves the list of registered workflows.
   * @return a list of workflow ids
   */
  public List<String> getRegisteredWorkflows() {
    List<String> workflowIDs = new ArrayList<String>();
    synchronized (registeredWorkflowNodesLock) {
      for (WorkflowTopologyNode node : registeredWorkflowNodes.values()) {
        workflowIDs.add(node.getWorkflowImpl().getWorkflowId());
      }
    }
    return workflowIDs;
  }
  /**
   * Registers a workflow node with the network group.
   *
   * @param workflowNode  the workflow node to register
   *
   * @throws  DirectoryException  If the workflow node ID for the provided
   *                              workflow node conflicts with the workflow
   *                              node ID of an existing workflow node.
   * @param workflowNode
   *          the workflow node to register
   * @throws DirectoryException
   *           If the workflow node ID for the provided workflow node
   *           conflicts with the workflow node ID of an existing
   *           workflow node.
   */
  private void registerWorkflowNode(
      WorkflowTopologyNode workflowNode
      ) throws DirectoryException
  private void registerWorkflowNode(WorkflowTopologyNode workflowNode)
      throws DirectoryException
  {
    String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
    ensureNotNull(workflowID);
@@ -561,10 +1956,11 @@
      // The workflow must not be already registered
      if (registeredWorkflowNodes.containsKey(workflowID))
      {
        Message message = ERR_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS.get(
          workflowID, networkGroupID);
        throw new DirectoryException(
            ResultCode.UNWILLING_TO_PERFORM, message);
        Message message =
            ERR_REGISTER_WORKFLOW_NODE_ALREADY_EXISTS.get(workflowID,
                networkGroupID);
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
            message);
      }
      // The workflow base DN should not be already present in the
@@ -573,685 +1969,26 @@
      // All is fine, let's register the workflow
      TreeMap<String, WorkflowTopologyNode> newRegisteredWorkflowNodes =
        new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
          new TreeMap<String, WorkflowTopologyNode>(
              registeredWorkflowNodes);
      newRegisteredWorkflowNodes.put(workflowID, workflowNode);
      registeredWorkflowNodes = newRegisteredWorkflowNodes;
    }
  }
  /**
   * Checks whether the base DN of a new workflow to register is
   * present in a workflow already registered with the network group.
   *
   * @param workflowNode  the workflow to check
   *
   * @throws  DirectoryException  If the base DN of the workflow is already
   *                              present in the network group
   */
  private void checkWorkflowBaseDN(
      WorkflowTopologyNode workflowNode
      ) throws DirectoryException
  {
    String workflowID = workflowNode.getWorkflowImpl().getWorkflowId();
    ensureNotNull(workflowID);
    // If the network group is the "internal" network group then bypass
    // the check because the internal network group may contain duplicates
    // of base DNs.
    if (isInternalNetworkGroup)
    {
      return;
    }
    // If the network group is the "admin" network group then bypass
    // the check because the internal network group may contain duplicates
    // of base DNs.
    if (isAdminNetworkGroup)
    {
      return;
    }
    // The workflow base DN should not be already present in the
    // network group. Bypass the check for the private workflows...
    for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
    {
      DN nodeBaseDN = node.getBaseDN();
      if (nodeBaseDN.equals(workflowNode.getBaseDN()))
      {
        // The base DN is already registered in the network group,
        // we must reject the registration request
        Message message = ERR_REGISTER_WORKFLOW_BASE_DN_ALREADY_EXISTS.get(
          workflowID,
          networkGroupID,
          node.getWorkflowImpl().getWorkflowId(),
          workflowNode.getWorkflowImpl().getBaseDN().toString());
        throw new DirectoryException(
            ResultCode.UNWILLING_TO_PERFORM, message);
      }
    }
  }
  /**
   * Deregisters the current workflow (this) with the server.
   *
   * @param workflowNode  the workflow node to deregister
   */
  private void deregisterWorkflowNode(
      WorkflowTopologyNode workflowNode
      )
  {
    synchronized (registeredWorkflowNodesLock)
    {
      TreeMap<String, WorkflowTopologyNode> newWorkflowNodes =
        new TreeMap<String, WorkflowTopologyNode>(registeredWorkflowNodes);
      newWorkflowNodes.remove(workflowNode.getWorkflowImpl().getWorkflowId());
      registeredWorkflowNodes = newWorkflowNodes;
    }
  }
  /**
   * Adds a connection to the group.
   *
   * @param connection the ClientConnection
   */
  public void addConnection(ClientConnection connection) {
    if (resourceLimits != null) {
      resourceLimits.addConnection(connection);
    }
  }
  /**
   * Removes a connection from the group.
   *
   * @param connection the ClientConnection
   */
  public void removeConnection(ClientConnection connection) {
    if (resourceLimits != null) {
      resourceLimits.removeConnection(connection);
    }
  }
  /**
   *
   * Sets the network group priority.
   *
   * @param prio the network group priority
   */
  public void setNetworkGroupPriority(int prio) {
    // Check whether the priority has changed
    if (priority != prio) {
      synchronized (registeredNetworkGroupsLock)
      {
        priority = prio;
        // Nothing to do if the network group is not registered
        if (registeredNetworkGroups.containsKey(networkGroupID)) {
          // If the network group was already registered, remove it from the
          // ordered list
          orderedNetworkGroups.remove(this);
          // Then insert it at the right position in the ordered list
          int index = 0;
          for (NetworkGroup ng : registeredNetworkGroups.values()) {
            if (ng.equals(this)) {
              continue;
            }
            if (this.priority > ng.priority) {
              index++;
            }
          }
          orderedNetworkGroups.add(index, this);
        }
      }
    }
  }
  /**
   *
   * Sets the network group criteria.
   *
   * @param ngCriteria the criteria
   */
  public void setCriteria(NetworkGroupCriteria ngCriteria) {
    criteria = ngCriteria;
  }
  /**
   * Sets the Resource Limits.
   *
   * @param limits the new resource limits
   */
  public void setResourceLimits(ResourceLimits limits) {
    resourceLimits = limits;
  }
  /**
   * Sets the Request Filtering Policy.
   *
   * @param policy the new request filtering policy
   */
  public void setRequestFilteringPolicy(RequestFilteringPolicy policy) {
    requestFilteringPolicy = policy;
  }
  /**
   * Sets the affinity policy. The client connection affinity is the ability
   * for the server to bypass a route algorithm like "load balancing" so
   * that a request is always sent to the same data source regardless the
   * route algorithm.
   *
   * @param  affinityPolicy
   *         The client connection affinity policy of the network group.
   */
  public void setAffinityPolicy(
      ClientConnectionAffinityPolicy affinityPolicy)
  {
    this.affinityPolicy = affinityPolicy;
  }
  /**
   * Sets the affinity timeout value. The client connection affinity, when
   * set, remains active until the time out expires. When the time out
   * value is set to 0 then an active affinity never expires.
   *
   * @param timeout
   *        The affinity timeout value (0 means never expire).
   */
  public void setAffinityTimeout(long timeout)
  {
    this.affinityTimeout = timeout;
  }
  /**
   * Gets the highest priority matching network group.
   *
   * @param connection the client connection
   * @return matching network group
   */
  public static NetworkGroup findMatchingNetworkGroup(
          ClientConnection connection) {
    for (NetworkGroup ng : getOrderedNetworkGroups()) {
      if (ng.match(connection)) {
        return ng;
      }
    }
    return defaultNetworkGroup;
  }
  /**
   * Gets the highest priority matching network group for a BIND op.
   *
   * @param connection the client connection
   * @param dn the operation bindDN
   * @param authType the operation authentication type
   * @param isSecure a boolean indicating whether the operation is secured
   * @return matching network group
   */
  public static NetworkGroup findBindMatchingNetworkGroup(
          ClientConnection connection, DN dn, AuthenticationType authType,
          boolean isSecure) {
    for (NetworkGroup ng:getOrderedNetworkGroups()) {
      if (ng.matchAfterBind(connection, dn, authType, isSecure)) {
        return ng;
      }
    }
    return defaultNetworkGroup;
  }
  /**
   * Checks whether the connection matches the network group criteria.
   *
   * @param connection  the client connection
   * @return a boolean indicating the match
   */
  private boolean match(ClientConnection connection) {
    if (criteria != null) {
      return (criteria.match(connection));
    }
    return (true);
  }
  /**
   * Checks whether the client connection matches the criteria after bind.
   *
   * @param connection the ClientConnection
   * @param bindDN the DN used to bind
   * @param authType the authentication type
   * @param isSecure a boolean indicating whether the connection is secure
   * @return a boolean indicating whether the connection matches the criteria
   */
  private boolean matchAfterBind(ClientConnection connection, DN bindDN,
          AuthenticationType authType, boolean isSecure) {
    if (criteria != null) {
      return (criteria.matchAfterBind(connection, bindDN, authType, isSecure));
    }
    return (true);
  }
  /**
   * Checks the resource limits.
   *
   * @param connection the client connection
   * @param operation the ongoing operation
   * @param fullCheck a boolean indicating the level of checking: full/partial
   * @param messages the messages indicating the cause of the failure.
   * @return a boolean indicating whether resource limits are exceeded
   */
  public boolean checkResourceLimits(
          ClientConnection connection,
          PreParseOperation operation,
          boolean fullCheck,
          List<Message> messages)
  {
    if (resourceLimits != null) {
      return (resourceLimits.checkLimits(connection, operation,
              fullCheck, messages));
    }
    return (true);
  }
  /**
   * Gets the search size limit, i.e. the maximum number of entries returned
   * by a search.
   * @return the maximum number of entries returned by a search
   */
  public int getSearchSizeLimit() {
    if (resourceLimits != null) {
      return resourceLimits.getSizeLimit();
    }
    return -1;
  }
  /**
   * Gets the search duration limit, i.e. the maximum duration of a search
   * operation.
   * @return the maximum duration in ms of a search operation
   */
  public int getSearchDurationLimit() {
    if (resourceLimits != null) {
      return resourceLimits.getTimeLimit();
    }
    return -1;
  }
  /**
   * Gets the minimum string length of a substring filter in a search
   * operation.
   * @return the minimum substring length
   */
  public int getMinSubstring() {
    if (resourceLimits != null) {
      return resourceLimits.getMinSubstring();
    }
    return 0;
  }
  /**
   * Gets the referral policy. The referral policy defines the behavior
   * when a referral or a search continuation reference is received.
   * The referral can either be discarded (ie an error is returned to the
   * client), forwarded (ie the result is passed as-is to the client) or
   * followed (ie the server contacts the server targeted by the referral to
   * pursue the request).
   * @return the referral policy for this network group
   */
  public ReferralPolicy getReferralPolicy() {
    if (resourceLimits != null) {
      return resourceLimits.getReferralPolicy();
    }
    return ReferralPolicy.FORWARD;
  }
  /**
   * Gets the referral bind policy. The referral bind policy defines
   * the bind credentials used when the server tries to follow a referral. It
   * can either bind to the referred server anonymously, or using the same
   * credentials as in the original request.
   * @return the referral binf policy
   */
  public ReferralBindPolicy getReferralBindPolicy() {
    if (resourceLimits != null) {
      return resourceLimits.getReferralBindPolicy();
    }
    return ReferralBindPolicy.ANONYMOUS;
  }
  /**
   * Gets the referral hop limit. When configured to follow referrals,
   * the request to the referred server can also contain a referral. The hop
   * limit is the maximum number of subsequent operations.
   * @return the referral hop limit
   */
  public int getReferralHopLimit() {
    if (resourceLimits != null) {
      return resourceLimits.getReferralHopLimit();
    }
    return 0;
  }
  /**
   * Gets the affinity policy. The client connection affinity is the ability
   * for the server to bypass a route algorithm like "load balancing" so
   * that a request is always sent to the same data source regardless the
   * route algorithm.
   *
   * @return the client connection affinity policy of the network group
   */
  public ClientConnectionAffinityPolicy getAffinityPolicy()
  {
    return this.affinityPolicy;
  }
  /**
   * Gets the affinity timeout value. The client connection affinity, when
   * set, is active for a period of time. Once that period of time has
   * expired, the client connection affinity is reset. A value of 0 means
   * "no limit" - when an affinity is set it remains active for ever.
   *
   * @return the affinity timeout value (0 means no limit).
   */
  public long getAffinityTimeout()
  {
    return this.affinityTimeout;
  }
  /**
   * Checks the request filtering policy.
   * @param operation the operation to be checked
   * @param messages the error messages
   * @return boolean indicating whether the operation conforms to the
   *         network group request filtering policy
   */
  public boolean checkRequestFilteringPolicy(
          PreParseOperation operation,
          List<Message> messages) {
    if (requestFilteringPolicy != null) {
      return requestFilteringPolicy.checkPolicy(operation, messages);
    }
    return true;
  }
  /**
   * Gets the highest workflow in the topology that can handle the baseDN.
   *
   * @param baseDN  the base DN of the request
   * @return the highest workflow in the topology that can handle the base DN,
   *         <code>null</code> if none was found
   */
  public Workflow getWorkflowCandidate(
      DN baseDN
      )
  {
    // the top workflow to return
    Workflow workflowCandidate = null;
    // get the list of workflow candidates
    if (baseDN.isNullDN())
    {
      // The rootDSE workflow is the candidate.
      workflowCandidate = rootDSEWorkflowNode;
    }
    else
    {
      // Search the highest workflow in the topology that can handle
      // the baseDN.
      for (WorkflowTopologyNode curWorkflow: namingContexts.getNamingContexts())
      {
        workflowCandidate = curWorkflow.getWorkflowCandidate (baseDN);
        if (workflowCandidate != null)
        {
          break;
        }
      }
    }
    return workflowCandidate;
  }
  /**
   * Returns the default network group. The default network group is always
   * defined and has no criterion, no policy and provide full access to
   * all the registered workflows.
   *
   * @return the default network group
   */
  public static NetworkGroup getDefaultNetworkGroup()
  {
    return defaultNetworkGroup;
  }
  /**
   * Returns the admin network group.
   * @return the admin network group
   */
  public static NetworkGroup getAdminNetworkGroup()
  {
    return adminNetworkGroup;
  }
  /**
   * Returns the internal network group.
   * @return the internal network group
   */
  public static NetworkGroup getInternalNetworkGroup()
  {
    return internalNetworkGroup;
  }
  /**
   * Rebuilds the list of naming contexts handled by the network group.
   * This operation should be performed whenever a workflow topology
   * has been updated (workflow registration or de-registration).
   */
  private void rebuildNamingContextList()
  {
    // reset lists of naming contexts
    namingContexts.resetLists();
    // a registered workflow with no parent is a naming context
    for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
    {
      WorkflowTopologyNode parent = workflowNode.getParent();
      if (parent == null)
      {
        namingContexts.addNamingContext (workflowNode);
      }
    }
  }
  /**
   * Returns the list of naming contexts handled by the network group.
   *
   * @return the list of naming contexts
   */
  public NetworkGroupNamingContexts getNamingContexts()
  {
    return namingContexts;
  }
  /**
   * Dumps info from the current network group for debug purpose.
   *
   * @param  leftMargin  white spaces used to indent traces
   * @return a string buffer that contains trace information
   */
  public StringBuilder toString(String leftMargin)
  {
    StringBuilder sb = new StringBuilder();
    String newMargin = leftMargin + "   ";
    sb.append (leftMargin + "Networkgroup (" + networkGroupID+ "\n");
    sb.append (leftMargin + "List of registered workflows:\n");
    for (WorkflowTopologyNode node: registeredWorkflowNodes.values())
    {
      sb.append (node.toString (newMargin));
    }
    namingContexts.toString (leftMargin);
    sb.append (leftMargin + "rootDSEWorkflow:\n");
    if (rootDSEWorkflowNode == null)
    {
      sb.append (newMargin + "null\n");
    }
    else
    {
      sb.append (rootDSEWorkflowNode.toString (newMargin));
    }
    return sb;
  }
  /**
   * Deregisters all network groups that have been registered.  This should be
   * called when the server is shutting down.
   */
  public static void deregisterAllOnShutdown()
  {
    synchronized (registeredNetworkGroupsLock)
    {
      // Invalidate all NetworkGroups so they cannot accidentally be used
      // after a restart.
      Collection<NetworkGroup> networkGroups = registeredNetworkGroups.values();
      for (NetworkGroup networkGroup: networkGroups)
      {
        networkGroup.invalidate();
      }
      defaultNetworkGroup.invalidate();
      adminNetworkGroup.invalidate();
      internalNetworkGroup.invalidate();
      registeredNetworkGroups = new TreeMap<String,NetworkGroup>();
      orderedNetworkGroups = new ArrayList<NetworkGroup>();
      defaultNetworkGroup = new NetworkGroup ("default");
      adminNetworkGroup = new NetworkGroup ("admin");
      internalNetworkGroup = new NetworkGroup("internal");
    }
  }
  /**
   * We've seen parts of the server hold references to a NetworkGroup
   * during an in-core server restart.  To help detect when this happens,
   * we null out the member variables, so we will fail fast with an NPE if an
   * invalidate NetworkGroup is used.
   */
  private void invalidate()
  {
    namingContexts = null;
    networkGroupID = null;
    rootDSEWorkflowNode = null;
    registeredWorkflowNodes = null;
  }
  /**
   * Provides the list of network group registered with the server.
   *
   * @return the list of registered network groups
   */
  public static Collection<NetworkGroup> getRegisteredNetworkGroups()
  {
    return registeredNetworkGroups.values();
  }
  /**
   * Provides the ordered list of registered Network groups.
   *
   * @return the ordered list of registered network groups
   */
  private static List<NetworkGroup> getOrderedNetworkGroups()
  {
    return orderedNetworkGroups;
  }
  /**
   * Returns a specific NetworkGroup.
   *
   * @param networkGroupId  the identifier of the requested network group
   * @return the requested NetworkGroup
   */
  public static NetworkGroup getNetworkGroup(String networkGroupId)
  {
    return registeredNetworkGroups.get(networkGroupId);
  }
  /**
   * Resets the configuration of all the registered network groups.
   */
  public static void resetConfig()
  {
    // Reset the default network group
    defaultNetworkGroup.reset();
    adminNetworkGroup.reset();
    internalNetworkGroup.reset();
    // Reset all the registered network group
    synchronized (registeredNetworkGroupsLock)
    {
      registeredNetworkGroups = new TreeMap<String, NetworkGroup>();
      orderedNetworkGroups = new ArrayList<NetworkGroup>();
    }
  }
  /**
   * Resets the configuration of the current network group.
   */
  public void reset()
  private void reset()
  {
    synchronized (registeredWorkflowNodesLock)
    {
      registeredWorkflowNodes = new TreeMap<String, WorkflowTopologyNode>();
      registeredWorkflowNodes =
          new TreeMap<String, WorkflowTopologyNode>();
      rootDSEWorkflowNode = null;
      namingContexts = new NetworkGroupNamingContexts();
    }
  }
  /**
   * Retrieves the statistics associated to the request filtering policy.
   *
   * @return the statistics associated to the request filtering policy
   */
  public RequestFilteringPolicyStat getRequestFilteringPolicyStat() {
    if (requestFilteringPolicy != null) {
      return requestFilteringPolicy.getStat();
    }
    return null;
  }
  /**
   * Retrieves the statistics associated to the resource limits.
   *
   * @return the statistics associated to the resource limits
   */
  public ResourceLimitsStat getResourceLimitStat() {
    if (resourceLimits != null) {
      return resourceLimits.getStat();
    }
    return null;
  }
  /**
   * Updates the operations statistics.
   * @param message The LDAP message being processed
   */
  public void updateMessageRead(LDAPMessage message) {
    stats.updateMessageRead(message);
  }
}
opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
@@ -28,14 +28,11 @@
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.loggers.ErrorLogger.logError;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap;
import org.opends.messages.Message;
@@ -44,35 +41,40 @@
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.server.ServerManagementContext;
import org.opends.server.admin.std.server.NetworkGroupCfg;
import org.opends.server.admin.std.server.NetworkGroupCriteriaCfg;
import
  org.opends.server.admin.std.server.NetworkGroupRequestFilteringPolicyCfg;
import org.opends.server.admin.std.server.NetworkGroupResourceLimitsCfg;
import org.opends.server.admin.std.server.RootCfg;
import org.opends.server.config.ConfigException;
import org.opends.server.core.*;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
/**
 * This class defines a utility that will be used to manage the configuration
 * for the set of network groups defined in the Directory Server.
 * It will perform the necessary initialization of those network groups when
 * the server is first started, and then will manage any changes to them while
 * the server is running.
 * This class defines a utility that will be used to manage the
 * configuration for the set of network groups defined in the Directory
 * Server. It will perform the necessary initialization of those network
 * groups when the server is first started, and then will manage any
 * changes to them while the server is running.
 */
public class NetworkGroupConfigManager
       implements ConfigurationChangeListener<NetworkGroupCfg>,
                  ConfigurationAddListener<NetworkGroupCfg>,
                  ConfigurationDeleteListener<NetworkGroupCfg>
public class NetworkGroupConfigManager implements
    ConfigurationChangeListener<NetworkGroupCfg>,
    ConfigurationAddListener<NetworkGroupCfg>,
    ConfigurationDeleteListener<NetworkGroupCfg>
{
  /**
   * The tracer object for the debug logger.
   */
  private static final DebugTracer TRACER = getTracer();
  // A mapping between the DNs of the config entries and the associated
  // network groups.
  private ConcurrentHashMap<DN, NetworkGroup> networkGroups;
  private final ConcurrentHashMap<DN, NetworkGroup> networkGroups;
@@ -87,175 +89,52 @@
  /**
   * Initializes all network groups currently defined in the Directory
   * Server configuration.  This should only be called at Directory Server
   * startup.
   *
   * @throws  ConfigException  If a configuration problem causes the network
   *                           group initialization process to fail.
   */
  public void initializeNetworkGroups()
      throws ConfigException
  {
    // Get the root configuration object.
    ServerManagementContext managementContext =
         ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
         managementContext.getRootConfiguration();
    // Register as an add and delete listener with the root configuration so we
    // can be notified if any network group entries are added or removed.
    rootConfiguration.addNetworkGroupAddListener(this);
    rootConfiguration.addNetworkGroupDeleteListener(this);
    //Initialize the existing network groups.
    for (String networkGroupName : rootConfiguration.listNetworkGroups())
    {
      NetworkGroupCfg networkGroupConfiguration =
           rootConfiguration.getNetworkGroup(networkGroupName);
      networkGroupConfiguration.addChangeListener(this);
      if (networkGroupConfiguration.isEnabled())
      {
        try
        {
          createAndRegisterNetworkGroup(networkGroupConfiguration);
        }
        catch (DirectoryException de)
        {
          throw new ConfigException(de.getMessageObject());
        }
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
      NetworkGroupCfg configuration,
      List<Message>   unacceptableReasons)
  {
    // Nothing to check.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
      NetworkGroupCfg configuration)
  {
    ResultCode    resultCode          = ResultCode.SUCCESS;
    boolean       adminActionRequired = false;
    List<Message> messages            = new ArrayList<Message>();
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    List<Message> messages = new ArrayList<Message>();
    // Register to be notified of changes to the new network group.
    configuration.addChangeListener(this);
    // If the new network group is enabled then create it and register it.
    // If the new network group is enabled then create it and register
    // it.
    if (configuration.isEnabled())
    {
      try
      {
        createAndRegisterNetworkGroup(configuration);
        NetworkGroup networkGroup =
            NetworkGroup.createUserNetworkGroup(configuration);
        networkGroups.put(configuration.dn(), networkGroup);
      }
      catch (DirectoryException de)
      catch (InitializationException e)
      {
        if (resultCode == ResultCode.SUCCESS)
        if (debugEnabled())
        {
          resultCode = DirectoryServer.getServerErrorResultCode();
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(de.getMessageObject());
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
      NetworkGroupCfg configuration,
      List<Message>   unacceptableReasons)
  {
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(
      NetworkGroupCfg configuration)
  {
    ResultCode    resultCode          = ResultCode.SUCCESS;
    boolean       adminActionRequired = false;
    List<Message> messages            = new ArrayList<Message>();
    NetworkGroup networkGroup = networkGroups.remove(configuration.dn());
    if (networkGroup != null)
    {
      networkGroup.deregister();
      networkGroup.finalizeNetworkGroup();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      NetworkGroupCfg configuration,
      List<Message>   unacceptableReasons)
  {
    // If the network group is disabled then there is nothing to check.
    if (! configuration.isEnabled())
    {
      return true;
    }
    // Check that all the workflows in the network group have a
    // different base DN.
    boolean result = true;
    Set<String> allBaseDNs = new HashSet<String>();
    for (String workflowId : configuration.getWorkflow())
    {
      WorkflowImpl workflow =
        (WorkflowImpl) WorkflowImpl.getWorkflow(workflowId);
      String baseDN = workflow.getBaseDN().toNormalizedString();
      if (allBaseDNs.contains(baseDN))
      catch (ConfigException e)
      {
        // This baseDN is duplicated
        Message message = ERR_WORKFLOW_BASE_DN_DUPLICATED_IN_NG.get(
          baseDN, configuration.getNetworkGroupId());
        unacceptableReasons.add(message);
        result = false;
        break;
      }
      else
      {
        allBaseDNs.add(baseDN);
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    }
    return result;
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
@@ -266,215 +145,168 @@
  public ConfigChangeResult applyConfigurationChange(
      NetworkGroupCfg configuration)
  {
    ResultCode    resultCode          = ResultCode.SUCCESS;
    boolean       adminActionRequired = false;
    List<Message> messages            = new ArrayList<Message>();
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    List<Message> messages = new ArrayList<Message>();
    ConfigChangeResult configChangeResult =
      new ConfigChangeResult(resultCode, adminActionRequired, messages);
    // Enable / disable the network group as required.
    NetworkGroup networkGroup = networkGroups.get(configuration.dn());
    // Get the existing network group if it's already enabled.
    NetworkGroup existingNetworkGroup = networkGroups.get(configuration.dn());
    // If the new configuration has the network group disabled, then disable
    // it if it is enabled, or do nothing if it's already disabled.
    if (! configuration.isEnabled())
    if (networkGroup != null && !configuration.isEnabled())
    {
      if (existingNetworkGroup != null)
      {
        networkGroups.remove(configuration.dn());
        existingNetworkGroup.deregister();
        existingNetworkGroup.finalizeNetworkGroup();
      }
      return configChangeResult;
      // The network group has been disabled.
      networkGroups.remove(configuration.dn());
      networkGroup.finalizeNetworkGroup();
    }
    // If the network group is disabled then create it and register it.
    if (existingNetworkGroup == null)
    else if (networkGroup == null && configuration.isEnabled())
    {
      // The network group has been enabled.
      try
      {
        createAndRegisterNetworkGroup(configuration);
        networkGroup =
            NetworkGroup.createUserNetworkGroup(configuration);
        networkGroups.put(configuration.dn(), networkGroup);
      }
      catch (DirectoryException de)
      catch (InitializationException e)
      {
        if (resultCode == ResultCode.SUCCESS)
        if (debugEnabled())
        {
          resultCode = DirectoryServer.getServerErrorResultCode();
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(de.getMessageObject());
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
    } else {
      // The network group is already defined
      // Simply update the properties
      existingNetworkGroup.setNetworkGroupPriority(configuration.getPriority());
      // Check for workflows currently registered in the network group
      // but that must be removed
      SortedSet<String> configWorkflows = configuration.getWorkflow();
      for (String id : existingNetworkGroup.getRegisteredWorkflows()) {
        if (!configWorkflows.contains(id)) {
          existingNetworkGroup.deregisterWorkflow(id);
      catch (ConfigException e)
      {
        if (debugEnabled())
        {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        messages.add(e.getMessageObject());
        resultCode = DirectoryServer.getServerErrorResultCode();
      }
      // Check for newly defined workflows
      List<String> ngWorkflows = existingNetworkGroup.getRegisteredWorkflows();
      for (String id : configuration.getWorkflow()) {
        if (! ngWorkflows.contains(id)) {
          WorkflowImpl workflowImpl =
                  (WorkflowImpl) WorkflowImpl.getWorkflow(id);
          try {
            existingNetworkGroup.registerWorkflow(workflowImpl);
          } catch (DirectoryException de) {
            if (resultCode == ResultCode.SUCCESS)
            {
              resultCode = de.getResultCode();
            }
            messages.add(de.getMessageObject());
          }
        }
      }
      // Update the client connection affinity policy.
      existingNetworkGroup.setAffinityPolicy(
        ClientConnectionAffinityPolicy.toClientConnectionAffinityPolicy(
          configuration.getAffinityPolicy()));
      // Update the client connection affinity timeout
      existingNetworkGroup.setAffinityTimeout(
        configuration.getAffinityTimeout());
    }
    configChangeResult =
      new ConfigChangeResult(resultCode, adminActionRequired, messages);
    return configChangeResult;
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
  /**
   * Creates and registers a network group.
   *
   * @param networkGroupCfg  the network group configuration
   *
   * @throws DirectoryException If a problem occurs while trying to
   *                            register a network group.
   * {@inheritDoc}
   */
  private void createAndRegisterNetworkGroup(
      NetworkGroupCfg networkGroupCfg
      ) throws DirectoryException
  public ConfigChangeResult applyConfigurationDelete(
      NetworkGroupCfg configuration)
  {
    // create the network group
    String networkGroupId = networkGroupCfg.getNetworkGroupId();
    NetworkGroup networkGroup = new NetworkGroup(networkGroupId);
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    List<Message> messages = new ArrayList<Message>();
    // register the workflows with the network group
    for (String workflowID: networkGroupCfg.getWorkflow())
    NetworkGroup networkGroup =
        networkGroups.remove(configuration.dn());
    if (networkGroup != null)
    {
      WorkflowImpl workflowImpl =
        (WorkflowImpl) WorkflowImpl.getWorkflow(workflowID);
      if (workflowImpl == null)
      networkGroup.finalizeNetworkGroup();
    }
    return new ConfigChangeResult(resultCode, adminActionRequired,
        messages);
  }
  /**
   * Initializes all network groups currently defined in the Directory
   * Server configuration. This should only be called at Directory
   * Server startup.
   *
   * @throws ConfigException
   *           If a critical configuration problem prevents the network
   *           group initialization from succeeding.
   * @throws InitializationException
   *           If a problem occurs while initializing the network groups
   *           that is not related to the server configuration.
   */
  public void initializeNetworkGroups() throws ConfigException,
      InitializationException
  {
    // Get the root configuration object.
    ServerManagementContext managementContext =
        ServerManagementContext.getInstance();
    RootCfg rootConfiguration =
        managementContext.getRootConfiguration();
    // Register as an add and delete listener with the root
    // configuration so we can be notified if any network group entries
    // are added or removed.
    rootConfiguration.addNetworkGroupAddListener(this);
    rootConfiguration.addNetworkGroupDeleteListener(this);
    // Initialize the existing network groups.
    for (String networkGroupName : rootConfiguration
        .listNetworkGroups())
    {
      NetworkGroupCfg configuration =
          rootConfiguration.getNetworkGroup(networkGroupName);
      configuration.addChangeListener(this);
      List<Message> unacceptableReasons = new ArrayList<Message>();
      if (!NetworkGroup.isConfigurationAcceptable(configuration,
          unacceptableReasons))
      {
        // The workflow does not exist, log an error message
        // and skip the workflow
        Message message = INFO_ERR_WORKFLOW_DOES_NOT_EXIST.get(
          workflowID, networkGroupId);
        logError(message);
        Message message =
            ERR_CONFIG_NETWORK_GROUP_CONFIG_NOT_ACCEPTABLE.get(String
                .valueOf(configuration.dn()), StaticUtils.listToString(
                unacceptableReasons, ". "));
        throw new InitializationException(message);
      }
      else
      if (configuration.isEnabled())
      {
        networkGroup.registerWorkflow(workflowImpl);
        NetworkGroup networkGroup =
            NetworkGroup.createUserNetworkGroup(configuration);
        networkGroups.put(configuration.dn(), networkGroup);
      }
    }
  }
    // register the root DSE workflow with the network group
    WorkflowImpl rootDSEworkflow =
      (WorkflowImpl) WorkflowImpl.getWorkflow("__root.dse__#");
    networkGroup.registerWorkflow(rootDSEworkflow);
    // finally register the network group with the server
    networkGroups.put(networkGroupCfg.dn(), networkGroup);
    networkGroup.register();
    // Set the priority
    networkGroup.setNetworkGroupPriority(networkGroupCfg.getPriority());
    // Set the criteria
    NetworkGroupCriteriaCfg criteriaCfg;
    NetworkGroupCriteria criteria;
    try {
      criteriaCfg = networkGroupCfg.getNetworkGroupCriteria();
    } catch (ConfigException ce) {
      criteriaCfg = null;
    }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
      NetworkGroupCfg configuration, List<Message> unacceptableReasons)
  {
    return NetworkGroup.isConfigurationAcceptable(configuration,
        unacceptableReasons);
  }
    criteria = new NetworkGroupCriteria(criteriaCfg);
    networkGroup.setCriteria(criteria);
    // Add a config listener on the criteria
    try {
      networkGroupCfg.addNetworkGroupCriteriaAddListener(criteria);
      networkGroupCfg.addNetworkGroupCriteriaDeleteListener(criteria);
    } catch (ConfigException ex) {
      throw new DirectoryException(ResultCode.UNDEFINED,
            ex.getMessageObject());
    }
    // Set the resource limits
    NetworkGroupResourceLimitsCfg limitsCfg;
    ResourceLimits limits;
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
      NetworkGroupCfg configuration, List<Message> unacceptableReasons)
  {
    return NetworkGroup.isConfigurationAcceptable(configuration,
        unacceptableReasons);
  }
    try {
      limitsCfg = networkGroupCfg.getNetworkGroupResourceLimits();
    } catch (ConfigException ex) {
      limitsCfg = null;
    }
    limits = new ResourceLimits(limitsCfg);
    networkGroup.setResourceLimits(limits);
    // Add a config listener on the resource limits
    try {
      networkGroupCfg.addNetworkGroupResourceLimitsAddListener(limits);
      networkGroupCfg.addNetworkGroupResourceLimitsDeleteListener(limits);
    } catch (ConfigException ex) {
      throw new DirectoryException(ResultCode.UNDEFINED,
              ex.getMessageObject());
    }
    // Set the request filtering policy
    NetworkGroupRequestFilteringPolicyCfg policyCfg;
    RequestFilteringPolicy policy;
    try {
      policyCfg = networkGroupCfg.getNetworkGroupRequestFilteringPolicy();
    } catch (ConfigException ex) {
      policyCfg = null;
    }
    policy = new RequestFilteringPolicy(policyCfg);
    networkGroup.setRequestFilteringPolicy(policy);
    // Add a config listener on the request filtering policy
    try {
      networkGroupCfg.addNetworkGroupRequestFilteringPolicyAddListener
              (policy);
      networkGroupCfg.addNetworkGroupRequestFilteringPolicyDeleteListener(
              policy);
    } catch (ConfigException ex) {
      throw new DirectoryException(ResultCode.UNDEFINED,
              ex.getMessageObject());
    }
    // Set the client connection affinity policy.
    ClientConnectionAffinityPolicy affinityPolicy =
      ClientConnectionAffinityPolicy.toClientConnectionAffinityPolicy(
        networkGroupCfg.getAffinityPolicy());
    networkGroup.setAffinityPolicy(affinityPolicy);
    // Set the client connection affinity timeout
    long affinityTimeout = networkGroupCfg.getAffinityTimeout();
    networkGroup.setAffinityTimeout(affinityTimeout);
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
      NetworkGroupCfg configuration, List<Message> unacceptableReasons)
  {
    // Always ok.
    return true;
  }
}
opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriteria.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriterion.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPlugin.java
File was renamed from opends/src/server/org/opends/server/plugins/NetworkGroupPlugin.java
@@ -22,9 +22,9 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.plugins;
package org.opends.server.core.networkgroups;
@@ -40,7 +40,6 @@
import org.opends.server.api.ClientConnection;
import org.opends.server.api.plugin.*;
import org.opends.server.config.ConfigException;
import org.opends.server.core.networkgroups.NetworkGroup;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
@@ -73,10 +72,6 @@
       extends DirectoryServerPlugin<NetworkGroupPluginCfg>
       implements ConfigurationChangeListener<NetworkGroupPluginCfg>
{
  // The current configuration for this plugin.
  private NetworkGroupPluginCfg currentConfig;
  /**
   * Creates a new instance of this Directory Server plugin.  Every plugin must
@@ -87,8 +82,6 @@
  public NetworkGroupPlugin()
  {
    super();
  }
  /**
@@ -99,8 +92,6 @@
                                     NetworkGroupPluginCfg configuration)
         throws ConfigException
  {
    currentConfig = configuration;
    // Make sure that the plugin has been enabled for the appropriate types.
    for (PluginType t : pluginTypes)
    {
@@ -151,7 +142,7 @@
          boolean fullCheck,
          ArrayList<Message> messages)
  {
    if (!connection.getNetworkGroup().checkResourceLimits(
    if (!connection.getNetworkGroup().checkResourceLimitsPolicy(
            connection, operation, fullCheck, messages)) {
      return false;
    }
@@ -449,7 +440,6 @@
  public ConfigChangeResult applyConfigurationChange(
                                 NetworkGroupPluginCfg configuration)
  {
    currentConfig = configuration;
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
}
opends/src/server/org/opends/server/core/networkgroups/NetworkGroupStatistics.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
@@ -40,8 +40,8 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.SearchScope;
import static org.opends.messages.ProtocolMessages.*;
import static org.opends.server.protocols.ldap.LDAPConstants.*;
import static org.opends.messages.ProtocolMessages.*;
/**
 * This class implements the statistics associated to a network group.
@@ -50,10 +50,10 @@
       extends MonitorProvider<MonitorProviderCfg> {
  // The instance name for this monitor provider instance.
  private String instanceName;
  private NetworkGroup networkGroup;
  private final String instanceName;
  private final NetworkGroup networkGroup;
  private Object lock = new Object();
  private final Object lock = new Object();
  private long abandonRequests = 0;
  private long addRequests = 0;
  private long bindRequests = 0;
@@ -131,6 +131,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void initializeMonitorProvider(MonitorProviderCfg configuration)
         throws ConfigException, InitializationException {
    // Throw an exception, because this monitor is not intended to be
@@ -145,6 +146,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getMonitorInstanceName() {
      return this.instanceName+",cn=Network Groups";
  }
@@ -152,6 +154,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public long getUpdateInterval() {
    // This monitor should not run periodically.
    return -1;
@@ -160,6 +163,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void updateMonitorData() {
    // No implementation is required since this does not do periodic updates.
  }
@@ -167,30 +171,39 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public List<Attribute> getMonitorData() {
    ArrayList<Attribute> attrs = new ArrayList<Attribute>();
    RequestFilteringPolicyStat requestFilteringPolicyStat =
            networkGroup.getRequestFilteringPolicyStat();
    if (requestFilteringPolicyStat != null) {
      attrs.add(Attributes.create("ds-mon-rejected-attributes-total-count",
          String.valueOf(requestFilteringPolicyStat.getRejectedAttributes())));
      attrs.add(Attributes.create("ds-mon-rejected-operations-total-count",
          String.valueOf(requestFilteringPolicyStat.getRejectedOperations())));
      attrs.add(Attributes.create("ds-mon-rejected-search-scopes-total-count",
          String.valueOf(requestFilteringPolicyStat.getRejectedScopes())));
      attrs.add(Attributes.create("ds-mon-rejected-subtrees-total-count",
          String.valueOf(requestFilteringPolicyStat.getRejectedSubtrees())));
    RequestFilteringPolicyStatistics rfpStatistics =
        networkGroup.getRequestFilteringPolicyStatistics();
    if (rfpStatistics != null)
    {
      attrs.add(Attributes.create(
          "ds-mon-rejected-attributes-total-count", String
              .valueOf(rfpStatistics.getRejectedAttributes())));
      attrs.add(Attributes.create(
          "ds-mon-rejected-operations-total-count", String
              .valueOf(rfpStatistics.getRejectedOperations())));
      attrs.add(Attributes.create(
          "ds-mon-rejected-search-scopes-total-count", String
              .valueOf(rfpStatistics.getRejectedScopes())));
      attrs.add(Attributes.create(
          "ds-mon-rejected-subtrees-total-count", String
              .valueOf(rfpStatistics.getRejectedSubtrees())));
    }
    ResourceLimitsStat resLimitStat = networkGroup.getResourceLimitStat();
    if (resLimitStat != null) {
    ResourceLimitsPolicyStatistics rlpStatistics =
        networkGroup.getResourceLimitsPolicyStatistics();
    if (rlpStatistics != null)
    {
      attrs.add(Attributes.create("ds-mon-client-connection-count",
          String.valueOf(resLimitStat.getClientConnections())));
          String.valueOf(rlpStatistics.getClientConnections())));
      attrs.add(Attributes.create("ds-mon-client-connection-max-count",
          String.valueOf(resLimitStat.getMaxClientConnections())));
      attrs.add(Attributes.create("ds-mon-client-connection-total-count",
          String.valueOf(resLimitStat.getTotalClientConnections())));
          String.valueOf(rlpStatistics.getMaxClientConnections())));
      attrs.add(Attributes.create(
          "ds-mon-client-connection-total-count", String
              .valueOf(rlpStatistics.getTotalClientConnections())));
    }
    synchronized(lock) {
@@ -218,6 +231,7 @@
      attrs.add(Attributes.create("ds-mon-unbind-operations-total-count",
          String.valueOf(unbindRequests)));
    }
    attrs.add(Attributes.create("ds-mon-discarded-referrals-total-count",
        "Not implemented"));
    attrs.add(Attributes.create("ds-mon-forwarded-referrals-total-count",
opends/src/server/org/opends/server/core/networkgroups/PortCriteria.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/ProtocolConnectionCriteria.java
New file
@@ -0,0 +1,109 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedProtocol;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * A connection criteria which matches connections which use a permitted
 * protocol.
 */
final class ProtocolConnectionCriteria implements ConnectionCriteria
{
  // The set of allowed protocols.
  private final Set<AllowedProtocol> protocols;
  /**
   * Creates a new protocol connection criteria using the provided
   * allowed protocols.
   *
   * @param protocols
   *          The allowed protocols.
   */
  public ProtocolConnectionCriteria(
      Collection<AllowedProtocol> protocols)
  {
    this.protocols = EnumSet.copyOf(protocols);
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    String protocolName =
        connection.getConnectionHandler().getProtocol();
    for (AllowedProtocol protocol : protocols)
    {
      switch (protocol)
      {
      case LDAP:
        if (protocolName.equals("LDAP"))
        {
          return true;
        }
        break;
      case LDAPS:
        if (protocolName.equals("LDAP+SSL"))
        {
          return true;
        }
        break;
      }
    }
    return false;
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    return matches(connection);
  }
}
opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicy.java
@@ -22,637 +22,59 @@
 * CDDL HEADER END
 *
 *
 *    Copyright 2008 Sun Microsystems, Inc.
 *    Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationAddListener;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.server.ConfigurationDeleteListener;
import org.opends.server.admin.std.server.
        NetworkGroupRequestFilteringPolicyCfg;
import org.opends.server.admin.std.meta.
        NetworkGroupRequestFilteringPolicyCfgDefn.AllowedOperations;
import org.opends.server.admin.std.meta.
        NetworkGroupRequestFilteringPolicyCfgDefn.AllowedSearchScopes;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.OperationType;
import org.opends.server.types.RawFilter;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PreParseAddOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import org.opends.server.types.operation.PreParseDeleteOperation;
import org.opends.server.types.operation.PreParseModifyDNOperation;
import org.opends.server.types.operation.PreParseModifyOperation;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.api.QOSPolicy;
import org.opends.server.types.operation.PreParseOperation;
import org.opends.server.types.operation.PreParseSearchOperation;
import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.ConfigMessages.*;
/**
 * This class defines the request filtering policy applicable to all
 * connections inside the same network group.
 */
public class RequestFilteringPolicy
implements ConfigurationAddListener<NetworkGroupRequestFilteringPolicyCfg>,
           ConfigurationDeleteListener<NetworkGroupRequestFilteringPolicyCfg>,
           ConfigurationChangeListener<NetworkGroupRequestFilteringPolicyCfg>
abstract class RequestFilteringPolicy extends QOSPolicy
{
  // The request filtering policy is defined through the config
  private boolean isConfigured = false;
  // The list of allowed operations
  Set<AllowedOperations> allowedOperations = null;
  // The list of allowed attributes
  Set<String> allowedAttributes = null;
  // The list of prohibited attributes
  Set<String> prohibitedAttributes = null;
  // The list of allowed search scopes
  Set<AllowedSearchScopes> allowedSearchScopes = null;
  // The list of allowed subtrees
  Set<DN> allowedSubtrees = null;
  // The list of prohibited subtrees
  Set<DN> prohibitedSubtrees = null;
  // The stats for the request filtering policy
  private RequestFilteringPolicyStat stat = new RequestFilteringPolicyStat();
  // The current configuration
  NetworkGroupRequestFilteringPolicyCfg config = null;
  /**
   * Constructor.
   *
   * @param policyCfg configuration
   * Creates a new request filtering policy.
   */
  public RequestFilteringPolicy(
          NetworkGroupRequestFilteringPolicyCfg policyCfg)
  protected RequestFilteringPolicy()
  {
    createPolicy(policyCfg);
    // No implementation required.
  }
  /**
   * Resets all the fields.
   */
  private void resetPolicy() {
    allowedOperations = Collections.emptySet();
    allowedAttributes = Collections.emptySet();
    prohibitedAttributes = Collections.emptySet();
    allowedSearchScopes = Collections.emptySet();
    allowedSubtrees = Collections.emptySet();
    prohibitedSubtrees = Collections.emptySet();
    isConfigured = false;
    if (config != null) {
      config.removeChangeListener(this);
    }
    config = null;
  }
  /**
   * Creates a RequestFilteringPolicy from a configuration object.
   * Returns the statistics associated with this request filtering
   * policy.
   *
   * @param policyCfg the configuration
   * @return The statistics associated with this request filtering
   *         policy.
   */
  private void createPolicy(
          NetworkGroupRequestFilteringPolicyCfg policyCfg)
  {
    if (policyCfg != null) {
      allowedOperations = policyCfg.getAllowedOperations();
      allowedAttributes = policyCfg.getAllowedAttributes();
      prohibitedAttributes = policyCfg.getProhibitedAttributes();
      allowedSearchScopes = policyCfg.getAllowedSearchScopes();
      allowedSubtrees = policyCfg.getAllowedSubtrees();
      prohibitedSubtrees = policyCfg.getProhibitedSubtrees();
  abstract RequestFilteringPolicyStatistics getStatistics();
      if (config == null) {
        policyCfg.addChangeListener(this);
      }
      config = policyCfg;
      isConfigured = true;
    } else {
      resetPolicy();
    }
  }
  /**
   * Returns the statistics associated to this policy.
   * @return The statistics associated to this policy
   */
  public RequestFilteringPolicyStat getStat() {
    return stat;
  }
  /**
   * Configures the set of allowed operations.
   * @param allowedOps The set of allowed operations
   */
  public void setAllowedOperations(Set<AllowedOperations> allowedOps) {
    if (allowedOps == null) {
      allowedOperations = Collections.emptySet();
    } else {
      allowedOperations = allowedOps;
    }
  }
  /**
   * Configures the set of allowed attributes in search and compare operations.
   * @param allowedAttrs The set of allowed attributes
   */
  public void setAllowedAttributes(Set<String> allowedAttrs) {
    if (allowedAttrs == null) {
      allowedAttributes = Collections.emptySet();
    } else {
      allowedAttributes = allowedAttrs;
    }
  }
  /**
   * Configures the set of prohibited attributes in search and compare
   * operations.
   * @param prohibitedAttrs The set of prohibited attributes
   */
  public void setProhibitedAttributes(Set<String> prohibitedAttrs) {
    if (prohibitedAttrs == null) {
      prohibitedAttributes = Collections.emptySet();
    } else {
      prohibitedAttributes = prohibitedAttrs;
    }
  }
  /**
   * Configures the set of allowed search scopes.
   * @param allowedScopes The set of scopes
   */
  public void setAllowedSearchScopes(Set<AllowedSearchScopes> allowedScopes) {
    if (allowedScopes == null) {
      allowedSearchScopes = Collections.emptySet();
    } else {
      allowedSearchScopes = allowedScopes;
    }
  }
  /**
   * Configures the set of subtrees allowed in search operations.
   * @param allowedSubt The set of allowed subtrees
   */
  public void setAllowedSubtrees(Set<DN> allowedSubt) {
    if (allowedSubt == null) {
      allowedSubtrees = Collections.emptySet();
    } else {
      allowedSubtrees = allowedSubt;
    }
  }
  /**
   * Configures the set of subtrees prohibited in search operations.
   * @param prohibitedSubt The set of prohibited subtrees
   */
  public void setProhibitedSubtrees(Set<DN> prohibitedSubt) {
    if (prohibitedSubt == null) {
      prohibitedSubtrees = Collections.emptySet();
    } else {
      prohibitedSubtrees = prohibitedSubt;
    }
  }
  /**
   * Checks the request filtering policy.
   * Determines if the provided operation is allowed according to this
   * request filtering policy.
   *
   * @param operation the ongoing operation
   * @param messages the messages to include in the disconnect notification
   *                response.  It may be <CODE>null</CODE> if no message
   *                is to be sent.
   * @return a boolean indicating whether the operation is allowed
   * @param operation
   *          The operation
   * @param messages
   *          The messages to include in the disconnect notification
   *          response. It may be <CODE>null</CODE> if no message is to
   *          be sent.
   * @return {@code true} if the operation is allowed.
   */
  public boolean checkPolicy(
          PreParseOperation operation,
          List<Message> messages)
  {
    boolean result = true;
    // Check the allowed operations
    if (!allowedOperations.isEmpty()) {
      switch (operation.getOperationType()) {
        case ABANDON:
          result= true;
          break;
        case ADD:
          result = allowedOperations.contains(AllowedOperations.ADD);
          break;
        case BIND:
          result = allowedOperations.contains(AllowedOperations.BIND);
          break;
        case COMPARE:
          result = allowedOperations.contains(AllowedOperations.COMPARE);
          break;
        case DELETE:
          result = allowedOperations.contains(AllowedOperations.DELETE);
          break;
        case EXTENDED:
          result = allowedOperations.contains(AllowedOperations.EXTENDED);
          break;
        case MODIFY:
          result = allowedOperations.contains(AllowedOperations.MODIFY);
          break;
        case MODIFY_DN:
          result = allowedOperations.contains(AllowedOperations.RENAME);
          break;
        case SEARCH:
          result = allowedOperations.contains(AllowedOperations.SEARCH);
          // If inequality search are prohibited, need to check
          if (result && !allowedOperations.contains(
                  AllowedOperations.INEQUALITY_SEARCH)) {
              RawFilter flt =
                      ((PreParseSearchOperation) operation).getRawFilter();
              result = (!containsInequalitySearch(flt));
          }
          break;
        case UNBIND:
          result = true;
          break;
      }
      if (!result) {
        stat.updateRejectedOperations();
        messages.add(INFO_ERROR_OPERATION_NOT_ALLOWED.get());
        return result;
      }
    }
    // For search operations:
    if (operation.getOperationType().equals(OperationType.SEARCH)) {
      PreParseSearchOperation searchOp = (PreParseSearchOperation) operation;
      // Check the allowed/prohibited attributes in search filter
      if (!prohibitedAttributes.isEmpty()) {
        // The attributes specified in prohibitedAttributes are not OK
        result = (!containsProhibitedAttribute(searchOp.getRawFilter()));
      }
      if (!result) {
        stat.updateRejectedAttributes();
        messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
        return result;
      }
      if (!allowedAttributes.isEmpty()) {
        // Only the attributes specified in allowedAttributes are OK
        result = (containsOnlyAllowedAttributes(searchOp.getRawFilter()));
      }
      if (!result) {
        stat.updateRejectedAttributes();
        messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
        return result;
      }
      // Check the search scope
      if (!allowedSearchScopes.isEmpty()) {
        switch (searchOp.getScope()) {
          case BASE_OBJECT:
            result = allowedSearchScopes.contains(AllowedSearchScopes.BASE);
            break;
          case SINGLE_LEVEL:
            result = allowedSearchScopes.contains(AllowedSearchScopes.ONE);
            break;
          case WHOLE_SUBTREE:
            result = allowedSearchScopes.contains(AllowedSearchScopes.SUB);
            break;
          case SUBORDINATE_SUBTREE:
            result = allowedSearchScopes.contains(AllowedSearchScopes.CHILDREN);
            break;
        }
        if (!result) {
          stat.updateRejectedScopes();
          messages.add(INFO_ERROR_SEARCH_SCOPE_NOT_ALLOWED.get());
          return result;
        }
      }
    }
    // For compare operation
    if (operation.getOperationType().equals(OperationType.COMPARE)) {
      PreParseCompareOperation compareOp = (PreParseCompareOperation) operation;
      // Check the allowed/prohibited attributes
      if (!prohibitedAttributes.isEmpty()) {
        result = (!prohibitedAttributes.contains(
                compareOp.getRawAttributeType()));
      }
      if (!result) {
        stat.updateRejectedAttributes();
        messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
        return result;
      }
      if (!allowedAttributes.isEmpty()) {
        result = (allowedAttributes.contains(compareOp.getRawAttributeType()));
      }
      if (!result) {
        stat.updateRejectedAttributes();
        messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
        return result;
      }
    }
    DN entryDN = null;
    DN newEntryDN = null;
    try {
      switch (operation.getOperationType()) {
        case ADD:
          entryDN = DN.decode(
                  ((PreParseAddOperation) operation).getRawEntryDN());
          break;
        case COMPARE:
          entryDN = DN.decode(
                  ((PreParseCompareOperation) operation).getRawEntryDN());
          break;
        case DELETE:
          entryDN = DN.decode(
                  ((PreParseDeleteOperation) operation).getRawEntryDN());
          break;
        case EXTENDED:
          break;
        case MODIFY:
          entryDN = DN.decode(
                  ((PreParseModifyOperation) operation).getRawEntryDN());
          break;
        case MODIFY_DN:
          entryDN = DN.decode(
                  ((PreParseModifyDNOperation) operation).getRawEntryDN());
          newEntryDN = DN.decode(
                  ((PreParseModifyDNOperation) operation).getRawNewRDN());
          break;
        case SEARCH:
          entryDN = DN.decode(
                  ((PreParseSearchOperation) operation).getRawBaseDN());
          break;
        default:
          break;
      }
      if (entryDN != null) {
        result = ((isInAllowedSubtrees(entryDN))
                    && !(isInProhibitedSubtrees(entryDN)));
      }
      if (newEntryDN != null) {
        result = ((isInAllowedSubtrees(newEntryDN))
                    && !(isInProhibitedSubtrees(newEntryDN)));
      }
    } catch (DirectoryException ex) {
      Logger.getLogger(RequestFilteringPolicy.class.getName())
                  .log(Level.SEVERE, null, ex);
    }
    if (!result) {
      stat.updateRejectedSubtrees();
      messages.add(INFO_ERROR_SUBTREE_NOT_ALLOWED.get());
      return result;
    }
    return (true);
  }
  /**
   * Checks whether a filter contains an inequality search filter
   * (i.e. either a greater_or_equal or a less_or_equal filter).
   * @param filter The filter to be tested
   * @return boolean indicating whether the filter contains an inequality
   *         search filter
   */
  private boolean containsInequalitySearch(RawFilter filter) {
    boolean result = false;
      switch (filter.getFilterType()) {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents = filter.getFilterComponents();
        if (filterComponents != null) {
          for (RawFilter element : filterComponents) {
            if (containsInequalitySearch(element)) {
              return true;
            }
          }
        }
        return false;
      case NOT:
        return containsInequalitySearch(filter.getNOTComponent());
      case GREATER_OR_EQUAL:
      case LESS_OR_EQUAL:
        return true;
      default:
        return false;
    }
  }
  /**
   * Checks whether a filter contains one of the prohibited attributes.
   * @param filter The filter to be tested
   * @return boolean indicating whether the filter contains at least one of
   *         the prohibited attributes
   */
  private boolean containsProhibitedAttribute(
          RawFilter filter) {
    boolean result = false;
    switch (filter.getFilterType()) {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents = filter.getFilterComponents();
        if (filterComponents != null) {
          for (RawFilter element : filterComponents) {
            if (containsProhibitedAttribute(element)) {
              return true;
            }
          }
        }
        return false;
      case NOT:
        return (containsProhibitedAttribute(filter.getNOTComponent()));
      default:
        return (prohibitedAttributes.contains(filter.getAttributeType()));
    }
  }
  /**
   * Checks whether a filter contains unallowed attributes.
   * @param filter The filter to be tested
   * @return boolean indicating whether the filter contains at least one
   *         attribute which is not in the allowed list
   */
  private boolean containsOnlyAllowedAttributes(
          RawFilter filter) {
    switch (filter.getFilterType()) {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents = filter.getFilterComponents();
        if (filterComponents != null) {
          for (RawFilter element : filterComponents) {
            if (!containsOnlyAllowedAttributes(element)) {
              return false;
            }
          }
        }
        return true;
      case NOT:
        return (containsOnlyAllowedAttributes(filter.getNOTComponent()));
      default:
        return (allowedAttributes.contains(filter.getAttributeType()));
    }
  }
  /**
   * Checks whether a DN is in a branch of the allowed subtrees.
   * @param dn The DN to be tested
   * @return boolean indicating whether the dn is in a branch of the allowed
   *         subtrees
   */
  private boolean isInAllowedSubtrees(DN dn) {
    boolean result = false;
    // If the variable is not set, consider allowedSubtrees = rootDSE
    if (allowedSubtrees.isEmpty()) {
      return true;
    }
    for (DN branch:allowedSubtrees) {
      if (dn.isDescendantOf(branch)) {
        result = true;
        break;
      }
    }
    return result;
  }
  /**
   * Checks whether a DN is in a branch of the prohibited subtrees.
   * @param dn The Dn to be tested
   * @return boolean indicating whether the dn is in a branch of the prohibited
   *         subtrees
   */
  private boolean isInProhibitedSubtrees(DN dn) {
    boolean result = false;
    for (DN branch:prohibitedSubtrees) {
      if (dn.isDescendantOf(branch)) {
        result = true;
        break;
      }
    }
    return result;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAddAcceptable(
          NetworkGroupRequestFilteringPolicyCfg configuration,
          List<Message> unacceptableReasons) {
    if (isConfigured) {
      return false;
    }
    return (isConfigurationChangeAcceptable(configuration,
        unacceptableReasons));
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationAdd(
          NetworkGroupRequestFilteringPolicyCfg configuration) {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    ConfigChangeResult configChangeResult =
          new ConfigChangeResult(resultCode, adminActionRequired, messages);
    createPolicy(configuration);
    return configChangeResult;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationDeleteAcceptable(
          NetworkGroupRequestFilteringPolicyCfg configuration,
          List<Message> unacceptableReasons) {
    return isConfigured;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationDelete(
          NetworkGroupRequestFilteringPolicyCfg configuration) {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    ConfigChangeResult configChangeResult =
      new ConfigChangeResult(resultCode, adminActionRequired, messages);
    resetPolicy();
    return configChangeResult;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
          NetworkGroupRequestFilteringPolicyCfg configuration,
          List<Message> unacceptableReasons) {
    if (configuration != null) {
      // Check that allowed-attributes does not contain any attribute
      // also configured in prohibited-attributes
      for (String allowedAttr: configuration.getAllowedAttributes()) {
        if (configuration.getProhibitedAttributes().contains(allowedAttr)) {
          unacceptableReasons.add(
              ERR_CONFIG_NETWORKGROUPREQUESTFILTERINGPOLICY_INVALID_ATTRIBUTE
              .get(allowedAttr, configuration.dn().toString()));
          return false;
        }
      }
      // Check that allowed-subtrees does not contain any subtree also
      // configured in prohibited-subtrees
      for (DN allowedSubtree: configuration.getAllowedSubtrees()) {
        if (configuration.getProhibitedSubtrees().contains(allowedSubtree)) {
          unacceptableReasons.add(
              ERR_CONFIG_NETWORKGROUPREQUESTFILTERINGPOLICY_INVALID_SUBTREE.get(
              allowedSubtree.toString(), configuration.dn().toString()));
          return false;
        }
      }
    }
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
          NetworkGroupRequestFilteringPolicyCfg configuration) {
    ResultCode resultCode = ResultCode.SUCCESS;
    boolean adminActionRequired = false;
    ArrayList<Message> messages = new ArrayList<Message>();
    ConfigChangeResult configChangeResult =
          new ConfigChangeResult(resultCode, adminActionRequired, messages);
    createPolicy(configuration);
    return configChangeResult;
  }
  abstract boolean isAllowed(PreParseOperation operation,
      List<Message> messages);
}
opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyFactory.java
New file
@@ -0,0 +1,672 @@
/*
 * 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
 *
 *
 *    Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.CoreMessages.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.
  RequestFilteringQOSPolicyCfgDefn.AllowedOperations;
import org.opends.server.admin.std.meta.
  RequestFilteringQOSPolicyCfgDefn.AllowedSearchScopes;
import org.opends.server.admin.std.server.RequestFilteringQOSPolicyCfg;
import org.opends.server.api.QOSPolicyFactory;
import org.opends.server.config.ConfigException;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.OperationType;
import org.opends.server.types.RawFilter;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PreParseAddOperation;
import org.opends.server.types.operation.PreParseCompareOperation;
import org.opends.server.types.operation.PreParseDeleteOperation;
import org.opends.server.types.operation.PreParseModifyDNOperation;
import org.opends.server.types.operation.PreParseModifyOperation;
import org.opends.server.types.operation.PreParseOperation;
import org.opends.server.types.operation.PreParseSearchOperation;
/**
 * This class defines a factory for creating user configurable request
 * filtering policies.
 */
public final class RequestFilteringPolicyFactory implements
    QOSPolicyFactory<RequestFilteringQOSPolicyCfg>
{
  /**
   * Policy implementation.
   */
  private static final class Policy extends RequestFilteringPolicy
      implements
      ConfigurationChangeListener<RequestFilteringQOSPolicyCfg>
  {
    // The list of allowed attributes
    private Set<String> allowedAttributes = null;
    // The list of allowed operations
    private Set<AllowedOperations> allowedOperations = null;
    // The list of allowed search scopes
    private Set<AllowedSearchScopes> allowedSearchScopes = null;
    // The list of allowed subtrees
    private Set<DN> allowedSubtrees = null;
    // The list of prohibited attributes
    private Set<String> prohibitedAttributes = null;
    // The list of prohibited subtrees
    private Set<DN> prohibitedSubtrees = null;
    // The statistics for the request filtering policy
    private final RequestFilteringPolicyStatistics statistics =
        new RequestFilteringPolicyStatistics();
    /**
     * Creates a new request filtering policy.
     */
    private Policy()
    {
      // Nothing to do.
    }
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationChange(
        RequestFilteringQOSPolicyCfg configuration)
    {
      ResultCode resultCode = ResultCode.SUCCESS;
      boolean adminActionRequired = false;
      ArrayList<Message> messages = new ArrayList<Message>();
      // Save the configuration.
      updateConfiguration(configuration);
      return new ConfigChangeResult(resultCode, adminActionRequired,
          messages);
    }
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationChangeAcceptable(
        RequestFilteringQOSPolicyCfg configuration,
        List<Message> unacceptableReasons)
    {
      return RequestFilteringPolicyFactory.validateConfiguration(
          configuration, unacceptableReasons);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    RequestFilteringPolicyStatistics getStatistics()
    {
      return statistics;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    boolean isAllowed(PreParseOperation operation,
        List<Message> messages)
    {
      boolean allowRequest = true;
      // Check the allowed operations
      if (!allowedOperations.isEmpty())
      {
        switch (operation.getOperationType())
        {
        case ABANDON:
          allowRequest = true;
          break;
        case ADD:
          allowRequest =
              allowedOperations.contains(AllowedOperations.ADD);
          break;
        case BIND:
          allowRequest =
              allowedOperations.contains(AllowedOperations.BIND);
          break;
        case COMPARE:
          allowRequest =
              allowedOperations.contains(AllowedOperations.COMPARE);
          break;
        case DELETE:
          allowRequest =
              allowedOperations.contains(AllowedOperations.DELETE);
          break;
        case EXTENDED:
          allowRequest =
              allowedOperations.contains(AllowedOperations.EXTENDED);
          break;
        case MODIFY:
          allowRequest =
              allowedOperations.contains(AllowedOperations.MODIFY);
          break;
        case MODIFY_DN:
          allowRequest =
              allowedOperations.contains(AllowedOperations.RENAME);
          break;
        case SEARCH:
          allowRequest =
              allowedOperations.contains(AllowedOperations.SEARCH);
          // If inequality search are prohibited, need to check
          if (allowRequest
              && !allowedOperations
                  .contains(AllowedOperations.INEQUALITY_SEARCH))
          {
            RawFilter flt =
                ((PreParseSearchOperation) operation).getRawFilter();
            allowRequest = !containsInequalitySearch(flt);
          }
          break;
        case UNBIND:
          allowRequest = true;
          break;
        }
        if (!allowRequest)
        {
          statistics.updateRejectedOperations();
          messages.add(INFO_ERROR_OPERATION_NOT_ALLOWED.get());
          return allowRequest;
        }
      }
      // For search operations:
      if (operation.getOperationType().equals(OperationType.SEARCH))
      {
        PreParseSearchOperation searchOp =
            (PreParseSearchOperation) operation;
        // Check the allowed/prohibited attributes in search filter
        if (!prohibitedAttributes.isEmpty())
        {
          // The attributes specified in prohibitedAttributes are not OK
          allowRequest =
              !containsProhibitedAttribute(searchOp.getRawFilter());
        }
        if (!allowRequest)
        {
          statistics.updateRejectedAttributes();
          messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
          return allowRequest;
        }
        if (!allowedAttributes.isEmpty())
        {
          // Only the attributes specified in allowedAttributes are OK
          allowRequest =
              containsOnlyAllowedAttributes(searchOp.getRawFilter());
        }
        if (!allowRequest)
        {
          statistics.updateRejectedAttributes();
          messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
          return allowRequest;
        }
        // Check the search scope
        if (!allowedSearchScopes.isEmpty())
        {
          switch (searchOp.getScope())
          {
          case BASE_OBJECT:
            allowRequest =
                allowedSearchScopes.contains(AllowedSearchScopes.BASE);
            break;
          case SINGLE_LEVEL:
            allowRequest =
                allowedSearchScopes.contains(AllowedSearchScopes.ONE);
            break;
          case WHOLE_SUBTREE:
            allowRequest =
                allowedSearchScopes.contains(AllowedSearchScopes.SUB);
            break;
          case SUBORDINATE_SUBTREE:
            allowRequest =
                allowedSearchScopes
                    .contains(AllowedSearchScopes.CHILDREN);
            break;
          }
          if (!allowRequest)
          {
            statistics.updateRejectedScopes();
            messages.add(INFO_ERROR_SEARCH_SCOPE_NOT_ALLOWED.get());
            return allowRequest;
          }
        }
      }
      // For compare operation
      if (operation.getOperationType().equals(OperationType.COMPARE))
      {
        PreParseCompareOperation compareOp =
            (PreParseCompareOperation) operation;
        // Check the allowed/prohibited attributes
        if (!prohibitedAttributes.isEmpty())
        {
          allowRequest =
              !prohibitedAttributes.contains(compareOp
                  .getRawAttributeType());
        }
        if (!allowRequest)
        {
          statistics.updateRejectedAttributes();
          messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
          return allowRequest;
        }
        if (!allowedAttributes.isEmpty())
        {
          allowRequest =
              allowedAttributes.contains(compareOp
                  .getRawAttributeType());
        }
        if (!allowRequest)
        {
          statistics.updateRejectedAttributes();
          messages.add(INFO_ERROR_ATTRIBUTE_NOT_ALLOWED.get());
          return allowRequest;
        }
      }
      DN entryDN = null;
      DN newEntryDN = null;
      try
      {
        switch (operation.getOperationType())
        {
        case ADD:
          entryDN =
              DN.decode(((PreParseAddOperation) operation)
                  .getRawEntryDN());
          break;
        case COMPARE:
          entryDN =
              DN.decode(((PreParseCompareOperation) operation)
                  .getRawEntryDN());
          break;
        case DELETE:
          entryDN =
              DN.decode(((PreParseDeleteOperation) operation)
                  .getRawEntryDN());
          break;
        case EXTENDED:
          break;
        case MODIFY:
          entryDN =
              DN.decode(((PreParseModifyOperation) operation)
                  .getRawEntryDN());
          break;
        case MODIFY_DN:
          entryDN =
              DN.decode(((PreParseModifyDNOperation) operation)
                  .getRawEntryDN());
          newEntryDN =
              DN.decode(((PreParseModifyDNOperation) operation)
                  .getRawNewRDN());
          break;
        case SEARCH:
          entryDN =
              DN.decode(((PreParseSearchOperation) operation)
                  .getRawBaseDN());
          break;
        default:
          break;
        }
        if (entryDN != null)
        {
          allowRequest =
              isInAllowedSubtrees(entryDN)
                  && !isInProhibitedSubtrees(entryDN);
        }
        if (newEntryDN != null)
        {
          allowRequest =
              isInAllowedSubtrees(newEntryDN)
                  && !isInProhibitedSubtrees(newEntryDN);
        }
      }
      catch (DirectoryException e)
      {
        // Invalid DN - reject the request.
        allowRequest = true;
      }
      if (!allowRequest)
      {
        statistics.updateRejectedSubtrees();
        messages.add(INFO_ERROR_SUBTREE_NOT_ALLOWED.get());
        return allowRequest;
      }
      return true;
    }
    /**
     * Checks whether a filter contains an inequality search filter
     * (i.e. either a greater_or_equal or a less_or_equal filter).
     *
     * @param filter
     *          The filter to be tested
     * @return boolean indicating whether the filter contains an
     *         inequality search filter
     */
    private boolean containsInequalitySearch(RawFilter filter)
    {
      switch (filter.getFilterType())
      {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents =
            filter.getFilterComponents();
        if (filterComponents != null)
        {
          for (RawFilter element : filterComponents)
          {
            if (containsInequalitySearch(element))
            {
              return true;
            }
          }
        }
        return false;
      case NOT:
        return containsInequalitySearch(filter.getNOTComponent());
      case GREATER_OR_EQUAL:
      case LESS_OR_EQUAL:
        return true;
      default:
        return false;
      }
    }
    /**
     * Checks whether a filter contains unallowed attributes.
     *
     * @param filter
     *          The filter to be tested
     * @return boolean indicating whether the filter contains at least
     *         one attribute which is not in the allowed list
     */
    private boolean containsOnlyAllowedAttributes(RawFilter filter)
    {
      switch (filter.getFilterType())
      {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents =
            filter.getFilterComponents();
        if (filterComponents != null)
        {
          for (RawFilter element : filterComponents)
          {
            if (!containsOnlyAllowedAttributes(element))
            {
              return false;
            }
          }
        }
        return true;
      case NOT:
        return containsOnlyAllowedAttributes(filter.getNOTComponent());
      default:
        return allowedAttributes.contains(filter.getAttributeType());
      }
    }
    /**
     * Checks whether a filter contains one of the prohibited
     * attributes.
     *
     * @param filter
     *          The filter to be tested
     * @return boolean indicating whether the filter contains at least
     *         one of the prohibited attributes
     */
    private boolean containsProhibitedAttribute(RawFilter filter)
    {
      switch (filter.getFilterType())
      {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents =
            filter.getFilterComponents();
        if (filterComponents != null)
        {
          for (RawFilter element : filterComponents)
          {
            if (containsProhibitedAttribute(element))
            {
              return true;
            }
          }
        }
        return false;
      case NOT:
        return containsProhibitedAttribute(filter.getNOTComponent());
      default:
        return prohibitedAttributes.contains(filter.getAttributeType());
      }
    }
    /**
     * Checks whether a DN is in a branch of the allowed subtrees.
     *
     * @param dn
     *          The DN to be tested
     * @return boolean indicating whether the dn is in a branch of the
     *         allowed subtrees
     */
    private boolean isInAllowedSubtrees(DN dn)
    {
      boolean result = false;
      // If the variable is not set, consider allowedSubtrees = rootDSE
      if (allowedSubtrees.isEmpty())
      {
        return true;
      }
      for (DN branch : allowedSubtrees)
      {
        if (dn.isDescendantOf(branch))
        {
          result = true;
          break;
        }
      }
      return result;
    }
    /**
     * Checks whether a DN is in a branch of the prohibited subtrees.
     *
     * @param dn
     *          The Dn to be tested
     * @return boolean indicating whether the dn is in a branch of the
     *         prohibited subtrees
     */
    private boolean isInProhibitedSubtrees(DN dn)
    {
      boolean result = false;
      for (DN branch : prohibitedSubtrees)
      {
        if (dn.isDescendantOf(branch))
        {
          result = true;
          break;
        }
      }
      return result;
    }
    // Updates this policy's configuration.
    private void updateConfiguration(
        RequestFilteringQOSPolicyCfg configuration)
    {
      this.allowedOperations = configuration.getAllowedOperations();
      this.allowedAttributes = configuration.getAllowedAttributes();
      this.prohibitedAttributes =
          configuration.getProhibitedAttributes();
      this.allowedSearchScopes = configuration.getAllowedSearchScopes();
      this.allowedSubtrees = configuration.getAllowedSubtrees();
      this.prohibitedSubtrees = configuration.getProhibitedSubtrees();
    }
  }
  // Validates a configuration.
  private static boolean validateConfiguration(
      RequestFilteringQOSPolicyCfg configuration,
      List<Message> unacceptableReasons)
  {
    // Check that allowed-attributes does not contain any attribute
    // also configured in prohibited-attributes
    for (String allowedAttr : configuration.getAllowedAttributes())
    {
      if (configuration.getProhibitedAttributes().contains(allowedAttr))
      {
        unacceptableReasons
            .add(ERR_CONFIG_NETWORKGROUPREQUESTFILTERINGPOLICY_INVALID_ATTRIBUTE
                .get(allowedAttr, configuration.dn().toString()));
        return false;
      }
    }
    // Check that allowed-subtrees does not contain any subtree also
    // configured in prohibited-subtrees
    for (DN allowedSubtree : configuration.getAllowedSubtrees())
    {
      if (configuration.getProhibitedSubtrees()
          .contains(allowedSubtree))
      {
        unacceptableReasons
            .add(ERR_CONFIG_NETWORKGROUPREQUESTFILTERINGPOLICY_INVALID_SUBTREE
                .get(allowedSubtree.toString(), configuration.dn()
                    .toString()));
        return false;
      }
    }
    return true;
  }
  /**
   * Creates a new request filtering policy factory.
   */
  public RequestFilteringPolicyFactory()
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  public RequestFilteringPolicy createQOSPolicy(
      RequestFilteringQOSPolicyCfg configuration)
      throws ConfigException, InitializationException
  {
    Policy policy = new Policy();
    // Save the configuration.
    policy.updateConfiguration(configuration);
    // Register change listener.
    configuration.addRequestFilteringChangeListener(policy);
    return policy;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAcceptable(
      RequestFilteringQOSPolicyCfg configuration,
      List<Message> unacceptableReasons)
  {
    return validateConfiguration(configuration, unacceptableReasons);
  }
}
opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStat.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStatistics.java
New file
@@ -0,0 +1,155 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.concurrent.atomic.AtomicLong;
/**
 * This class implements the statistics associated with a network group
 * request filtering policy.
 */
final class RequestFilteringPolicyStatistics
{
  private final AtomicLong rejectedAttributes = new AtomicLong();
  private final AtomicLong rejectedOperations = new AtomicLong();
  private final AtomicLong rejectedScopes = new AtomicLong();
  private final AtomicLong rejectedSubtrees = new AtomicLong();
  /**
   * Creates a new request filtering policy statistics.
   */
  RequestFilteringPolicyStatistics()
  {
    // Do nothing.
  }
  /**
   * Returns the number of rejected operations due to an attribute not
   * allowed by the request filtering policy.
   *
   * @return The number of rejected operations due to an invalid
   *         attribute.
   */
  long getRejectedAttributes()
  {
    return rejectedAttributes.get();
  }
  /**
   * Returns the number of rejected operations due to an operation type
   * not allowed by the request filtering policy.
   *
   * @return The number of rejected operations due to an invalid
   *         operation type.
   */
  long getRejectedOperations()
  {
    return rejectedOperations.get();
  }
  /**
   * Returns the number of rejected operations due to a scope not
   * allowed by the request filtering policy.
   *
   * @return The number of rejected operations due to an invalid scope.
   */
  long getRejectedScopes()
  {
    return rejectedScopes.get();
  }
  /**
   * Returns the number of rejected operations due to a subtree not
   * allowed by the request filtering policy.
   *
   * @return The number of rejected operations due to an invalid
   *         subtree.
   */
  long getRejectedSubtrees()
  {
    return rejectedSubtrees.get();
  }
  /**
   * Increments the number of rejected operations due to an attribute
   * not allowed by the request filtering policy.
   */
  void updateRejectedAttributes()
  {
    rejectedAttributes.incrementAndGet();
  }
  /**
   * Increments the number of rejected operations due to an operation
   * type not allowed by the request filtering policy.
   */
  void updateRejectedOperations()
  {
    rejectedOperations.incrementAndGet();
  }
  /**
   * Increments the number of rejected operations due to a search scope
   * not allowed by the request filtering policy.
   */
  void updateRejectedScopes()
  {
    rejectedScopes.incrementAndGet();
  }
  /**
   * Increments the number of rejected operations due to a subtree not
   * allowed by the request filtering policy.
   */
  void updateRejectedSubtrees()
  {
    rejectedSubtrees.incrementAndGet();
  }
}
opends/src/server/org/opends/server/core/networkgroups/ResourceLimits.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicy.java
New file
@@ -0,0 +1,135 @@
/*
 * 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
 *
 *
 *    Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.QOSPolicy;
import org.opends.server.types.operation.PreParseOperation;
/**
 * This class defines the resource limits policy applicable to all
 * connections inside the same network group.
 */
abstract class ResourceLimitsPolicy extends QOSPolicy
{
  /**
   * Creates a new resource limits policy.
   */
  protected ResourceLimitsPolicy()
  {
    // No implementation required.
  }
  /**
   * Adds a connection to the network group.
   *
   * @param connection
   *          The client connection.
   */
  abstract void addConnection(ClientConnection connection);
  /**
   * Returns the minimum string length for a substring filter.
   *
   * @return The minimum string length for a substring filter.
   */
  abstract int getMinSubstring();
  /**
   * Returns the default maximum number of entries that should be
   * returned for a searches processed by this network group.
   *
   * @return The default maximum number of entries that should be
   *         returned for a searches processed by this network group.
   */
  abstract int getSizeLimit();
  /**
   * Returns the statistics associated with this resource limits policy.
   *
   * @return The statistics associated with this resource limits policy.
   */
  abstract ResourceLimitsPolicyStatistics getStatistics();
  /**
   * Returns the maximum length of time in seconds permitted for a
   * search operation processed by this network group.
   *
   * @return The maximum length of time in seconds permitted for a
   *         search operation processed by this network group.
   */
  abstract int getTimeLimit();
  /**
   * Determines if the provided operation is allowed according to this
   * resource limits policy.
   *
   * @param connection
   *          the ClientConnection to check
   * @param operation
   *          the ongoing operation
   * @param fullCheck
   *          a boolean indicating if full checks must be done
   * @param messages
   *          the messages to include in the disconnect notification
   *          response. It may be <CODE>null</CODE> if no message is to
   *          be sent.
   * @return a boolean indicating whether the connection is allowed
   */
  abstract boolean isAllowed(ClientConnection connection,
      PreParseOperation operation, boolean fullCheck,
      List<Message> messages);
  /**
   * Removes a connection from the network group.
   *
   * @param connection
   *          The client connection to remove.
   */
  abstract void removeConnection(ClientConnection connection);
}
opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyFactory.java
New file
@@ -0,0 +1,496 @@
/*
 * 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
 *
 *
 *    Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import static org.opends.messages.CoreMessages.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.opends.messages.Message;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.ResourceLimitsQOSPolicyCfg;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.QOSPolicyFactory;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.InitializationException;
import org.opends.server.types.RawFilter;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PreParseOperation;
import org.opends.server.types.operation.PreParseSearchOperation;
/**
 * This class defines a factory for creating user configurable resource
 * limits policies.
 */
public final class ResourceLimitsPolicyFactory implements
    QOSPolicyFactory<ResourceLimitsQOSPolicyCfg>
{
  /**
   * Policy implementation.
   */
  private static final class Policy extends ResourceLimitsPolicy
      implements
      ConfigurationChangeListener<ResourceLimitsQOSPolicyCfg>
  {
    /**
     * {@inheritDoc}
     */
    public ConfigChangeResult applyConfigurationChange(
        ResourceLimitsQOSPolicyCfg configuration)
    {
      ResultCode resultCode = ResultCode.SUCCESS;
      boolean adminActionRequired = false;
      ArrayList<Message> messages = new ArrayList<Message>();
      // Save the configuration.
      updateConfiguration(configuration);
      return new ConfigChangeResult(resultCode, adminActionRequired,
          messages);
    }
    /**
     * {@inheritDoc}
     */
    public boolean isConfigurationChangeAcceptable(
        ResourceLimitsQOSPolicyCfg configuration,
        List<Message> unacceptableReasons)
    {
      return ResourceLimitsPolicyFactory.validateConfiguration(
          configuration, unacceptableReasons);
    }
    // Map containing the connections sorted by incoming IP address.
    private final HashMap<String, Integer> connectionsPerIPMap =
        new HashMap<String, Integer>();
    // The maximum number of concurrent operations per connection.
    private int maxConcurrentOpsPerConnection;
    // The maximum number of connections in the network group.
    private int maxConnections;
    // The maximum number of connections coming from the same IP
    // address.
    private int maxConnectionsFromSameIP;
    // The maximum number of operations per connection.
    private int maxOpsPerConnection;
    // The minimum substring length in a search.
    private int minSearchSubstringLength;
    // The lock for connections per IP map.
    private final Object mutex = new Object();
    // The maximum size for a search.
    private int sizeLimit;
    // The statistics for the resource limits policy.
    private final ResourceLimitsPolicyStatistics statistics =
        new ResourceLimitsPolicyStatistics();
    // The maximum time for a search.
    private int timeLimit;
    /**
     * Creates a new resource limits policy.
     */
    private Policy()
    {
      // Nothing to do.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    void addConnection(ClientConnection connection)
    {
      synchronized (mutex)
      {
        // Update the statistics.
        statistics.addClientConnection();
        // Increment the number of connections from the given IP
        // address.
        String ip = connection.getClientAddress();
        Integer currentCount = connectionsPerIPMap.get(ip);
        if (currentCount == null)
        {
          connectionsPerIPMap.put(ip, 1);
        }
        else
        {
          connectionsPerIPMap.put(ip, currentCount + 1);
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    int getMinSubstring()
    {
      return minSearchSubstringLength;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    int getSizeLimit()
    {
      return sizeLimit;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    ResourceLimitsPolicyStatistics getStatistics()
    {
      return statistics;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    int getTimeLimit()
    {
      return timeLimit;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    boolean isAllowed(ClientConnection connection,
        PreParseOperation operation, boolean fullCheck,
        List<Message> messages)
    {
      boolean result = true;
      if (fullCheck)
      {
        // Check the total number of connections in the resource group
        synchronized (mutex)
        {
          if (maxConnections > 0
              && statistics.getClientConnections() > maxConnections)
          {
            messages.add(INFO_ERROR_MAX_CONNECTIONS_LIMIT_EXCEEDED
                .get());
            result = false;
          }
        }
        if (!result)
        {
          return result;
        }
        // Check the number of connections coming from the same IP
        synchronized (mutex)
        {
          // Add the connection in the map
          String ip = connection.getClientAddress();
          Integer currentCount = connectionsPerIPMap.get(ip);
          if (currentCount == null)
          {
            currentCount = new Integer(0);
          }
          if (maxConnectionsFromSameIP > 0
              && currentCount.intValue() > maxConnectionsFromSameIP)
          {
            messages
                .add(INFO_ERROR_MAX_CONNECTIONS_FROM_SAME_IP_LIMIT_EXCEEDED
                    .get());
            result = false;
          }
        }
        if (!result)
        {
          return result;
        }
      }
      // Check the max number of operations per connection
      if (maxOpsPerConnection > 0
          && connection.getNumberOfOperations() > maxOpsPerConnection)
      {
        messages
            .add(INFO_ERROR_MAX_OPERATIONS_PER_CONNECTION_LIMIT_EXCEEDED
                .get());
        return false;
      }
      // Check the max number of concurrent operations per connection
      if (maxConcurrentOpsPerConnection > 0
          && connection.getOperationsInProgress().size()
          > maxConcurrentOpsPerConnection)
      {
        messages.add(
          INFO_ERROR_MAX_CONCURRENT_OPERATIONS_PER_CONNECTION_LIMIT_EXCEEDED
            .get());
        return false;
      }
      // If the operation is a search, check the min search substring
      // length
      if (operation != null
          && operation instanceof PreParseSearchOperation)
      {
        if (!checkSubstringFilter(((PreParseSearchOperation) operation)
            .getRawFilter()))
        {
          messages
              .add(INFO_ERROR_MIN_SEARCH_SUBSTRING_LENGTH_LIMIT_EXCEEDED
                  .get());
          return false;
        }
      }
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    void removeConnection(ClientConnection connection)
    {
      synchronized (mutex)
      {
        // Update the statistics.
        statistics.removeClientConnection();
        // Decrement the number of connections from the given IP
        // address.
        String ip = connection.getClientAddress();
        Integer currentCount = connectionsPerIPMap.get(ip);
        if (currentCount != null)
        {
          if (currentCount == 1)
          {
            // This was the last connection.
            connectionsPerIPMap.remove(ip);
          }
          else
          {
            connectionsPerIPMap.put(ip, currentCount - 1);
          }
        }
      }
    }
    /**
     * Checks whether a filter enforces minimum substring length. If the
     * filter is a composed filter (AND, OR, NOT filters), each
     * component of the filter is recursively checked. When the filter
     * is a substring filter, this routine checks that the substring
     * length is greater or equal to the minimum substring length. For
     * other search filter types, true is returned.
     *
     * @param filter
     *          The LDAP search filter to be tested
     * @return boolean indicating whether the filter conforms to the
     *         minimum substring length rule.
     */
    private boolean checkSubstringFilter(RawFilter filter)
    {
      switch (filter.getFilterType())
      {
      case AND:
      case OR:
        ArrayList<RawFilter> filterComponents =
            filter.getFilterComponents();
        if (filterComponents != null)
        {
          for (RawFilter element : filterComponents)
          {
            if (!checkSubstringFilter(element))
            {
              return false;
            }
          }
        }
        return true;
      case NOT:
        return checkSubstringFilter(filter.getNOTComponent());
      case SUBSTRING:
        int length = 0;
        ByteString subInitialElement = filter.getSubInitialElement();
        if (subInitialElement != null)
        {
          length += subInitialElement.stringValue().length();
        }
        ArrayList<ByteString> subAnyElements =
            filter.getSubAnyElements();
        if (subAnyElements != null)
        {
          for (ByteString element : subAnyElements)
          {
            length += element.stringValue().length();
          }
        }
        ByteString subFinalElement = filter.getSubFinalElement();
        if (subFinalElement != null)
        {
          length += subFinalElement.stringValue().length();
        }
        return length >= minSearchSubstringLength;
      default:
        return true;
      }
    }
    // Updates this policy's configuration.
    private void updateConfiguration(
        ResourceLimitsQOSPolicyCfg configuration)
    {
      maxConnections = configuration.getMaxConnections();
      maxConnectionsFromSameIP =
          configuration.getMaxConnectionsFromSameIP();
      maxOpsPerConnection = configuration.getMaxOpsPerConnection();
      maxConcurrentOpsPerConnection =
          configuration.getMaxConcurrentOpsPerConnection();
      Integer tmpSizeLimit = configuration.getSizeLimit();
      if (tmpSizeLimit != null)
      {
        sizeLimit = tmpSizeLimit;
      }
      else
      {
        sizeLimit = DirectoryServer.getSizeLimit();
      }
      Long tmpTimeLimit = configuration.getTimeLimit();
      if (tmpTimeLimit != null)
      {
        timeLimit = tmpTimeLimit.intValue();
      }
      else
      {
        timeLimit = DirectoryServer.getTimeLimit();
      }
      minSearchSubstringLength = configuration.getMinSubstringLength();
    }
  }
  // Validates a configuration.
  private static boolean validateConfiguration(
      ResourceLimitsQOSPolicyCfg configuration,
      List<Message> unacceptableReasons)
  {
    // Always valid.
    return true;
  }
  /**
   * Creates a new resource limits policy factory.
   */
  public ResourceLimitsPolicyFactory()
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  public ResourceLimitsPolicy createQOSPolicy(
      ResourceLimitsQOSPolicyCfg configuration) throws ConfigException,
      InitializationException
  {
    Policy policy = new Policy();
    // Save the configuration.
    policy.updateConfiguration(configuration);
    // Register change listener.
    configuration.addResourceLimitsChangeListener(policy);
    return policy;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationAcceptable(
      ResourceLimitsQOSPolicyCfg configuration,
      List<Message> unacceptableReasons)
  {
    return validateConfiguration(configuration, unacceptableReasons);
  }
}
opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyStatistics.java
New file
@@ -0,0 +1,121 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
/**
 * This class implements the statistics associated to a network group
 * resource limit.
 */
final class ResourceLimitsPolicyStatistics
{
  // Updates to these are protected by a mutex in the resource limits
  // policy.
  private long clientConnections = 0;
  private long maxClientConnections = 0;
  private long totalClientConnections = 0;
  /**
   * Creates a new resource limits statistics.
   */
  ResourceLimitsPolicyStatistics()
  {
    // Do nothing.
  }
  /**
   * Updates these statistics to reflect a new client connection being
   * added.
   */
  void addClientConnection()
  {
    clientConnections++;
    totalClientConnections++;
    if (clientConnections > maxClientConnections)
    {
      maxClientConnections = clientConnections;
    }
  }
  /**
   * Returns the number of client connections currently in the network
   * group.
   *
   * @return The number of client connections currently in the network
   *         group.
   */
  long getClientConnections()
  {
    return clientConnections;
  }
  /**
   * Returns the maximum number of simultaneous client connections in
   * the network group.
   *
   * @return The maximum number of simultaneous client connections in
   *         the network group.
   */
  long getMaxClientConnections()
  {
    return maxClientConnections;
  }
  /**
   * Returns the total number of client connections managed by the
   * network group since its creation.
   *
   * @return The total number of client connections managed by the
   *         network group since its creation.
   */
  long getTotalClientConnections()
  {
    return totalClientConnections;
  }
  /**
   * Updates these statistics to reflect an existing client connection
   * being closed.
   */
  void removeClientConnection()
  {
    clientConnections--;
  }
}
opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsStat.java
File was deleted
opends/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteria.java
New file
@@ -0,0 +1,99 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
/**
 * A connection criteria which matches connections which are secured
 * using SSL or TLS.
 */
final class SecurityConnectionCriteria implements ConnectionCriteria
{
  /**
   * A connection criteria which does not require a secured connection.
   */
  public static final SecurityConnectionCriteria SECURITY_NOT_REQUIRED =
      new SecurityConnectionCriteria(false);
  /**
   * A connection criteria which requires a secured connection.
   */
  public static final SecurityConnectionCriteria SECURITY_REQUIRED =
      new SecurityConnectionCriteria(true);
  // Indicates whether or not the connection must be secured.
  private final boolean mustBeSecured;
  /**
   * Creates a new security connection criteria.
   *
   * @param mustBeSecured
   *          Indicates whether or not the connection must be secured.
   */
  private SecurityConnectionCriteria(boolean mustBeSecured)
  {
    this.mustBeSecured = mustBeSecured;
  }
  /**
   * {@inheritDoc}
   */
  public boolean matches(ClientConnection connection)
  {
    return willMatchAfterBind(null, null, null, connection.isSecure());
  }
  /**
   * {@inheritDoc}
   */
  public boolean willMatchAfterBind(ClientConnection connection,
      DN bindDN, AuthenticationType authType, boolean isSecure)
  {
    if (mustBeSecured)
    {
      return isSecure;
    }
    else
    {
      return true;
    }
  }
}
opends/src/server/org/opends/server/core/networkgroups/SecurityCriteria.java
File was deleted
opends/src/server/org/opends/server/tools/dsconfig/CLIProfile.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -30,13 +30,14 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinitionResource;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
@@ -83,7 +84,11 @@
  public Set<String> getDefaultListPropertyNames(RelationDefinition<?, ?> r) {
    String s = resource.getString(r.getParentDefinition(), "relation."
        + r.getName() + ".list-properties");
    return new LinkedHashSet<String>(Arrays.asList(s.split(",")));
    if (s.trim().length() == 0) {
      return Collections.emptySet();
    } else {
      return new LinkedHashSet<String>(Arrays.asList(s.split(",")));
    }
  }
@@ -97,7 +102,7 @@
   * @return Returns the naming argument which should be used for a
   *         relation definition.
   */
  public String getNamingArgument(InstantiableRelationDefinition<?, ?> r) {
  public String getNamingArgument(RelationDefinition<?, ?> r) {
    String s = resource.getString(r.getParentDefinition(),
        "relation." + r.getName() + ".naming-argument-override").trim();
@@ -112,7 +117,16 @@
      } else {
        builder.append(s.substring(i + 1));
      }
      builder.append("-name");
      if (r instanceof SetRelationDefinition) {
        // Set relations are named using their type, so be consistent
        // with their associated create-xxx sub-command.
        builder.append("-type");
      } else {
        // Other relations (instantiable) are named by the user.
        builder.append("-name");
      }
      s = builder.toString();
    }
opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
@@ -35,13 +35,14 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.opends.messages.Message;
@@ -66,6 +67,7 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyProvider;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
@@ -397,6 +399,33 @@
  /**
   * Creates a new create-xxx sub-command for a sets
   * relation.
   *
   * @param <C>
   *          The type of managed object which can be created.
   * @param <S>
   *          The type of server managed object which can be created.
   * @param parser
   *          The sub-command argument parser.
   * @param p
   *          The parent managed object path.
   * @param r
   *          The set relation.
   * @return Returns the new create-xxx sub-command.
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static <C extends ConfigurationClient, S extends Configuration>
      CreateSubCommandHandler<C, S> create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      SetRelationDefinition<C, S> r) throws ArgumentException {
    return new CreateSubCommandHandler<C, S>(parser, p, r, null, p.child(r));
  }
  /**
   * Creates a new create-xxx sub-command for an optional relation.
   *
   * @param <C>
@@ -499,7 +528,7 @@
    // First determine what type of component the user wants to create.
    MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result;
    result = getTypeInteractively(app, d);
    result = getTypeInteractively(app, d, Collections.<String>emptySet());
    ManagedObjectDefinition<? extends C, ? extends S> mod;
    if (result.isSuccess()) {
@@ -913,78 +942,56 @@
  // Generate the type name - definition mapping table.
  @SuppressWarnings("unchecked")
  private static <C extends ConfigurationClient, S extends Configuration>
  SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>>
      getSubTypes(AbstractManagedObjectDefinition<C, S> d) {
    SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> map;
    map =
      new TreeMap<String, ManagedObjectDefinition<? extends C, ? extends S>>();
    // If the top-level definition is instantiable, we use the value
    // "generic" or "custom".
    if (!d.hasOption(ManagedObjectOption.HIDDEN)) {
      if (d instanceof ManagedObjectDefinition) {
        ManagedObjectDefinition<? extends C, ? extends S> mod =
          (ManagedObjectDefinition<? extends C, ? extends S>) d;
        if (CLIProfile.getInstance().isForCustomization(mod)) {
          map.put(DSConfig.CUSTOM_TYPE, mod);
        } else {
          map.put(DSConfig.GENERIC_TYPE, mod);
        }
      }
    }
    // Process its sub-definitions.
    String suffix = "-" + d.getName();
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      if (d.hasOption(ManagedObjectOption.HIDDEN)) {
        continue;
      }
      if (c instanceof ManagedObjectDefinition) {
        ManagedObjectDefinition<? extends C, ? extends S> mod =
          (ManagedObjectDefinition<? extends C, ? extends S>) c;
        // For the type name we shorten it, if possible, by stripping
        // off the trailing part of the name which matches the
        // base-type.
        String name = mod.getName();
        if (name.endsWith(suffix)) {
          name = name.substring(0, name.length() - suffix.length());
        }
        // If this type is intended for customization, prefix it with
        // "custom".
        if (CLIProfile.getInstance().isForCustomization(mod)) {
          name = String.format("%s-%s", DSConfig.CUSTOM_TYPE, name);
        }
        map.put(name, mod);
      }
    }
    return map;
  }
  // Interactively ask the user which type of component they want to create.
  private static <C extends ConfigurationClient, S extends Configuration>
      MenuResult<ManagedObjectDefinition<? extends C, ? extends S>>
      getTypeInteractively(ConsoleApplication app,
          AbstractManagedObjectDefinition<C, S> d) throws CLIException {
    Collection<ManagedObjectDefinition<? extends C, ? extends S>> types;
    types = getSubTypes(d).values();
          AbstractManagedObjectDefinition<C, S> d,
          Set<String> prohibitedTypes) throws CLIException {
    // First get the list of available of sub-types.
    List<ManagedObjectDefinition<? extends C, ? extends S>> filteredTypes =
      new LinkedList<ManagedObjectDefinition<? extends C,? extends S>>(
          getSubTypes(d).values());
    boolean isOnlyOneType = filteredTypes.size() == 1;
    Iterator<ManagedObjectDefinition<? extends C,? extends S>> i;
    for (i = filteredTypes.iterator() ; i.hasNext();) {
      ManagedObjectDefinition<? extends C,? extends S> cd = i.next();
      if (prohibitedTypes.contains(cd.getName())) {
        // Remove filtered types.
        i.remove();
      } else if (!app.isAdvancedMode()) {
        // Only display advanced types and custom types in advanced mode.
        if (cd.hasOption(ManagedObjectOption.ADVANCED)) {
          i.remove();
        } else if (CLIProfile.getInstance().isForCustomization(cd)) {
          i.remove();
        }
      }
    }
    // If there is only one choice then return immediately.
    if (types.size() == 1) {
      ManagedObjectDefinition<? extends C, ? extends S> type =
        types.iterator().next();
    if (filteredTypes.size() == 0) {
      Message msg =
        ERR_DSCFG_ERROR_NO_AVAILABLE_TYPES.get(d.getUserFriendlyName());
      app.println(msg);
      return MenuResult.<ManagedObjectDefinition<? extends C,
          ? extends S>>success(type);
          ? extends S>>cancel();
    } else if (filteredTypes.size() == 1) {
      ManagedObjectDefinition<? extends C, ? extends S> type =
        filteredTypes.iterator().next();
      if (!isOnlyOneType) {
        // Only one option available so confirm that the user wishes to
        // use it.
        Message msg = INFO_DSCFG_TYPE_PROMPT_SINGLE.get(
            d.getUserFriendlyName(), type.getUserFriendlyName());
        if (!app.confirmAction(msg, true)) {
          return MenuResult.cancel();
        }
      }
      return MenuResult.<ManagedObjectDefinition<? extends C, ? extends S>>
        success(type);
    } else {
      MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>> builder =
        new MenuBuilder<ManagedObjectDefinition<? extends C, ? extends S>>(app);
@@ -992,17 +999,8 @@
      builder.setMultipleColumnThreshold(MULTI_COLUMN_THRESHOLD);
      builder.setPrompt(msg);
      for (ManagedObjectDefinition<? extends C, ? extends S> mod : types) {
        // Only display advanced types and custom types in advanced mode.
        if (!app.isAdvancedMode()) {
          if (mod.hasOption(ManagedObjectOption.ADVANCED)) {
            continue;
          }
          if (CLIProfile.getInstance().isForCustomization(mod)) {
            continue;
          }
        }
      for (ManagedObjectDefinition<? extends C, ? extends S> mod :
        filteredTypes) {
        Message option = mod.getUserFriendlyName();
        if (CLIProfile.getInstance().isForCustomization(mod)) {
@@ -1083,6 +1081,9 @@
    // Create the naming arguments.
    this.namingArgs = createNamingArgs(subCommand, c, true);
    // Build the -t option usage.
    this.typeUsage = getSubTypesUsage(r.getChildDefinition());
    // Create the --property argument which is used to specify
    // property values.
    this.propertySetArgument = new StringArgument(OPTION_DSCFG_LONG_SET,
@@ -1091,18 +1092,6 @@
        INFO_DSCFG_DESCRIPTION_PROP_VAL.get());
    this.subCommand.addArgument(this.propertySetArgument);
    // Build the -t option usage.
    StringBuilder builder = new StringBuilder();
    boolean isFirst = true;
    for (String s : types.keySet()) {
      if (!isFirst) {
        builder.append(" | ");
      }
      builder.append(s);
      isFirst = false;
    }
    this.typeUsage = builder.toString();
    if (!types.containsKey(DSConfig.GENERIC_TYPE)) {
      // The option is mandatory when non-interactive.
      this.typeArgument = new StringArgument("type", OPTION_DSCFG_SHORT_TYPE,
@@ -1163,43 +1152,7 @@
  public MenuResult<Integer> run(ConsoleApplication app,
      ManagementContextFactory factory) throws ArgumentException,
      ClientException, CLIException {
    // Determine the type of managed object to be created.
    ManagedObjectDefinition<? extends C, ? extends S> d;
    if (!typeArgument.isPresent()) {
      if (app.isInteractive()) {
        // Let the user choose.
        MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> result;
        app.println();
        app.println();
        result = getTypeInteractively(app, relation.getChildDefinition());
        if (result.isSuccess()) {
          d = result.getValue();
        } else if (result.isCancel()) {
          return MenuResult.cancel();
        } else {
          // Must be quit.
          if (!app.isMenuDrivenMode()) {
            app.printVerboseMessage(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(relation
                .getUserFriendlyName()));
          }
          return MenuResult.quit();
        }
      } else if (typeArgument.getDefaultValue() != null) {
        d = types.get(typeArgument.getDefaultValue());
      } else {
        throw ArgumentExceptionFactory
            .missingMandatoryNonInteractiveArgument(typeArgument);
      }
    } else {
      d = types.get(typeArgument.getValue());
      if (d == null) {
        throw ArgumentExceptionFactory.unknownSubType(relation, typeArgument
            .getValue(), typeUsage);
      }
    }
    Message ufn = d.getUserFriendlyName();
    Message ufn = relation.getUserFriendlyName();
    // Get the naming argument values.
    List<String> names = getNamingArgValues(app, namingArgs);
@@ -1212,11 +1165,6 @@
    // Update the command builder.
    updateCommandBuilderWithSubCommand();
    // Encode the provided properties.
    List<String> propertyArgs = propertySetArgument.getValues();
    MyPropertyProvider provider = new MyPropertyProvider(d,
        namingPropertyDefinition, propertyArgs);
    // Add the child managed object.
    ManagementContext context = factory.getManagementContext(app);
    MenuResult<ManagedObject<?>> result;
@@ -1265,6 +1213,82 @@
    }
    ManagedObject<?> parent = result.getValue();
    // Determine the type of managed object to be created. If we are creating
    // a managed object beneath a set relation then prevent creation of
    // duplicates.
    Set<String> prohibitedTypes;
    if (relation instanceof SetRelationDefinition) {
      SetRelationDefinition<C, S> sr = (SetRelationDefinition<C, S>) relation;
      prohibitedTypes = new HashSet<String>();
      try
      {
        for (String child : parent.listChildren(sr)) {
          prohibitedTypes.add(child);
        }
      }
      catch (AuthorizationException e)
      {
        Message msg = ERR_DSCFG_ERROR_CREATE_AUTHZ.get(ufn);
        throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
            msg);
      }
      catch (ConcurrentModificationException e)
      {
        Message msg = ERR_DSCFG_ERROR_CREATE_CME.get(ufn);
        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
      }
      catch (CommunicationException e)
      {
        Message msg = ERR_DSCFG_ERROR_CREATE_CE.get(ufn, e
            .getMessage());
        throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN, msg);
      }
    } else {
      // No prohibited types.
      prohibitedTypes = Collections.emptySet();
    }
    ManagedObjectDefinition<? extends C, ? extends S> d;
    if (!typeArgument.isPresent()) {
      if (app.isInteractive()) {
        // Let the user choose.
        MenuResult<ManagedObjectDefinition<? extends C, ? extends S>> dresult;
        app.println();
        app.println();
        dresult = getTypeInteractively(app, relation.getChildDefinition(),
            prohibitedTypes);
        if (dresult.isSuccess()) {
          d = dresult.getValue();
        } else if (dresult.isCancel()) {
          return MenuResult.cancel();
        } else {
          // Must be quit.
          if (!app.isMenuDrivenMode()) {
            app.printVerboseMessage(INFO_DSCFG_CONFIRM_CREATE_FAIL.get(ufn));
          }
          return MenuResult.quit();
        }
      } else if (typeArgument.getDefaultValue() != null) {
        d = types.get(typeArgument.getDefaultValue());
      } else {
        throw ArgumentExceptionFactory
            .missingMandatoryNonInteractiveArgument(typeArgument);
      }
    } else {
      d = types.get(typeArgument.getValue());
      if (d == null) {
        throw ArgumentExceptionFactory.unknownSubType(relation, typeArgument
            .getValue(), typeUsage);
      }
    }
    // Encode the provided properties.
    List<String> propertyArgs = propertySetArgument.getValues();
    MyPropertyProvider provider = new MyPropertyProvider(d,
        namingPropertyDefinition, propertyArgs);
    ManagedObject<? extends C> child;
    List<DefaultBehaviorException> exceptions =
      new LinkedList<DefaultBehaviorException>();
@@ -1297,6 +1321,10 @@
              .adaptIllegalManagedObjectNameException(e, d);
        }
      }
    } else if (relation instanceof SetRelationDefinition) {
      SetRelationDefinition<C, S> srelation =
        (SetRelationDefinition<C, S>) relation;
      child = parent.createChild(srelation, d, exceptions);
    } else {
      OptionalRelationDefinition<C, S> orelation =
        (OptionalRelationDefinition<C, S>) relation;
opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
@@ -56,6 +56,7 @@
import org.opends.server.admin.ClassPropertyDefinition;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.Tag;
import org.opends.server.admin.client.ManagedObjectDecodingException;
import org.opends.server.admin.client.MissingMandatoryPropertiesException;
@@ -181,6 +182,10 @@
        InstantiableRelationDefinition<?, ?> ir =
          (InstantiableRelationDefinition<?, ?>) rd;
        ufpn = ir.getUserFriendlyPluralName();
      } else if (rd instanceof SetRelationDefinition) {
        SetRelationDefinition<?, ?> sr =
          (SetRelationDefinition<?, ?>) rd;
        ufpn = sr.getUserFriendlyPluralName();
      }
      MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
@@ -467,6 +472,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isAdvancedMode() {
    return advancedModeArgument.isPresent();
  }
@@ -476,6 +482,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isInteractive() {
    return !noPromptArgument.isPresent();
  }
@@ -495,6 +502,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isQuiet() {
    return quietArgument.isPresent();
  }
@@ -504,6 +512,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isScriptFriendly() {
    return scriptFriendlyArgument.isPresent();
  }
@@ -513,6 +522,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isVerbose() {
    return verboseArgument.isPresent();
  }
opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -31,14 +31,17 @@
import static org.opends.messages.DSConfigMessages.*;
import java.util.List;
import java.util.SortedMap;
import org.opends.messages.Message;
import org.opends.server.admin.DefinitionDecodingException;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
@@ -122,6 +125,29 @@
    return new DeleteSubCommandHandler(parser, p, r, p.child(r));
  }
  /**
   * Creates a new delete-xxx sub-command for a set relation.
   *
   * @param parser
   *          The sub-command argument parser.
   * @param p
   *          The parent managed object path.
   * @param r
   *          The set relation.
   * @return Returns the new delete-xxx sub-command.
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static DeleteSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      SetRelationDefinition<?, ?> r) throws ArgumentException {
    return new DeleteSubCommandHandler(parser, p, r, p.child(r));
  }
  // The argument which should be used to force deletion.
  private final BooleanArgument forceArgument;
@@ -255,14 +281,13 @@
    ManagedObject<?> parent = result.getValue();
    try {
      if (relation instanceof InstantiableRelationDefinition) {
        InstantiableRelationDefinition<?, ?> irelation =
          (InstantiableRelationDefinition<?, ?>) relation;
      if (relation instanceof InstantiableRelationDefinition
          || relation instanceof SetRelationDefinition) {
        String childName = names.get(names.size() - 1);
        if (childName == null) {
          MenuResult<String> sresult =
            readChildName(app, parent, irelation, null);
            readChildName(app, parent, relation, null);
          if (sresult.isQuit()) {
            if (!app.isMenuDrivenMode()) {
@@ -277,11 +302,32 @@
          } else {
            childName = sresult.getValue();
          }
        } else if (relation instanceof SetRelationDefinition) {
          // The provided type short name needs mapping to the full name.
          String name = childName.trim();
          SortedMap types = getSubTypes(relation.getChildDefinition());
          ManagedObjectDefinition cd =
            (ManagedObjectDefinition) types.get(name);
          if (cd == null) {
            // The name must be invalid.
            String typeUsage = getSubTypesUsage(relation.getChildDefinition());
            Message msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED.get(
                name, relation.getUserFriendlyName(), typeUsage);
            throw new ArgumentException(msg);
          } else {
            childName = cd.getName();
          }
        }
        if (confirmDeletion(app)) {
          setCommandBuilderUseful(true);
          parent.removeChild(irelation, childName);
          if (relation instanceof InstantiableRelationDefinition) {
            parent.removeChild((InstantiableRelationDefinition<?,?>) relation,
                childName);
          } else {
            parent.removeChild((SetRelationDefinition<?,?>) relation,
                childName);
          }
        } else {
          return MenuResult.cancel();
        }
opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -52,6 +52,7 @@
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelativeInheritedDefaultBehaviorProvider;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.client.AuthorizationException;
@@ -128,6 +129,27 @@
  /**
   * Creates a new get-xxx-prop sub-command for a set relation.
   *
   * @param parser
   *          The sub-command argument parser.
   * @param path
   *          The parent managed object path.
   * @param r
   *          The set relation.
   * @return Returns the new get-xxx-prop sub-command.
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static GetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, ?> r) throws ArgumentException {
    return new GetPropSubCommandHandler(parser, path.child(r), r);
  }
  /**
   * Creates a new get-xxx-prop sub-command for a singleton relation.
   *
   * @param parser
opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
@@ -21,7 +21,6 @@
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -47,6 +46,7 @@
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
@@ -100,6 +100,28 @@
  /**
   * Creates a new list-xxx sub-command for a set relation.
   *
   * @param parser
   *          The sub-command argument parser.
   * @param p
   *          The parent managed object path.
   * @param r
   *          The set relation.
   * @return Returns the new list-xxx sub-command.
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static ListSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> p,
      SetRelationDefinition<?, ?> r) throws ArgumentException {
    return new ListSubCommandHandler(parser, p, r, r.getPluralName(), r
        .getUserFriendlyPluralName());
  }
  /**
   * Creates a new list-xxx sub-command for an optional relation.
   *
   * @param parser
@@ -216,6 +238,10 @@
      InstantiableRelationDefinition<?, ?> irelation =
        (InstantiableRelationDefinition<?, ?>) relation;
      ufn = irelation.getUserFriendlyPluralName();
    } else if (relation instanceof SetRelationDefinition) {
      SetRelationDefinition<?, ?> srelation =
        (SetRelationDefinition<?, ?>) relation;
      ufn = srelation.getUserFriendlyPluralName();
    } else {
      ufn = relation.getUserFriendlyName();
    }
@@ -291,6 +317,39 @@
        throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
            msg);
      }
    } else if (relation instanceof SetRelationDefinition) {
      SetRelationDefinition<?, ?> srelation =
        (SetRelationDefinition<?, ?>) relation;
      try {
        for (String s : parent.listChildren(srelation)) {
          try {
            children.put(s, parent.getChild(srelation, s));
          } catch (ManagedObjectNotFoundException e) {
            // Ignore - as it's been removed since we did the list.
          }
        }
      } catch (DefinitionDecodingException e) {
        // FIXME: just output this as a warnings (incl. the name) but
        // continue.
        Message msg = ERR_DSCFG_ERROR_LIST_DDE.get(ufn, ufn, ufn);
        throw new ClientException(LDAPResultCode.OTHER, msg);
      } catch (ManagedObjectDecodingException e) {
        // FIXME: just output this as a warnings (incl. the name) but
        // continue.
        Message msg = ERR_DSCFG_ERROR_LIST_MODE.get(ufn);
        throw new ClientException(LDAPResultCode.OTHER, msg, e);
      } catch (AuthorizationException e) {
        Message msg = ERR_DSCFG_ERROR_LIST_AUTHZ.get(ufn);
        throw new ClientException(LDAPResultCode.INSUFFICIENT_ACCESS_RIGHTS,
            msg);
      } catch (ConcurrentModificationException e) {
        Message msg = ERR_DSCFG_ERROR_LIST_CME.get(ufn);
        throw new ClientException(LDAPResultCode.CONSTRAINT_VIOLATION, msg);
      } catch (CommunicationException e) {
        Message msg = ERR_DSCFG_ERROR_LIST_CE.get(ufn, e.getMessage());
        throw new ClientException(LDAPResultCode.CLIENT_SIDE_SERVER_DOWN,
            msg);
      }
    } else if (relation instanceof OptionalRelationDefinition) {
      OptionalRelationDefinition<?, ?> orelation =
        (OptionalRelationDefinition<?, ?>) relation;
@@ -363,7 +422,7 @@
      }
      builder.addSortKey(0);
      String baseType = relation.getName();
      String baseType = relation.getChildDefinition().getName();
      String typeSuffix = "-" + baseType;
      for (String name : children.keySet()) {
        ManagedObject<?> child = children.get(name);
@@ -382,7 +441,11 @@
        // First output the name.
        builder.startRow();
        builder.appendCell(name);
        if (relation instanceof SetRelationDefinition) {
          builder.appendCell(d.getUserFriendlyName());
        } else {
          builder.appendCell(name);
        }
        // Output the managed object type in the form used in
        // create-xxx commands.
opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
@@ -53,6 +53,7 @@
import org.opends.server.admin.PropertyException;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.UndefinedDefaultBehaviorProvider;
import org.opends.server.admin.client.AuthorizationException;
@@ -192,6 +193,27 @@
  /**
   * Creates a new set-xxx-prop sub-command for a set relation.
   *
   * @param parser
   *          The sub-command argument parser.
   * @param path
   *          The parent managed object path.
   * @param r
   *          The set relation.
   * @return Returns the new set-xxx-prop sub-command.
   * @throws ArgumentException
   *           If the sub-command could not be created successfully.
   */
  public static SetPropSubCommandHandler create(
      SubCommandArgumentParser parser, ManagedObjectPath<?, ?> path,
      SetRelationDefinition<?, ?> r) throws ArgumentException {
    return new SetPropSubCommandHandler(parser, path.child(r), r);
  }
  /**
   * Creates a new set-xxx-prop sub-command for a singleton relation.
   *
   * @param parser
@@ -429,7 +451,6 @@
                      {
                        String argName =
                          CLIProfile.getInstance().getNamingArgument(
                              (InstantiableRelationDefinition<?, ?>)
                              path.getRelationDefinition());
                        try
                        {
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2007-2008 Sun Microsystems, Inc.
 *      Copyright 2007-2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -40,10 +40,10 @@
import java.util.LinkedHashSet;
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 java.util.SortedMap;
import java.util.TreeMap;
import org.opends.messages.Message;
import org.opends.server.admin.AbstractManagedObjectDefinition;
import org.opends.server.admin.Configuration;
@@ -59,6 +59,8 @@
import org.opends.server.admin.OptionalRelationDefinition;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyDefinitionUsageBuilder;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.admin.SizeUnit;
import org.opends.server.admin.Tag;
@@ -256,6 +258,100 @@
     */
    public <C extends ConfigurationClient, S extends Configuration>
        void appendManagedObjectPathElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      if (result.isSuccess()) {
        // We should ignore the "template" name here and use a path
        // argument.
        String childName = args.get(argIndex++);
        try {
          // If the name is null then we must be interactive - so let
          // the user choose.
          if (childName == null) {
            try {
              MenuResult<String> sresult = readChildName(app,
                  result.getValue(), r, d);
              if (sresult.isCancel()) {
                result = MenuResult.cancel();
                return;
              } else if (sresult.isQuit()) {
                result = MenuResult.quit();
                return;
              } else {
                childName = sresult.getValue();
              }
            } catch (CLIException e) {
              clie = e;
              result = MenuResult.quit();
              return;
            }
          } else if (childName.trim().length() == 0) {
            IllegalManagedObjectNameException e =
              new IllegalManagedObjectNameException(childName);
            clie = ArgumentExceptionFactory
                .adaptIllegalManagedObjectNameException(e, d);
            result = MenuResult.quit();
            return;
          } else {
            String name = childName.trim();
            SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>>
              types = getSubTypes(d);
            ManagedObjectDefinition<?, ?> cd = types.get(name);
            if (cd == null) {
              // The name must be invalid.
              String typeUsage = getSubTypesUsage(d);
              Message msg = ERR_DSCFG_ERROR_SUB_TYPE_UNRECOGNIZED.get(
                  name, r.getUserFriendlyName(), typeUsage);
              clie = new CLIException(msg);
              result = MenuResult.quit();
              return;
            } else {
              childName = cd.getName();
            }
          }
          ManagedObject<?> child = result.getValue().getChild(r, childName);
          // Check that child is a sub-type of the specified
          // definition.
          if (!child.getManagedObjectDefinition().isChildOf(d)) {
            clie = ArgumentExceptionFactory.wrongManagedObjectType(r, child
                .getManagedObjectDefinition(), getSubCommand().getName());
            result = MenuResult.quit();
          } else {
            result = MenuResult.<ManagedObject<?>>success(child);
          }
        } catch (DefinitionDecodingException e) {
          dde = e;
          result = MenuResult.quit();
        } catch (ManagedObjectDecodingException e) {
          mode = e;
          result = MenuResult.quit();
        } catch (AuthorizationException e) {
          authze = e;
          result = MenuResult.quit();
        } catch (ManagedObjectNotFoundException e) {
          monfe = e;
          result = MenuResult.quit();
        } catch (ConcurrentModificationException e) {
          cme = e;
          result = MenuResult.quit();
        } catch (CommunicationException e) {
          ce = e;
          result = MenuResult.quit();
        }
      }
    }
    /**
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
        void appendManagedObjectPathElement(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      if (result.isSuccess()) {
@@ -513,6 +609,38 @@
     */
    public <C extends ConfigurationClient, S extends Configuration>
        void appendManagedObjectPathElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      sz--;
      // The name of the managed object is determined from its type, so
      // we don't need this argument.
      if (isCreate && sz == 0) {
        return;
      }
      String argName = CLIProfile.getInstance().getNamingArgument(r);
      StringArgument arg;
      try {
        arg =
            new StringArgument(argName, null, argName, false, true,
                INFO_NAME_PLACEHOLDER.get(),
                INFO_DSCFG_DESCRIPTION_NAME.get(d.getUserFriendlyName()));
        subCommand.addArgument(arg);
        arguments.add(arg);
      } catch (ArgumentException e) {
        this.e = e;
      }
    }
    /**
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
        void appendManagedObjectPathElement(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      sz--;
@@ -997,21 +1125,22 @@
   * @param parent
   *          The parent managed object.
   * @param r
   *          The relation between the parent and the children.
   *          The relation between the parent and the children, must be
   *          a set or instantiable relation.
   * @param d
   *          The type of child managed object to choose from.
   * @return Returns a {@link MenuResult#success()} containing the
   *         name of the managed object that the user selected, or
   *         {@link MenuResult#quit()}, or
   *         {@link MenuResult#cancel()}, if the sub-command was run
   *         interactive and the user chose to quit or cancel.
   * @return Returns a {@link MenuResult#success()} containing the name
   *         of the managed object that the user selected, or
   *         {@link MenuResult#quit()}, or {@link MenuResult#cancel()},
   *         if the sub-command was run interactive and the user chose
   *         to quit or cancel.
   * @throws CommunicationException
   *           If the server cannot be contacted.
   * @throws ConcurrentModificationException
   *           If the parent managed object has been deleted.
   * @throws AuthorizationException
   *           If the children cannot be listed due to an
   *           authorization failure.
   *           If the children cannot be listed due to an authorization
   *           failure.
   * @throws CLIException
   *           If the user input can be read from the console or if
   *           there are no children.
@@ -1019,7 +1148,7 @@
  protected final <C extends ConfigurationClient, S extends Configuration>
  MenuResult<String> readChildName(
      ConsoleApplication app, ManagedObject<?> parent,
      InstantiableRelationDefinition<C, S> r,
      RelationDefinition<C, S> r,
      AbstractManagedObjectDefinition<? extends C, ? extends S> d)
      throws AuthorizationException, ConcurrentModificationException,
      CommunicationException, CLIException {
@@ -1031,14 +1160,25 @@
    app.println();
    // Filter out advanced and hidden types if required.
    String[] childNames = parent.listChildren(r, d);
    SortedSet<String> children = new TreeSet<String>(
    String[] childNames;
    if (r instanceof InstantiableRelationDefinition) {
      childNames =
        parent.listChildren((InstantiableRelationDefinition<C,S>)r, d);
    } else {
      childNames = parent.listChildren((SetRelationDefinition<C,S>)r, d);
    }
    SortedMap<String, String> children = new TreeMap<String, String>(
        String.CASE_INSENSITIVE_ORDER);
    for (String childName : childNames) {
      ManagedObject<?> child;
      try {
        child = parent.getChild(r, childName);
        if (r instanceof InstantiableRelationDefinition) {
          child = parent.getChild((InstantiableRelationDefinition<C,S>)r,
              childName);
        } else {
          child = parent.getChild((SetRelationDefinition<C,S>)r, childName);
        }
        ManagedObjectDefinition<?, ?> cd = child.getManagedObjectDefinition();
@@ -1051,13 +1191,18 @@
          continue;
        }
        children.add(childName);
        if (r instanceof InstantiableRelationDefinition) {
          children.put(childName, childName);
        } else {
          // For sets the RDN is the type string, the ufn is more friendly.
          children.put(cd.getUserFriendlyName().toString(), childName);
        }
      } catch (DefinitionDecodingException e) {
        // Add it anyway: maybe the user is trying to fix the problem.
        children.add(childName);
        children.put(childName, childName);
      } catch (ManagedObjectDecodingException e) {
        // Add it anyway: maybe the user is trying to fix the problem.
        children.add(childName);
        children.put(childName, childName);
      } catch (ManagedObjectNotFoundException e) {
        // Skip it - the managed object has been concurrently removed.
      }
@@ -1075,7 +1220,7 @@
      // Only one option available so confirm that the user wishes to
      // access it.
      Message msg = INFO_DSCFG_FINDER_PROMPT_SINGLE.get(
          d.getUserFriendlyName(), children.first());
          d.getUserFriendlyName(), children.firstKey());
      if (app.confirmAction(msg, true)) {
        try
        {
@@ -1083,7 +1228,18 @@
          StringArgument arg = new StringArgument(argName, null, argName, false,
              true, INFO_NAME_PLACEHOLDER.get(),
              INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()));
          arg.addValue(children.first());
          if (r instanceof InstantiableRelationDefinition) {
            arg.addValue(children.get(children.firstKey()));
          } else {
            // Set relation: need the short type name.
            String longName = children.firstKey();
            try {
              AbstractManagedObjectDefinition<?,?> cd = d.getChild(longName);
              arg.addValue(getShortTypeName(r.getChildDefinition(), cd));
            } catch (IllegalArgumentException e) {
              arg.addValue(children.get(children.firstKey()));
            }
          }
          getCommandBuilder().addArgument(arg);
        }
        catch (Throwable t)
@@ -1091,7 +1247,7 @@
          // Bug
          new RuntimeException("Unexpected exception: "+t, t);
        }
        return MenuResult.success(children.first());
        return MenuResult.success(children.get(children.firstKey()));
      } else {
        return MenuResult.cancel();
      }
@@ -1103,9 +1259,9 @@
      builder.setPrompt(INFO_DSCFG_FINDER_PROMPT_MANY.get(d
          .getUserFriendlyName()));
      for (String child : children) {
        Message option = Message.raw("%s", child);
        builder.addNumberedOption(option, MenuResult.success(child));
      for (Map.Entry<String, String> child : children.entrySet()) {
        Message option = Message.raw("%s", child.getKey());
        builder.addNumberedOption(option, MenuResult.success(child.getValue()));
      }
      if (app.isMenuDrivenMode()) {
@@ -1121,7 +1277,18 @@
        StringArgument arg = new StringArgument(argName, null, argName, false,
            true, INFO_NAME_PLACEHOLDER.get(),
            INFO_DSCFG_DESCRIPTION_NAME_CREATE.get(d.getUserFriendlyName()));
        arg.addValue(result.getValue());
        if (r instanceof InstantiableRelationDefinition) {
          arg.addValue(result.getValue());
        } else {
          // Set relation: need the short type name.
          String longName = result.getValue();
          try {
            AbstractManagedObjectDefinition<?, ?> cd = d.getChild(longName);
            arg.addValue(getShortTypeName(r.getChildDefinition(), cd));
          } catch (IllegalArgumentException e) {
            arg.addValue(children.get(result.getValue()));
          }
        }
        getCommandBuilder().addArgument(arg);
      }
      catch (Throwable t)
@@ -1248,4 +1415,131 @@
    }
    return value;
  }
  /**
   * Returns a mapping of subordinate managed object type argument
   * values to their corresponding managed object definitions for the
   * provided managed object definition.
   *
   * @param <C>
   *          The type of client configuration.
   * @param <S>
   *          The type of server configuration.
   * @param d
   *          The managed object definition.
   * @return A mapping of managed object type argument values to their
   *         corresponding managed object definitions.
   */
  @SuppressWarnings("unchecked")
  protected static <C extends ConfigurationClient, S extends Configuration>
  SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>>
      getSubTypes(AbstractManagedObjectDefinition<C, S> d) {
    SortedMap<String, ManagedObjectDefinition<? extends C, ? extends S>> map;
    map =
      new TreeMap<String, ManagedObjectDefinition<? extends C, ? extends S>>();
    // If the top-level definition is instantiable, we use the value
    // "generic" or "custom".
    if (!d.hasOption(ManagedObjectOption.HIDDEN)) {
      if (d instanceof ManagedObjectDefinition) {
        ManagedObjectDefinition<? extends C, ? extends S> mod =
          (ManagedObjectDefinition<? extends C, ? extends S>) d;
        map.put(getShortTypeName(d, mod), mod);
      }
    }
    // Process its sub-definitions.
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      if (d.hasOption(ManagedObjectOption.HIDDEN)) {
        continue;
      }
      if (c instanceof ManagedObjectDefinition) {
        ManagedObjectDefinition<? extends C, ? extends S> mod =
          (ManagedObjectDefinition<? extends C, ? extends S>) c;
        map.put(getShortTypeName(d, mod), mod);
      }
    }
    return map;
  }
  /**
   * Returns the type short name for a child managed object definition.
   *
   * @param <C>
   *          The type of client configuration.
   * @param <S>
   *          The type of server configuration.
   * @param d
   *          The top level parent definition.
   * @param c
   *          The child definition.
   * @return The type short name.
   */
  protected static  <C extends ConfigurationClient, S extends Configuration>
      String getShortTypeName(
      AbstractManagedObjectDefinition<C,S> d,
      AbstractManagedObjectDefinition<?, ?> c) {
    if (c == d) {
      // This is the top-level definition, so use the value "generic" or
      // "custom".
      if (CLIProfile.getInstance().isForCustomization(c)) {
        return DSConfig.CUSTOM_TYPE;
      } else {
        return DSConfig.GENERIC_TYPE;
      }
    } else {
      // It's a child definition.
      String suffix = "-" + d.getName();
      // For the type name we shorten it, if possible, by stripping
      // off the trailing part of the name which matches the
      // base-type.
      String name = c.getName();
      if (name.endsWith(suffix)) {
        name = name.substring(0, name.length() - suffix.length());
      }
      // If this type is intended for customization, prefix it with
      // "custom".
      if (CLIProfile.getInstance().isForCustomization(c)) {
        name = String.format("%s-%s", DSConfig.CUSTOM_TYPE, name);
      }
      return name;
    }
  }
  /**
   * Returns a usage string representing the list of possible types for
   * the provided managed object definition.
   *
   * @param d
   *          The managed object definition.
   * @return A usage string representing the list of possible types for
   *         the provided managed object definition.
   */
  protected static String getSubTypesUsage(
      AbstractManagedObjectDefinition<?, ?> d) {
    // Build the -t option usage.
    SortedMap<String, ?> types = getSubTypes(d);
    StringBuilder builder = new StringBuilder();
    boolean isFirst = true;
    for (String s : types.keySet()) {
      if (!isFirst) {
        builder.append(" | ");
      }
      builder.append(s);
      isFirst = false;
    }
    return builder.toString();
  }
}
opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.tools.dsconfig;
@@ -40,6 +40,7 @@
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.RelationDefinitionVisitor;
import org.opends.server.admin.RelationOption;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.SingletonRelationDefinition;
import org.opends.server.util.args.ArgumentException;
import org.opends.server.util.args.SubCommandArgumentParser;
@@ -115,6 +116,32 @@
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
        Void visitSet(
        SetRelationDefinition<C, S> rd, ManagedObjectPath<?, ?> p) {
      try {
        // Create the sub-commands.
        createHandlers.add(CreateSubCommandHandler.create(parser, p, rd));
        deleteHandlers.add(DeleteSubCommandHandler.create(parser, p, rd));
        listHandlers.add(ListSubCommandHandler.create(parser, p, rd));
        getPropHandlers.add(GetPropSubCommandHandler.create(parser, p, rd));
        setPropHandlers.add(SetPropSubCommandHandler.create(parser, p, rd));
        // Process the referenced managed object definition and its
        // sub-types.
        processRelation(p, rd);
      } catch (ArgumentException e) {
        exception = e;
      }
      return null;
    }
    /**
     * {@inheritDoc}
     */
    public <C extends ConfigurationClient, S extends Configuration>
        Void visitSingleton(
        SingletonRelationDefinition<C, S> rd, ManagedObjectPath<?, ?> p) {
      try {
@@ -135,36 +162,36 @@
  }
  // The set of all available sub-commands.
  private SortedSet<SubCommandHandler> allHandlers =
  private final SortedSet<SubCommandHandler> allHandlers =
    new TreeSet<SubCommandHandler>();
  // The set of create-xxx available sub-commands.
  private SortedSet<CreateSubCommandHandler<?, ?>> createHandlers =
  private final SortedSet<CreateSubCommandHandler<?, ?>> createHandlers =
    new TreeSet<CreateSubCommandHandler<?, ?>>();
  // The set of delete-xxx available sub-commands.
  private SortedSet<DeleteSubCommandHandler> deleteHandlers =
  private final SortedSet<DeleteSubCommandHandler> deleteHandlers =
    new TreeSet<DeleteSubCommandHandler>();
  // Any exception that occurred whilst creating the sub-commands.
  private ArgumentException exception = null;
  // The set of get-xxx-prop available sub-commands.
  private SortedSet<GetPropSubCommandHandler> getPropHandlers =
  private final SortedSet<GetPropSubCommandHandler> getPropHandlers =
    new TreeSet<GetPropSubCommandHandler>();
  // The help sub-command handler.
  private HelpSubCommandHandler helpHandler = null;
  // The set of list-xxx available sub-commands.
  private SortedSet<ListSubCommandHandler> listHandlers =
  private final SortedSet<ListSubCommandHandler> listHandlers =
    new TreeSet<ListSubCommandHandler>();
  // The sub-command argument parser.
  private final SubCommandArgumentParser parser;
  // The set of set-xxx-prop available sub-commands.
  private SortedSet<SetPropSubCommandHandler> setPropHandlers =
  private final SortedSet<SetPropSubCommandHandler> setPropHandlers =
    new TreeSet<SetPropSubCommandHandler>();
  // The relation visitor.
@@ -327,6 +354,27 @@
  // Process a set relation.
  private <C extends ConfigurationClient, S extends Configuration>
      void processRelation(
      ManagedObjectPath<?, ?> path, SetRelationDefinition<C, S> r) {
    AbstractManagedObjectDefinition<C, S> d = r.getChildDefinition();
    // Process all relations associated directly with this
    // definition.
    helpHandler.registerManagedObjectDefinition(d);
    processPath(path.child(r, d));
    // Now process relations associated with derived definitions.
    for (AbstractManagedObjectDefinition<? extends C, ? extends S> c : d
        .getAllChildren()) {
      helpHandler.registerManagedObjectDefinition(c);
      processPath(path.child(r, c));
    }
  }
  // Process a singleton relation.
  private <C extends ConfigurationClient, S extends Configuration>
      void processRelation(
opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2008-2009 Sun Microsystems, Inc.
 */
package org.opends.server.admin;
@@ -99,7 +99,7 @@
   * {@inheritDoc}
   */
  @Override
  public String getInstantiableRelationChildRDNType(
  public String getRelationChildRDNType(
      InstantiableRelationDefinition<?, ?> r) {
    if (r == TestCfg.getTestOneToManyParentRelationDefinition()
        || r == TestParentCfgDefn.getInstance()
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteriaTest.java
New file
@@ -0,0 +1,140 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Arrays;
import java.util.Collection;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.api.ClientConnection;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Unit tests for ANDConnectionCriteria.
 */
public class ANDConnectionCriteriaTest extends DirectoryServerTestCase
{
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Returns test data for the following test cases.
   *
   * @return The test data for the following test cases.
   */
  @DataProvider(name = "testData")
  public Object[][] createTestData()
  {
    return new Object[][] {
        { Arrays.<ConnectionCriteria> asList(), true },
        { Arrays.asList(ConnectionCriteria.TRUE), true },
        {
            Arrays.asList(ConnectionCriteria.TRUE,
                ConnectionCriteria.TRUE), true },
        { Arrays.asList(ConnectionCriteria.FALSE), false },
        {
            Arrays.asList(ConnectionCriteria.TRUE,
                ConnectionCriteria.FALSE), false },
        {
            Arrays.asList(ConnectionCriteria.FALSE,
                ConnectionCriteria.TRUE), false },
        {
            Arrays.asList(ConnectionCriteria.FALSE,
                ConnectionCriteria.FALSE), false }, };
  }
  /**
   * Tests the matches method.
   *
   * @param subCriteria
   *          The sub criteria.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testMatches(Collection<ConnectionCriteria> subCriteria,
      boolean expectedResult) throws Exception
  {
    ANDConnectionCriteria criteria =
        new ANDConnectionCriteria(subCriteria);
    ClientConnection connection =
        new InternalClientConnection(DN.NULL_DN);
    Assert.assertEquals(criteria.matches(connection), expectedResult);
  }
  /**
   * Tests the willMatchAfterBind method.
   *
   * @param subCriteria
   *          The sub criteria.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testWillMatchAfterBind(
      Collection<ConnectionCriteria> subCriteria, boolean expectedResult)
      throws Exception
  {
    ANDConnectionCriteria criteria =
        new ANDConnectionCriteria(subCriteria);
    ClientConnection connection =
        new InternalClientConnection(DN.NULL_DN);
    Assert.assertEquals(criteria.willMatchAfterBind(connection,
        DN.NULL_DN, AuthenticationType.SIMPLE, false), expectedResult);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteriaTest.java
New file
@@ -0,0 +1,202 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Unit tests for AuthMethodConnectionCriteria.
 */
public class AuthMethodConnectionCriteriaTest extends
    DirectoryServerTestCase
{
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Returns test data for the following test cases.
   *
   * @return The test data for the following test cases.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @DataProvider(name = "testData")
  public Object[][] createTestData() throws Exception
  {
    return new Object[][] {
        { AllowedAuthMethod.ANONYMOUS,
            Collections.singleton(AllowedAuthMethod.ANONYMOUS), true },
        { AllowedAuthMethod.ANONYMOUS,
            Collections.singleton(AllowedAuthMethod.SIMPLE), false },
        { AllowedAuthMethod.ANONYMOUS,
            Collections.singleton(AllowedAuthMethod.SASL), false },
        { AllowedAuthMethod.SIMPLE,
            Collections.singleton(AllowedAuthMethod.ANONYMOUS), false },
        { AllowedAuthMethod.SIMPLE,
            Collections.singleton(AllowedAuthMethod.SIMPLE), true },
        { AllowedAuthMethod.SIMPLE,
            Collections.singleton(AllowedAuthMethod.SASL), false },
        { AllowedAuthMethod.SASL,
            Collections.singleton(AllowedAuthMethod.ANONYMOUS), false },
        { AllowedAuthMethod.SASL,
            Collections.singleton(AllowedAuthMethod.SIMPLE), false },
        { AllowedAuthMethod.SASL,
            Collections.singleton(AllowedAuthMethod.SASL), true },
        { AllowedAuthMethod.ANONYMOUS,
            EnumSet.noneOf(AllowedAuthMethod.class), false },
        { AllowedAuthMethod.SIMPLE,
            EnumSet.noneOf(AllowedAuthMethod.class), false },
        { AllowedAuthMethod.SASL,
            EnumSet.noneOf(AllowedAuthMethod.class), false },
        { AllowedAuthMethod.ANONYMOUS,
            EnumSet.allOf(AllowedAuthMethod.class), true },
        { AllowedAuthMethod.SIMPLE,
            EnumSet.allOf(AllowedAuthMethod.class), true },
        { AllowedAuthMethod.SASL,
            EnumSet.allOf(AllowedAuthMethod.class), true }, };
  }
  /**
   * Tests the matches method.
   *
   * @param clientAuthMethod
   *          The client authentication method.
   * @param allowedAuthMethods
   *          The set of allowed authentication methods.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testMatches(AllowedAuthMethod clientAuthMethod,
      Collection<AllowedAuthMethod> allowedAuthMethods,
      boolean expectedResult) throws Exception
  {
    DN bindDN;
    if (clientAuthMethod == AllowedAuthMethod.ANONYMOUS)
    {
      bindDN = DN.nullDN();
    }
    else
    {
      bindDN =
          DN.decode("cn=Directory Manager, cn=Root DNs, cn=config");
    }
    ClientConnection client =
        new MockClientConnection(12345, false, bindDN, clientAuthMethod);
    AuthMethodConnectionCriteria criteria =
        new AuthMethodConnectionCriteria(allowedAuthMethods);
    Assert.assertEquals(criteria.matches(client), expectedResult);
  }
  /**
   * Tests the willMatchAfterBind method.
   *
   * @param clientAuthMethod
   *          The client authentication method.
   * @param allowedAuthMethods
   *          The set of allowed authentication methods.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testWillMatchAfterBind(
      AllowedAuthMethod clientAuthMethod,
      Collection<AllowedAuthMethod> allowedAuthMethods,
      boolean expectedResult) throws Exception
  {
    ClientConnection client =
        new MockClientConnection(12345, false, DN.nullDN(),
            AllowedAuthMethod.ANONYMOUS);
    AuthenticationType authType;
    DN bindDN;
    switch (clientAuthMethod)
    {
    case ANONYMOUS:
      authType = null;
      bindDN = DN.nullDN();
      break;
    case SIMPLE:
      authType = AuthenticationType.SIMPLE;
      bindDN =
          DN.decode("cn=Directory Manager, cn=Root DNs, cn=config");
      break;
    default: // SASL
      authType = AuthenticationType.SASL;
      bindDN =
          DN.decode("cn=Directory Manager, cn=Root DNs, cn=config");
      break;
    }
    AuthMethodConnectionCriteria criteria =
        new AuthMethodConnectionCriteria(allowedAuthMethods);
    Assert.assertEquals(criteria.willMatchAfterBind(client, bindDN,
        authType, false), expectedResult);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteriaTest.java
New file
@@ -0,0 +1,156 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.authorization.dseecompat.PatternDN;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Unit tests for BindDNConnectionCriteria.
 */
public class BindDNConnectionCriteriaTest extends
    DirectoryServerTestCase
{
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Returns test data for the following test cases.
   *
   * @return The test data for the following test cases.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @DataProvider(name = "testData")
  public Object[][] createTestData() throws Exception
  {
    DN anonymousUser = DN.nullDN();
    DN rootUser =
        DN.decode("cn=Directory Manager, cn=Root DNs, cn=config");
    PatternDN rootMatch = PatternDN.decode("*, cn=Root DNs, cn=config");
    PatternDN rootNoMatch =
        PatternDN.decode("cn=Dirx*, cn=Root DNs, cn=config");
    return new Object[][] {
        { anonymousUser, Collections.<PatternDN> emptySet(), false },
        { rootUser, Collections.<PatternDN> emptySet(), false },
        { anonymousUser, Collections.singleton(rootMatch), false },
        { rootUser, Collections.singleton(rootMatch), true },
        { anonymousUser, Collections.singleton(rootNoMatch), false },
        { rootUser, Collections.singleton(rootNoMatch), false },
        { anonymousUser, Arrays.asList(rootMatch, rootNoMatch), false },
        { rootUser, Arrays.asList(rootMatch, rootNoMatch), true }, };
  }
  /**
   * Tests the matches method.
   *
   * @param clientBindDN
   *          The client bind DN.
   * @param allowedDNPatterns
   *          The set of allowed DN patterns.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testMatches(DN clientBindDN,
      Collection<PatternDN> allowedDNPatterns, boolean expectedResult)
      throws Exception
  {
    ClientConnection client =
        new MockClientConnection(12345, false, clientBindDN,
            AllowedAuthMethod.SIMPLE);
    BindDNConnectionCriteria criteria =
        BindDNConnectionCriteria.create(allowedDNPatterns);
    Assert.assertEquals(criteria.matches(client), expectedResult);
  }
  /**
   * Tests the willMatchAfterBind method.
   *
   * @param clientBindDN
   *          The client bind DN.
   * @param allowedDNPatterns
   *          The set of allowed DN patterns.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testWillMatchAfterBind(DN clientBindDN,
      Collection<PatternDN> allowedDNPatterns, boolean expectedResult)
      throws Exception
  {
    ClientConnection client =
        new MockClientConnection(12345, false, DN.nullDN(),
            AllowedAuthMethod.ANONYMOUS);
    BindDNConnectionCriteria criteria =
        BindDNConnectionCriteria.create(allowedDNPatterns);
    Assert
        .assertEquals(criteria.willMatchAfterBind(client, clientBindDN,
            AuthenticationType.SIMPLE, false), expectedResult);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/IPConnectionCriteriaTest.java
New file
@@ -0,0 +1,157 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Collection;
import java.util.Collections;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AddressMask;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Unit tests for IPConnectionCriteria.
 */
public class IPConnectionCriteriaTest extends DirectoryServerTestCase
{
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Returns test data for the following test cases.
   *
   * @return The test data for the following test cases.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @DataProvider(name = "testData")
  public Object[][] createTestData() throws Exception
  {
    AddressMask matchAnything = AddressMask.decode("*.*.*.*");
    AddressMask matchNothing = AddressMask.decode("0.0.0.0");
    ClientConnection client =
        new MockClientConnection(12345, false, DN.nullDN(),
            AllowedAuthMethod.ANONYMOUS);
    Collection<AddressMask> emptyMasks = Collections.emptySet();
    return new Object[][] {
        { emptyMasks, emptyMasks, client, true },
        { Collections.singleton(matchAnything), emptyMasks, client,
            true },
        { emptyMasks, Collections.singleton(matchAnything), client,
            false },
        { Collections.singleton(matchAnything),
            Collections.singleton(matchAnything), client, false },
        { Collections.singleton(matchNothing), emptyMasks, client,
            false },
        { emptyMasks, Collections.singleton(matchNothing), client, true },
        { Collections.singleton(matchNothing),
            Collections.singleton(matchNothing), client, false }, };
  }
  /**
   * Tests the matches method.
   *
   * @param allowedClients
   *          The set of allowed client address masks.
   * @param deniedClients
   *          The set of denied client address masks.
   * @param client
   *          The test client.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testMatches(Collection<AddressMask> allowedClients,
      Collection<AddressMask> deniedClients, ClientConnection client,
      boolean expectedResult) throws Exception
  {
    IPConnectionCriteria criteria =
        new IPConnectionCriteria(allowedClients, deniedClients);
    Assert.assertEquals(criteria.matches(client), expectedResult);
  }
  /**
   * Tests the willMatchAfterBind method.
   *
   * @param allowedClients
   *          The set of allowed client address masks.
   * @param deniedClients
   *          The set of denied client address masks.
   * @param client
   *          The test client.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testWillMatchAfterBind(
      Collection<AddressMask> allowedClients,
      Collection<AddressMask> deniedClients, ClientConnection client,
      boolean expectedResult) throws Exception
  {
    IPConnectionCriteria criteria =
        new IPConnectionCriteria(allowedClients, deniedClients);
    Assert.assertEquals(criteria.willMatchAfterBind(client, DN.NULL_DN,
        AuthenticationType.SIMPLE, false), expectedResult);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockClientConnection.java
New file
@@ -0,0 +1,374 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Collection;
import org.opends.messages.Message;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.api.ConnectionSecurityProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.SearchOperation;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.AuthenticationInfo;
import org.opends.server.types.ByteString;
import org.opends.server.types.CancelRequest;
import org.opends.server.types.CancelResult;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.Entry;
import org.opends.server.types.IntermediateResponse;
import org.opends.server.types.Operation;
import org.opends.server.types.SearchResultEntry;
import org.opends.server.types.SearchResultReference;
/**
 * A mock connection for connection criteria testing.
 */
public final class MockClientConnection extends ClientConnection
{
  private final int clientPort;
  private final boolean isSecure;
  private final AuthenticationInfo authInfo;
  /**
   * Creates a new mock client connection.
   *
   * @param clientPort
   *          The client port.
   * @param isSecure
   *          Is the client using a secure connection.
   * @param bindDN
   *          The client bind DN.
   * @param authMethod
   *          The client authentication mathod.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  public MockClientConnection(int clientPort, boolean isSecure,
      DN bindDN, AllowedAuthMethod authMethod) throws Exception
  {
    this.clientPort = clientPort;
    this.isSecure = isSecure;
    switch (authMethod)
    {
    case ANONYMOUS:
      this.authInfo = new AuthenticationInfo();
      break;
    case SIMPLE:
      Entry simpleUser = DirectoryServer.getEntry(bindDN);
      ByteString password = new ASN1OctetString();
      password.setValue("password");
      this.authInfo =
          new AuthenticationInfo(simpleUser, password, true);
      break;
    default: // SASL
      Entry saslUser = DirectoryServer.getEntry(bindDN);
      this.authInfo =
          new AuthenticationInfo(saslUser, "external", true);
      break;
    }
  }
  @Override
  public AuthenticationInfo getAuthenticationInfo()
  {
    return authInfo;
  }
  @Override
  public void cancelAllOperations(CancelRequest cancelRequest)
  {
    // Stub.
  }
  @Override
  public void cancelAllOperationsExcept(CancelRequest cancelRequest,
      int messageID)
  {
    // Stub.
  }
  @Override
  public CancelResult cancelOperation(int messageID,
      CancelRequest cancelRequest)
  {
    // Stub.
    return null;
  }
  @Override
  public void disconnect(DisconnectReason disconnectReason,
      boolean sendNotification, Message message)
  {
    // Stub.
  }
  @Override
  public String getClientAddress()
  {
    return "127.0.0.1";
  }
  @Override
  public int getClientPort()
  {
    return clientPort;
  }
  @Override
  public ConnectionHandler<?> getConnectionHandler()
  {
    // Stub.
    return null;
  }
  @Override
  public long getConnectionID()
  {
    // Stub.
    return 0;
  }
  @Override
  public ConnectionSecurityProvider getConnectionSecurityProvider()
  {
    // Stub.
    return null;
  }
  @Override
  public InetAddress getLocalAddress()
  {
    // Stub.
    return null;
  }
  @Override
  public String getMonitorSummary()
  {
    // Stub.
    return null;
  }
  @Override
  public long getNumberOfOperations()
  {
    // Stub.
    return 0;
  }
  @Override
  public Operation getOperationInProgress(int messageID)
  {
    // Stub.
    return null;
  }
  @Override
  public Collection<Operation> getOperationsInProgress()
  {
    // Stub.
    return null;
  }
  @Override
  public String getProtocol()
  {
    // Stub.
    return null;
  }
  @Override
  public InetAddress getRemoteAddress()
  {
    try
    {
      return InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
    }
    catch (UnknownHostException e)
    {
      throw new RuntimeException(e);
    }
  }
  @Override
  public String getSecurityMechanism()
  {
    // Stub.
    return null;
  }
  @Override
  public String getServerAddress()
  {
    // Stub.
    return null;
  }
  @Override
  public int getServerPort()
  {
    // Stub.
    return 0;
  }
  @Override
  public boolean isSecure()
  {
    return isSecure;
  }
  @Override
  public boolean processDataRead(ByteBuffer buffer)
  {
    // Stub.
    return false;
  }
  @Override
  public boolean removeOperationInProgress(int messageID)
  {
    // Stub.
    return false;
  }
  @Override
  protected boolean sendIntermediateResponseMessage(
      IntermediateResponse intermediateResponse)
  {
    // Stub.
    return false;
  }
  @Override
  public void sendResponse(Operation operation)
  {
    // Stub.
  }
  @Override
  public void sendSearchEntry(SearchOperation searchOperation,
      SearchResultEntry searchEntry) throws DirectoryException
  {
    // Stub.
  }
  @Override
  public boolean sendSearchReference(SearchOperation searchOperation,
      SearchResultReference searchReference) throws DirectoryException
  {
    // Stub.
    return false;
  }
  @Override
  public void setConnectionSecurityProvider(
      ConnectionSecurityProvider securityProvider)
  {
    // Stub.
  }
  @Override
  public void toString(StringBuilder buffer)
  {
    // Stub.
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockRequestFilteringQOSPolicyCfg.java
New file
@@ -0,0 +1,189 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.meta.RequestFilteringQOSPolicyCfgDefn.AllowedOperations;
import org.opends.server.admin.std.meta.RequestFilteringQOSPolicyCfgDefn.AllowedSearchScopes;
import org.opends.server.admin.std.server.QOSPolicyCfg;
import org.opends.server.admin.std.server.RequestFilteringQOSPolicyCfg;
import org.opends.server.types.DN;
/**
 * Stub configuration used in tests.
 */
public abstract class MockRequestFilteringQOSPolicyCfg implements
    RequestFilteringQOSPolicyCfg
{
  /**
   * {@inheritDoc}
   */
  public final void addRequestFilteringChangeListener(
      ConfigurationChangeListener<RequestFilteringQOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final Class<? extends RequestFilteringQOSPolicyCfg> configurationClass()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public final String getJavaClass()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public final void removeRequestFilteringChangeListener(
      ConfigurationChangeListener<RequestFilteringQOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final void addChangeListener(
      ConfigurationChangeListener<QOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final void removeChangeListener(
      ConfigurationChangeListener<QOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final DN dn()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<String> getAllowedAttributes()
  {
    return Collections.unmodifiableSortedSet(new TreeSet<String>());
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<AllowedOperations> getAllowedOperations()
  {
    return Collections
        .unmodifiableSortedSet(new TreeSet<AllowedOperations>());
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<AllowedSearchScopes> getAllowedSearchScopes()
  {
    return Collections
        .unmodifiableSortedSet(new TreeSet<AllowedSearchScopes>());
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<DN> getAllowedSubtrees()
  {
    return Collections.unmodifiableSortedSet(new TreeSet<DN>());
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<String> getProhibitedAttributes()
  {
    return Collections.unmodifiableSortedSet(new TreeSet<String>());
  }
  /**
   * {@inheritDoc}
   */
  public SortedSet<DN> getProhibitedSubtrees()
  {
    return Collections.unmodifiableSortedSet(new TreeSet<DN>());
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockResourceLimitsQOSPolicyCfg.java
New file
@@ -0,0 +1,191 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.QOSPolicyCfg;
import org.opends.server.admin.std.server.ResourceLimitsQOSPolicyCfg;
import org.opends.server.types.DN;
/**
 * Stub configuration used in tests.
 */
public abstract class MockResourceLimitsQOSPolicyCfg implements
    ResourceLimitsQOSPolicyCfg
{
  /**
   * {@inheritDoc}
   */
  public final void addChangeListener(
      ConfigurationChangeListener<QOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final void removeChangeListener(
      ConfigurationChangeListener<QOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final DN dn()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public final void addResourceLimitsChangeListener(
      ConfigurationChangeListener<ResourceLimitsQOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public final Class<? extends ResourceLimitsQOSPolicyCfg> configurationClass()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public final void removeResourceLimitsChangeListener(
      ConfigurationChangeListener<ResourceLimitsQOSPolicyCfg> listener)
  {
    // Stub.
  }
  /**
   * {@inheritDoc}
   */
  public String getJavaClass()
  {
    // Stub.
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxConcurrentOpsPerConnection()
  {
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxConnections()
  {
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxConnectionsFromSameIP()
  {
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxOpsPerConnection()
  {
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public int getMinSubstringLength()
  {
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  public Integer getSizeLimit()
  {
    return null;
  }
  /**
   * {@inheritDoc}
   */
  public Long getTimeLimit()
  {
    return null;
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java
@@ -22,22 +22,24 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import static org.opends.messages.CoreMessages.*;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.opends.server.config.ConfigConstants.DN_BACKEND_BASE;
import java.util.ArrayList;
import java.util.Collections;
import static org.opends.messages.CoreMessages.*;
import org.opends.server.TestCaseUtils;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.admin.std.meta.NetworkGroupCriteriaCfgDefn.AllowedAuthMethod;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.core.*;
import org.opends.server.extensions.NullConnectionSecurityProvider;
@@ -51,6 +53,7 @@
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.ResultCode;
@@ -461,7 +464,7 @@
      String networkGroupID,
      DN     workflowBaseDN
      )
      throws DirectoryException
      throws Exception
  {
    // Create and register the network group with the server.
    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
@@ -474,10 +477,10 @@
    {
      networkGroup.register();
    }
    catch (DirectoryException de)
    catch (InitializationException e)
    {
      exceptionRaised = true;
      assertEquals(de.getMessageObject().getDescriptor(),
      assertEquals(e.getMessageObject().getDescriptor(),
                   ERR_REGISTER_NETWORK_GROUP_ALREADY_EXISTS);
    }
    assertEquals(exceptionRaised, true);
@@ -552,7 +555,7 @@
    // Dump the default network group
    dump(adminNG, "adminNetworkGroup> ");
    // let's get the internal network group -- it should always exist
    NetworkGroup internalNG = NetworkGroup.getInternalNetworkGroup();
    assertNotNull(internalNG);
@@ -592,7 +595,7 @@
      DN subordinate2,
      DN subordinate3,
      DN unrelatedDN
      ) throws DirectoryException
      ) throws Exception
  {
    // The network group identifier is always the same for this test.
    String networkGroupID = "Network Group for test2";
@@ -758,55 +761,86 @@
      String networkGroupID,
      DN     workflowBaseDN,
      int    priority,
      int    maxConnections,
      int    maxConnectionsFromSameClient,
      int    maxOpsPerConn,
      int    maxConcurrentOpsPerConn,
      int    searchTimeLimit,
      int    searchSizeLimit,
      int    minSubstringLength
      final int    maxConnections,
      final int    maxConnectionsFromSameClient,
      final int    maxOpsPerConn,
      final int    maxConcurrentOpsPerConn,
      final int    searchTimeLimit,
      final int    searchSizeLimit,
      final int    minSubstringLength
      )
      throws DirectoryException
      throws Exception
  {
    // Create and register the network group with the server.
    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
    networkGroup.register();
    TestCaseUtils.dsconfig(
        "set-global-configuration-prop",
        "--set", "workflow-configuration-mode:manual");
    networkGroup.setNetworkGroupPriority(priority);
    try
    {
      TestCaseUtils.dsconfig(
          "create-network-group",
          "--group-name", networkGroupID,
          "--set", "enabled:true",
          "--set", "priority:" + priority);
    // Create a workflow -- the workflow ID is the string representation
    // of the workflow base DN.
    WorkflowElement<?> nullWE = null;
    WorkflowImpl workflow = new WorkflowImpl(
        workflowBaseDN.toString(), workflowBaseDN, null, nullWE);
      try
      {
        // Ensure that the network group was created ok.
        NetworkGroup networkGroup = NetworkGroup.getNetworkGroup(networkGroupID);
        assertNotNull(networkGroup, "The network group does not seem to be registered.");
    // Register the workflow with the network group.
    networkGroup.registerWorkflow(workflow);
        TestCaseUtils.dsconfig(
            "create-network-group-qos-policy",
            "--group-name", networkGroupID,
            "--type", "resource-limits",
            "--set", "max-concurrent-ops-per-connection:" + maxConcurrentOpsPerConn,
            "--set", "max-connections:" + maxConnections,
            "--set", "max-connections-from-same-ip:" + maxConnectionsFromSameClient,
            "--set", "max-ops-per-connection:" + maxOpsPerConn,
            "--set", "min-substring-length:" + minSubstringLength,
            "--set", "size-limit:" + searchSizeLimit,
            "--set", "time-limit:" + searchTimeLimit + "s");
    // Create the resource limits
    ResourceLimits limits = new ResourceLimits(null);
    limits.setMaxConnections(maxConnections);
    limits.setMaxConnectionsFromSameIP(maxConnectionsFromSameClient);
    limits.setMaxOpsPerConnection(maxOpsPerConn);
    limits.setMaxConcurrentOpsPerConnection(maxConcurrentOpsPerConn);
    limits.setSearchTimeLimit(searchTimeLimit);
    limits.setSearchSizeLimit(searchSizeLimit);
    limits.setMinSearchSubstringLength(minSubstringLength);
        // Check that the policy was created.
        ResourceLimitsPolicy policy = networkGroup.getNetworkGroupQOSPolicy(ResourceLimitsPolicy.class);
        assertNotNull(policy, "The policy was not registered.");
    // Associate the resource limits to the network group
    networkGroup.setResourceLimits(limits);
        // Check the resource limits are set properly.
        assertEquals(policy.getTimeLimit(), searchTimeLimit);
        assertEquals(policy.getSizeLimit(), searchSizeLimit);
        assertEquals(policy.getMinSubstring(), minSubstringLength);
    // Check the resource limits
    assertEquals(networkGroup.getSearchDurationLimit(), searchTimeLimit);
    assertEquals(networkGroup.getSearchSizeLimit(), searchSizeLimit);
    assertEquals(networkGroup.getMinSubstring(), minSubstringLength);
        assertEquals(networkGroup.getTimeLimit(), searchTimeLimit);
        assertEquals(networkGroup.getSizeLimit(), searchSizeLimit);
        assertEquals(networkGroup.getMinSubstring(), minSubstringLength);
    // Clean the network group
    networkGroup.deregisterWorkflow(workflow.getWorkflowId());
    networkGroup.deregister();
        TestCaseUtils.dsconfig(
            "delete-network-group-qos-policy",
            "--group-name", networkGroupID,
            "--policy-type", "resource-limits");
        // Check that the policy was removed.
        policy = networkGroup.getNetworkGroupQOSPolicy(ResourceLimitsPolicy.class);
        assertNull(policy, "The policy was not deregistered.");
      }
      finally
      {
        // The policy will get removed by this as well.
        TestCaseUtils.dsconfig("delete-network-group", "--group-name",
            networkGroupID);
      }
    }
    finally
    {
      TestCaseUtils.dsconfig(
          "set-global-configuration-prop",
          "--set", "workflow-configuration-mode:auto");
    }
  }
  /**
   * Tests the mechanism to attribute a network group to a client connection,
   * comparing the priority.
@@ -820,7 +854,7 @@
      DN dn2,
      int prio2
      )
      throws DirectoryException
      throws Exception
  {
    // Create and register the network group with the server.
    NetworkGroup networkGroup1 = new NetworkGroup(ng1);
@@ -872,39 +906,36 @@
          int prio1,
          int prio2,
          int prio3)
    throws DirectoryException
    throws Exception
  {
    // Create a AuthMethodCriteria for anonymous connections
    AuthMethodCriteria authCriteria1 = new AuthMethodCriteria();
    authCriteria1.addAuthMethod(AllowedAuthMethod.ANONYMOUS);
    NetworkGroupCriteria criteria1 = new NetworkGroupCriteria(null);
    criteria1.setAuthMethodCriteria(authCriteria1);
    AuthMethodConnectionCriteria authCriteria1 =
        new AuthMethodConnectionCriteria(Collections
            .singleton(AllowedAuthMethod.ANONYMOUS));
    // Create a AuthMethodCriteria for simple bind connections
    AuthMethodCriteria authCriteria2 = new AuthMethodCriteria();
    authCriteria2.addAuthMethod(AllowedAuthMethod.SIMPLE);
    NetworkGroupCriteria criteria2 = new NetworkGroupCriteria(null);
    criteria2.setAuthMethodCriteria(authCriteria2);
    AuthMethodConnectionCriteria authCriteria2 =
        new AuthMethodConnectionCriteria(Collections
            .singleton(AllowedAuthMethod.SIMPLE));
    // Create a AuthMethodCriteria for sasl connections
    AuthMethodCriteria authCriteria3 = new AuthMethodCriteria();
    authCriteria3.addAuthMethod(AllowedAuthMethod.SASL);
    NetworkGroupCriteria criteria3 = new NetworkGroupCriteria(null);
    criteria3.setAuthMethodCriteria(authCriteria3);
    AuthMethodConnectionCriteria authCriteria3 =
        new AuthMethodConnectionCriteria(Collections
            .singleton(AllowedAuthMethod.SASL));
    // Create and register the network group with the server.
    NetworkGroup networkGroup1 = new NetworkGroup("anonymous_group");
    networkGroup1.register();
    networkGroup1.setCriteria(criteria1);
    networkGroup1.setConnectionCriteria(authCriteria1);
    networkGroup1.setNetworkGroupPriority(prio1);
    NetworkGroup networkGroup2 = new NetworkGroup("simplebind_group");
    networkGroup2.register();
    networkGroup2.setCriteria(criteria2);
    networkGroup2.setConnectionCriteria(authCriteria2);
    networkGroup2.setNetworkGroupPriority(prio2);
    NetworkGroup networkGroup3 = new NetworkGroup("sasl_group");
    networkGroup3.register();
    networkGroup3.setCriteria(criteria3);
    networkGroup3.setConnectionCriteria(authCriteria3);
    networkGroup3.setNetworkGroupPriority(prio3);
    // Create a new client connection, with anonymous authentication
@@ -943,18 +974,17 @@
  public void testNetworkGroupBindDnCriteria(
          String bindDnFilter,
          boolean match)
    throws DirectoryException
    throws Exception
  {
    // Create a BindDnFilterCriteria
    BindDnCriteria bindCriteria = new BindDnCriteria();
    bindCriteria.addBindDnFilter(bindDnFilter);
    NetworkGroupCriteria criteria = new NetworkGroupCriteria(null);
    criteria.setBindDnCriteria(bindCriteria);
    BindDNConnectionCriteria bindCriteria =
        BindDNConnectionCriteria.decode(Collections
            .singleton(bindDnFilter));
    // Create and register the network group with the server.
    NetworkGroup networkGroup = new NetworkGroup("bindfilter_group");
    networkGroup.register();
    networkGroup.setCriteria(criteria);
    networkGroup.setConnectionCriteria(bindCriteria);
    NetworkGroup defaultNg = NetworkGroup.getDefaultNetworkGroup();
@@ -1000,17 +1030,16 @@
   */
  @Test (groups = "virtual")
  public void testNetworkGroupSecurityCriteria()
    throws DirectoryException
    throws Exception
  {
    // Create a SecurityCriteria
    SecurityCriteria secCriteria = new SecurityCriteria(true);
    NetworkGroupCriteria criteria = new NetworkGroupCriteria(null);
    criteria.setSecurityCriteria(secCriteria);
    SecurityConnectionCriteria secCriteria =
        SecurityConnectionCriteria.SECURITY_REQUIRED;
    // Create and register the network group with the server.
    NetworkGroup networkGroup = new NetworkGroup("secured_group");
    networkGroup.register();
    networkGroup.setCriteria(criteria);
    networkGroup.setConnectionCriteria(secCriteria);
    NetworkGroup defaultNg = NetworkGroup.getDefaultNetworkGroup();
@@ -1029,8 +1058,8 @@
    assertEquals(ng, defaultNg);
    // now change the criteria (security not mandatory)
    secCriteria = new SecurityCriteria(false);
    criteria.setSecurityCriteria(secCriteria);
    secCriteria = SecurityConnectionCriteria.SECURITY_NOT_REQUIRED;
    networkGroup.setConnectionCriteria(secCriteria);
    // connection1 should match the networkGroup, even though it is not
    // secured
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyTest.java
@@ -22,21 +22,20 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2008 Sun Microsystems, Inc.
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import java.util.ArrayList;
import java.util.Set;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import org.opends.messages.Message;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.
        NetworkGroupRequestFilteringPolicyCfgDefn.AllowedOperations;
import org.opends.server.admin.std.meta.
        NetworkGroupRequestFilteringPolicyCfgDefn.AllowedSearchScopes;
import org.opends.server.admin.std.meta.RequestFilteringQOSPolicyCfgDefn.AllowedOperations;
import org.opends.server.admin.std.meta.RequestFilteringQOSPolicyCfgDefn.AllowedSearchScopes;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
@@ -45,7 +44,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.LDAPException;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.OperationType;
@@ -275,6 +273,8 @@
  @DataProvider (name = "ComplexSubtreesSet")
  public Object[][] initComplexSubtreesSet() throws DirectoryException
  {
    TreeSet<DN> subtrees_empty = new TreeSet<DN>();
    TreeSet<DN> subtrees_root = new TreeSet<DN>();
    subtrees_root.add(DN.decode("dc=example,dc=com"));
@@ -289,8 +289,8 @@
      {subtrees_root, subtrees_people, "dc=example,dc=com", true},
      {subtrees_root, subtrees_people, "ou=people,dc=example,dc=com", false},
      {subtrees_root, subtrees_entry, "ou=people,dc=example,dc=com", true},
      {null, subtrees_people, "dc=example,dc=com", true},
      {null, subtrees_people, "ou=people,dc=example,dc=com", false}
      {subtrees_empty, subtrees_people, "dc=example,dc=com", true},
      {subtrees_empty, subtrees_people, "ou=people,dc=example,dc=com", false}
    };
    return myData;
  }
@@ -352,14 +352,23 @@
   */
  @Test (dataProvider = "AllowedAttributesSet", groups = "virtual")
  public void testAllowedAttributes(
          Set<String> allowedAttributes,
          final SortedSet<String> allowedAttributes,
          String searchFilter,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setAllowedAttributes(allowedAttributes);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<String> getAllowedAttributes()
      {
        return Collections.unmodifiableSortedSet(allowedAttributes);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -367,7 +376,7 @@
        SearchScope.BASE_OBJECT,
        LDAPFilter.decode(searchFilter).toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -380,14 +389,23 @@
   */
  @Test (dataProvider = "ProhibitedAttributesSet", groups = "virtual")
  public void testProhibitedAttributes(
          Set<String> prohibitedAttributes,
          final SortedSet<String> prohibitedAttributes,
          String searchFilter,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setProhibitedAttributes(prohibitedAttributes);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<String> getProhibitedAttributes()
      {
        return Collections.unmodifiableSortedSet(prohibitedAttributes);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -395,7 +413,7 @@
        SearchScope.BASE_OBJECT,
        LDAPFilter.decode(searchFilter).toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -408,14 +426,23 @@
   */
  @Test (dataProvider = "AllowedSearchScopesSet", groups = "virtual")
  public void testAllowedSearchScopes(
          Set<AllowedSearchScopes> allowedScopes,
          final SortedSet<AllowedSearchScopes> allowedScopes,
          SearchScope searchScope,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setAllowedSearchScopes(allowedScopes);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<AllowedSearchScopes> getAllowedSearchScopes()
      {
        return Collections.unmodifiableSortedSet(allowedScopes);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -423,7 +450,7 @@
            searchScope,
            LDAPFilter.decode("objectclass=*").toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -436,14 +463,23 @@
   */
  @Test (dataProvider = "AllowedSubtreesSet", groups = "virtual")
  public void testAllowedSubtrees(
          Set<DN> allowedSubtrees,
          final SortedSet<DN> allowedSubtrees,
          String searchSubtree,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setAllowedSubtrees(allowedSubtrees);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<DN> getAllowedSubtrees()
      {
        return Collections.unmodifiableSortedSet(allowedSubtrees);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -451,7 +487,7 @@
            SearchScope.WHOLE_SUBTREE,
            LDAPFilter.decode("objectclass=*").toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -464,14 +500,23 @@
   */
  @Test (dataProvider = "ProhibitedSubtreesSet", groups = "virtual")
  public void testProhibitedSubtrees(
          Set<DN> prohibitedSubtrees,
          final SortedSet<DN> prohibitedSubtrees,
          String searchSubtree,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setProhibitedSubtrees(prohibitedSubtrees);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<DN> getProhibitedSubtrees()
      {
        return Collections.unmodifiableSortedSet(prohibitedSubtrees);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -479,7 +524,7 @@
            SearchScope.WHOLE_SUBTREE,
            LDAPFilter.decode("objectclass=*").toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -492,16 +537,30 @@
   */
  @Test (dataProvider = "ComplexSubtreesSet", groups = "virtual")
  public void testComplexSubtrees(
          Set<DN> allowedSubtrees,
          Set<DN> prohibitedSubtrees,
          final SortedSet<DN> allowedSubtrees,
          final SortedSet<DN> prohibitedSubtrees,
          String searchSubtree,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
    policy.setAllowedSubtrees(allowedSubtrees);
    policy.setProhibitedSubtrees(prohibitedSubtrees);
    RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
    RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
      @Override
      public SortedSet<DN> getAllowedSubtrees()
      {
        return Collections.unmodifiableSortedSet(allowedSubtrees);
      }
      @Override
      public SortedSet<DN> getProhibitedSubtrees()
      {
        return Collections.unmodifiableSortedSet(prohibitedSubtrees);
      }
    });
    InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
    InternalSearchOperation search = conn.processSearch(
@@ -509,7 +568,7 @@
            SearchScope.WHOLE_SUBTREE,
            LDAPFilter.decode("objectclass=*").toSearchFilter());
    boolean check = policy.checkPolicy(search, messages);
    boolean check = policy.isAllowed(search, messages);
    if (success) {
      assertTrue(check);
    } else {
@@ -522,14 +581,23 @@
   */
   @Test (dataProvider = "AllowedOperationsSet", groups = "virtual")
   public void testAllowedOperations(
           Set<AllowedOperations> allowedOps,
           final SortedSet<AllowedOperations> allowedOps,
           OperationType type,
           boolean success)
           throws DirectoryException, LDAPException, Exception
           throws Exception
   {
     ArrayList<Message> messages = new ArrayList<Message>();
     RequestFilteringPolicy policy = new RequestFilteringPolicy(null);
     policy.setAllowedOperations(allowedOps);
     RequestFilteringPolicyFactory factory = new RequestFilteringPolicyFactory();
     RequestFilteringPolicy policy = factory.createQOSPolicy(new MockRequestFilteringQOSPolicyCfg() {
       @Override
       public SortedSet<AllowedOperations> getAllowedOperations()
       {
         return Collections.unmodifiableSortedSet(allowedOps);
       }
     });
     InternalClientConnection conn = new InternalClientConnection(DN.NULL_DN);
     PreParseOperation op = null;
@@ -583,7 +651,7 @@
         return;
     }
     boolean check = policy.checkPolicy(op, messages);
     boolean check = policy.isAllowed(op, messages);
     if (success) {
       assertTrue(check);
     } else {
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyTest.java
File was renamed from opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsTest.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2008 Sun Microsystems, Inc.
 *      Copyright 2006-2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
@@ -35,8 +35,6 @@
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.LDAPException;
import org.opends.server.types.SearchScope;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
@@ -49,7 +47,7 @@
/*
 * This set of tests test the resource limits.
 */
public class ResourceLimitsTest extends DirectoryServerTestCase {
public class ResourceLimitsPolicyTest extends DirectoryServerTestCase {
  //===========================================================================
  //
  //                      B E F O R E    C L A S S
@@ -115,29 +113,42 @@
  /**
   * Tests the max number of connections resource limit.
   * @throws DirectoryException when there was a problem creating the connection
   * @throws Exception If the test failed unexpectedly.
   */
  @Test (groups = "virtual")
  public void testMaxNumberOfConnections()
          throws DirectoryException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    ResourceLimits limits = new ResourceLimits(null);
    limits.setMaxConnections(1);
    ResourceLimitsPolicyFactory factory =
        new ResourceLimitsPolicyFactory();
    ResourceLimitsPolicy limits =
        factory
            .createQOSPolicy(new MockResourceLimitsQOSPolicyCfg()
              {
                @Override
                public int getMaxConnections()
                {
                  return 1;
                }
              });
    InternalClientConnection conn1 = new InternalClientConnection(DN.NULL_DN);
    limits.addConnection(conn1);
    boolean check = limits.checkLimits(conn1, null, true, messages);
    boolean check = limits.isAllowed(conn1, null, true, messages);
    assertTrue(check);
    InternalClientConnection conn2 = new InternalClientConnection(DN.NULL_DN);
    limits.addConnection(conn2);
    check = limits.checkLimits(conn2, null, true, messages);
    check = limits.isAllowed(conn2, null, true, messages);
    assertFalse(check);
    limits.removeConnection(conn1);
    check = limits.checkLimits(conn2, null, true, messages);
    check = limits.isAllowed(conn2, null, true, messages);
    assertTrue(check);
    limits.removeConnection(conn2);
@@ -145,29 +156,42 @@
  /**
   * Tests the max number of connections from same IP resource limit.
   * @throws DirectoryException when there was a problem creating the connection
   * @throws Exception If the test failed unexpectedly.
   */
  @Test (groups = "virtual")
  public void testMaxNumberOfConnectionsFromSameIp()
          throws DirectoryException
          throws Exception
  {
    ArrayList<Message> messages = new ArrayList<Message>();
    ResourceLimits limits = new ResourceLimits(null);
    limits.setMaxConnectionsFromSameIP(1);
    ResourceLimitsPolicyFactory factory =
        new ResourceLimitsPolicyFactory();
    ResourceLimitsPolicy limits =
        factory
            .createQOSPolicy(new MockResourceLimitsQOSPolicyCfg()
              {
                @Override
                public int getMaxConnectionsFromSameIP()
                {
                  return 1;
                }
              });
    InternalClientConnection conn1 = new InternalClientConnection(DN.NULL_DN);
    limits.addConnection(conn1);
    boolean check = limits.checkLimits(conn1, null, true, messages);
    boolean check = limits.isAllowed(conn1, null, true, messages);
    assertTrue(check);
    InternalClientConnection conn2 = new InternalClientConnection(DN.NULL_DN);
    limits.addConnection(conn2);
    check = limits.checkLimits(conn2, null, true, messages);
    check = limits.isAllowed(conn2, null, true, messages);
    assertFalse(check);
    limits.removeConnection(conn1);
    check = limits.checkLimits(conn2, null, true, messages);
    check = limits.isAllowed(conn2, null, true, messages);
    assertTrue(check);
    limits.removeConnection(conn2);
@@ -178,19 +202,31 @@
   * @param minLength minimum search filter substring length
   * @param searchFilter the search filter to test
   * @param success boolean indicating the expected result
   * @throws DirectoryException when there was a problem creating the connection
   * @throws LDAPException when there was a problem decoding the filter
   * @throws Exception If the test failed unexpectedly.
   */
  @Test (dataProvider = "SearchFilterSet", groups = "virtual")
  public void testMinSubstringLength(
          int minLength,
          final int minLength,
          String searchFilter,
          boolean success)
          throws DirectoryException, LDAPException
          throws Exception
  {
    List<Message> messages = new ArrayList<Message>();
    ResourceLimits limits = new ResourceLimits(null);
    limits.setMinSearchSubstringLength(minLength);
    ResourceLimitsPolicyFactory factory =
        new ResourceLimitsPolicyFactory();
    ResourceLimitsPolicy limits =
        factory
            .createQOSPolicy(new MockResourceLimitsQOSPolicyCfg()
              {
                @Override
                public int getMinSubstringLength()
                {
                  return minLength;
                }
              });
    InternalClientConnection conn1 = new InternalClientConnection(DN.NULL_DN);
    limits.addConnection(conn1);
@@ -200,7 +236,7 @@
        SearchScope.BASE_OBJECT,
        LDAPFilter.decode(searchFilter).toSearchFilter());
    boolean check = limits.checkLimits(conn1, search, true, messages);
    boolean check = limits.isAllowed(conn1, search, true, messages);
    if (success) {
      assertTrue(check);
    } else {
opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteriaTest.java
New file
@@ -0,0 +1,136 @@
/*
 * 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
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 */
package org.opends.server.core.networkgroups;
import org.opends.server.DirectoryServerTestCase;
import org.opends.server.TestCaseUtils;
import org.opends.server.admin.std.meta.NetworkGroupCfgDefn.AllowedAuthMethod;
import org.opends.server.api.ClientConnection;
import org.opends.server.types.AuthenticationType;
import org.opends.server.types.DN;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Unit tests for ProtocolConnectionCriteria.
 */
public class SecurityConnectionCriteriaTest extends
    DirectoryServerTestCase
{
  /**
   * Sets up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           if the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception
  {
    TestCaseUtils.startServer();
  }
  /**
   * Returns test data for the following test cases.
   *
   * @return The test data for the following test cases.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @DataProvider(name = "testData")
  public Object[][] createTestData() throws Exception
  {
    return new Object[][] {
        { false, SecurityConnectionCriteria.SECURITY_NOT_REQUIRED, true },
        { false, SecurityConnectionCriteria.SECURITY_REQUIRED, false },
        { true, SecurityConnectionCriteria.SECURITY_NOT_REQUIRED, true },
        { true, SecurityConnectionCriteria.SECURITY_REQUIRED, true }, };
  }
  /**
   * Tests the matches method.
   *
   * @param isSecure
   *          Indicates if the client is using a secured connection.
   * @param criteria
   *          The security criteria.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testMatches(boolean isSecure,
      SecurityConnectionCriteria criteria, boolean expectedResult)
      throws Exception
  {
    ClientConnection client =
        new MockClientConnection(12345, isSecure, DN.nullDN(),
            AllowedAuthMethod.ANONYMOUS);
    Assert.assertEquals(criteria.matches(client), expectedResult);
  }
  /**
   * Tests the willMatchAfterBind method.
   *
   * @param isSecure
   *          Indicates if the client is using a secured connection.
   * @param criteria
   *          The security criteria.
   * @param expectedResult
   *          The expected result.
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(dataProvider = "testData")
  public void testWillMatchAfterBind(boolean isSecure,
      SecurityConnectionCriteria criteria, boolean expectedResult)
      throws Exception
  {
    ClientConnection client =
        new MockClientConnection(12345, false, DN.nullDN(),
            AllowedAuthMethod.ANONYMOUS);
    Assert.assertEquals(criteria.willMatchAfterBind(client,
        DN.nullDN(), AuthenticationType.SIMPLE, isSecure),
        expectedResult);
  }
}