From 95df5cfdba474acb03076953e992b898fbb277a8 Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 02 Feb 2009 23:37:54 +0000
Subject: [PATCH] Fix issue 3734 - Make network group policies extensible.

---
 opends/src/server/org/opends/server/admin/AdminException.java                                                        |    9 
 opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java                                       |    4 
 opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java                                |  508 -
 opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPlugin.java                                       |   16 
 opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml                                              |    6 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteriaTest.java        |  140 
 opends/src/server/org/opends/server/admin/server/ServerManagementContext.java                                        |   63 
 opends/src/server/org/opends/server/core/networkgroups/ProtocolConnectionCriteria.java                               |  109 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockRequestFilteringQOSPolicyCfg.java |  189 
 opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java                                             | 2777 ++++++++----
 opends/src/admin/defn/org/opends/server/admin/std/QOSPolicyConfiguration.xml                                         |   69 
 opends/src/server/org/opends/server/core/networkgroups/IPConnectionCriteria.java                                     |  114 
 opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java                                  |   20 
 opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java                                            |  346 +
 opends/resource/schema/02-config.ldif                                                                                |   95 
 opends/src/admin/defn/org/opends/server/admin/std/RequestFilteringQOSPolicyConfiguration.xml                         |  138 
 opends/src/server/org/opends/server/api/QOSPolicy.java                                                               |   30 
 opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliParser.java                                  |   25 
 opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java                                     |   24 
 opends/src/admin/defn/org/opends/server/admin/std/Package.xml                                                        |   80 
 opends/src/server/org/opends/server/admin/OptionalRelationDefinition.java                                            |    5 
 opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicy.java                                     |  135 
 opends/src/admin/defn/org/opends/server/admin/std/ResourceLimitsQOSPolicyConfiguration.xml                           |  225 +
 opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java                                     |   62 
 opends/src/server/org/opends/server/admin/client/ManagedObject.java                                                  |  205 
 opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java                                             |   61 
 opends/resource/config/config.ldif                                                                                   |    4 
 opends/src/server/org/opends/server/admin/SetRelationDefinition.java                                                 |  289 +
 opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml                                            |   10 
 opends/src/server/org/opends/server/admin/ManagedObjectPath.java                                                     |  212 
 opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java                                                |   48 
 opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java                                      |   58 
 opends/resource/admin/metaMO.xsl                                                                                     |   69 
 opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java                                       |   20 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyTest.java       |  160 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockResourceLimitsQOSPolicyCfg.java   |  191 
 opends/src/server/org/opends/server/admin/InstantiableRelationDefinition.java                                        |    7 
 opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyFactory.java                            |  672 +++
 opends/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteria.java                             |  142 
 opends/src/admin/defn/org/opends/server/admin/std/ConnectionHandlerConfiguration.xml                                 |   82 
 opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java                                      |  134 
 opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java                                           |   24 
 opends/src/server/org/opends/server/admin/RelationDefinitionVisitor.java                                             |   22 
 opends/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteria.java                                    |  104 
 opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStatistics.java                         |  155 
 opends/src/server/org/opends/server/tools/dsconfig/CLIProfile.java                                                   |   24 
 opends/resource/admin/admin.xsd                                                                                      |   13 
 opends/resource/admin/clientMO.xsl                                                                                   |   90 
 opends/src/server/org/opends/server/admin/client/ManagementContext.java                                              |   99 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/IPConnectionCriteriaTest.java         |  157 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteriaTest.java |  202 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteriaTest.java     |  156 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java                 |  177 
 opends/src/messages/messages/config.properties                                                                       |    7 
 opends/src/server/org/opends/server/admin/SingletonRelationDefinition.java                                           |    5 
 opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyFactory.java                              |  496 ++
 opends/src/server/org/opends/server/admin/client/spi/Driver.java                                                     |  187 
 opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java                                        |   69 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteriaTest.java   |  136 
 opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java                                       |   25 
 opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java                                         |   22 
 opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java                                       |   71 
 opends/src/server/org/opends/server/api/QOSPolicyFactory.java                                                        |   89 
 opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java                                    |   71 
 opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java                                                     |   10 
 opends/src/server/org/opends/server/core/networkgroups/ConnectionCriteria.java                                       |  139 
 opends/src/server/org/opends/server/admin/server/ServerManagedObject.java                                            |  270 +
 opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml                                      |  305 
 opends/src/server/org/opends/server/admin/AdminRuntimeException.java                                                 |    9 
 opends/src/server/org/opends/server/admin/client/AdminSecurityException.java                                         |    9 
 opends/src/server/org/opends/server/core/networkgroups/NetworkGroupStatistics.java                                   |   58 
 opends/src/messages/messages/dsconfig.properties                                                                     |    6 
 opends/src/server/org/opends/server/admin/LDAPProfile.java                                                           |   67 
 opends/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteria.java                                 |  138 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockClientConnection.java             |  374 +
 opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicy.java                                   |  632 --
 /dev/null                                                                                                            |   75 
 opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupPluginConfiguration.xml                                |    6 
 opends/src/server/org/opends/server/admin/DecodingException.java                                                     |    9 
 opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java                                           |   58 
 opends/src/server/org/opends/server/api/ClientConnection.java                                                        |   33 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyTest.java         |   86 
 opends/src/server/org/opends/server/admin/RelationDefinition.java                                                    |    8 
 opends/resource/admin/abbreviations.xsl                                                                              |    4 
 opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java                                     |   23 
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java                               |    4 
 opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java                                      |  292 
 opends/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteria.java                               |   99 
 opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyStatistics.java                           |  121 
 89 files changed, 9,630 insertions(+), 3,159 deletions(-)

diff --git a/opends/resource/admin/abbreviations.xsl b/opends/resource/admin/abbreviations.xsl
index 64d52d2..4e38ab4 100644
--- a/opends/resource/admin/abbreviations.xsl
+++ b/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>
diff --git a/opends/resource/admin/admin.xsd b/opends/resource/admin/admin.xsd
index ace9c9c..7eef037 100644
--- a/opends/resource/admin/admin.xsd
+++ b/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>
diff --git a/opends/resource/admin/clientMO.xsl b/opends/resource/admin/clientMO.xsl
index e9bdee3..bc2b893 100644
--- a/opends/resource/admin/clientMO.xsl
+++ b/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>
diff --git a/opends/resource/admin/metaMO.xsl b/opends/resource/admin/metaMO.xsl
index 469d41c..c6a57d6 100644
--- a/opends/resource/admin/metaMO.xsl
+++ b/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>
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 9f6f82a..f9f22d2 100644
--- a/opends/resource/config/config.ldif
+++ b/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
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 93aafcb..3064dde 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/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 $
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/ConnectionHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/ConnectionHandlerConfiguration.xml
index f97a80f..2b54bb1 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/ConnectionHandlerConfiguration.xml
+++ b/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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
index bcbe6d2..cbfd739 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
+++ b/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
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml
index cc5457a..6e08be5 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml
+++ b/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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupCriteriaConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupCriteriaConfiguration.xml
deleted file mode 100644
index dcb8982..0000000
--- a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupCriteriaConfiguration.xml
+++ /dev/null
@@ -1,213 +0,0 @@
-<?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 2007-2008 Sun Microsystems, Inc.
-  ! -->
-<adm:managed-object name="network-group-criteria" plural-name="network-group-criteria"
-  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.
-  </adm:synopsis>
-
-  <adm:tag name="core-server" />
-
-  <adm:profile name="ldap">
-    <ldap:object-class>
-      <ldap:name>ds-cfg-network-group-criteria</ldap:name>
-      <ldap:superior>top</ldap:superior>
-    </ldap:object-class>
-  </adm:profile>
-
-  <adm:property name="allowed-auth-method" multi-valued="true">
-    <adm:synopsis>
-      Specifies the allowed authorization methods for a client connection to
-      match the <adm:user-friendly-name />.
-    </adm:synopsis>
-    <adm:default-behavior>
-      <adm:undefined />
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:enumeration>
-        <adm:value name="anonymous">
-          <adm:synopsis>
-            Anonymous connections.
-          </adm:synopsis>
-        </adm:value>
-        <adm:value name="simple">
-          <adm:synopsis>
-            Simple bind connections, with bind DN and password.
-          </adm:synopsis>
-        </adm:value>
-        <adm:value name="sasl">
-          <adm:synopsis>
-            SASL/external connections, with a certificate containing the
-            user authentication.
-          </adm:synopsis>
-        </adm:value>
-      </adm:enumeration>
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-allowed-auth-method</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="allowed-ldap-port" multi-valued="true">
-    <adm:synopsis>
-      Specifies the allowed LDAP ports for the client connection to match the
-      <adm:user-friendly-name />.
-    </adm:synopsis>
-    <adm:default-behavior>
-      <adm:undefined />
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:enumeration>
-        <adm:value name="ldap">
-          <adm:synopsis>
-            Connection over ldap port.
-          </adm:synopsis>
-        </adm:value>
-        <adm:value name="ldaps">
-          <adm:synopsis>
-            Connection over ldaps port.
-          </adm:synopsis>
-        </adm:value>
-      </adm:enumeration>
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-allowed-ldap-port</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="bind-dn-filter" multi-valued="true">
-    <adm:synopsis>
-      Specifies a bind DN filter for the client connection to match the
-      <adm:user-friendly-name />.
-    </adm:synopsis>
-    <adm:description>
-      A valid bind DN filter is a string 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:default-behavior>
-      <adm:undefined />
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:string />
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-bind-dn-filter</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="ip-address-filter" multi-valued="true">
-    <adm:synopsis>
-      Specifies an IP address filter for the <adm:user-friendly-name />.
-    </adm:synopsis>
-    <adm:description>
-      A valid IP address mask can be one of the followings:
-              129.34.55.67
-              129.*.78.55
-              .sun.com
-              foo.sun.com
-              foo.*.sun.*
-              128.*.*.*
-              129.45.23.67/22
-              128.33.21.21/32
-              *.*.*.*
-              129.45.67.34/0
-              foo.com
-              foo
-              2001:fecd:ba23:cd1f:dcb1:1010:9234:4088/124
-              2001:fecd:ba23:cd1f:dcb1:1010:9234:4088
-              [2001:fecd:ba23:cd1f:dcb1:1010:9234:4088]/45
-              ::/128
-              ::1/128
-              ::
-    </adm:description>
-    <adm:default-behavior>
-      <adm:undefined />
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:ip-address-mask />
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-ip-address-filter</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="is-security-mandatory">
-    <adm:synopsis>
-      Specifies whether security is mandatory for the
-      <adm:user-friendly-name />.
-    </adm:synopsis>
-    <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>
-
-  <adm:property name="user-entry-filter">
-    <adm:synopsis>
-      Specifies a search filter that the entry of a bound client
-      must match.
-    </adm:synopsis>
-    <adm:default-behavior>
-      <adm:undefined />
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:string />
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-user-entry-filter</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-  
-</adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupPluginConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupPluginConfiguration.xml
index 810decf..9109ccc 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupPluginConfiguration.xml
+++ b/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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupResourceLimitsConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupResourceLimitsConfiguration.xml
deleted file mode 100644
index cd502cd..0000000
--- a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupResourceLimitsConfiguration.xml
+++ /dev/null
@@ -1,308 +0,0 @@
-<?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 2007-2008 Sun Microsystems, Inc.
-  ! -->
-<adm:managed-object name="network-group-resource-limits"
-  plural-name="network-group-resource-limits"
-  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 network group.
-  </adm:synopsis>
-
-  <adm:tag name="core-server" />
-
-  <adm:profile name="ldap">
-    <ldap:object-class>
-      <ldap:name>ds-cfg-network-group-resource-limits</ldap:name>
-      <ldap:superior>top</ldap:superior>
-    </ldap:object-class>
-  </adm:profile>
-
-  <adm:property name="max-connections" mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of connections in the network group.
-    </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"
-   mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of connections from the same client
-      (identified by its IP 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" mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of operations per 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"
-   mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of concurrent operations per 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="search-size-limit" mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of entries that the Directory Server
-      should return to the client during a search operation.
-    </adm:synopsis>
-    <adm:description>
-      A value of 0 indicates that no size limit is enforced. Note that this
-      is the network-group limit, but it may be overridden on a
-      per-user basis.
-    </adm:description>
-    <adm:default-behavior>
-      <adm:alias>
-        <adm:synopsis>
-          Use the global default size limit.
-        </adm:synopsis>
-      </adm:alias>
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:integer lower-limit="0"/>
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-search-size-limit</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="search-time-limit" mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum length of time that the Directory Server should
-      spend 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 network group time limit, but it may be overridden
-      on a per-user basis.
-    </adm:description>
-    <adm:default-behavior>
-      <adm:alias>
-        <adm:synopsis>
-          Use the global default time limit.
-        </adm:synopsis>
-      </adm:alias>
-    </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-search-time-limit</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="min-substring-length" mandatory="false">
-    <adm:synopsis>
-      Specifies the minimum length for a search substring.
-    </adm:synopsis>
-    <adm:description>
-      Search operations with short search substring are likely to match
-      a high number of entries and might degrade performances. 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:property name="referral-policy" mandatory="false">
-    <adm:synopsis>
-      Specifies the referral policy.
-    </adm:synopsis>
-    <adm:description>
-      The referral policy defines the behavior when a referral is
-      received. The server can either discard the referral, forward
-      the referral to the client or follow the referral.
-    </adm:description>
-    <adm:default-behavior>
-      <adm:defined>
-        <adm:value>forward</adm:value>
-      </adm:defined>
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:enumeration>
-        <adm:value name="discard">
-          <adm:synopsis>Discard referrals.</adm:synopsis>
-        </adm:value>
-        <adm:value name="forward">
-          <adm:synopsis>Forward referrals.</adm:synopsis>
-        </adm:value>
-        <adm:value name="follow">
-          <adm:synopsis>Follow referrals</adm:synopsis>
-        </adm:value>
-      </adm:enumeration>
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-referral-policy</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="referral-bind-policy" mandatory="false">
-    <adm:synopsis>
-      Specifies the referral bind policy.
-    </adm:synopsis>
-    <adm:description>
-      The referral bind policy is used only when the referral policy
-      is set to "follow". It specifies whether the referral is followed
-      with the client bind DN and password or anonymously.
-    </adm:description>
-    <adm:default-behavior>
-      <adm:defined>
-        <adm:value>anonymous</adm:value>
-      </adm:defined>
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:enumeration>
-        <adm:value name="anonymous">
-          <adm:synopsis>Follow referrals anonymously.</adm:synopsis>
-        </adm:value>
-        <adm:value name="use-password">
-          <adm:synopsis>
-            Follow referrals with the client bind DN and password.
-          </adm:synopsis>
-        </adm:value>
-      </adm:enumeration>
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-referral-bind-policy</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-  <adm:property name="referral-hop-limit" mandatory="false">
-    <adm:synopsis>
-      Specifies the maximum number of hops when following referrals.
-    </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-referral-hop-limit</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
-
-</adm:managed-object>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/Package.xml b/opends/src/admin/defn/org/opends/server/admin/std/Package.xml
index c061b51..dee716a 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/Package.xml
+++ b/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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/QOSPolicyConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/QOSPolicyConfiguration.xml
new file mode 100644
index 0000000..0af495d
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/QOSPolicyConfiguration.xml
@@ -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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupRequestFilteringPolicyConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RequestFilteringQOSPolicyConfiguration.xml
similarity index 60%
rename from opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupRequestFilteringPolicyConfiguration.xml
rename to opends/src/admin/defn/org/opends/server/admin/std/RequestFilteringQOSPolicyConfiguration.xml
index 4c83f24..8354518 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupRequestFilteringPolicyConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/RequestFilteringQOSPolicyConfiguration.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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/ResourceLimitsQOSPolicyConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/ResourceLimitsQOSPolicyConfiguration.xml
new file mode 100644
index 0000000..725a07f
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/ResourceLimitsQOSPolicyConfiguration.xml
@@ -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>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
index 845a6f6..5f6250d 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml
+++ b/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>
diff --git a/opends/src/messages/messages/config.properties b/opends/src/messages/messages/config.properties
index 2f2fd20..0763759 100644
--- a/opends/src/messages/messages/config.properties
+++ b/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
diff --git a/opends/src/messages/messages/dsconfig.properties b/opends/src/messages/messages/dsconfig.properties
index 0e663e5..0d5e492 100644
--- a/opends/src/messages/messages/dsconfig.properties
+++ b/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
\ No newline at end of file
+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?
diff --git a/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java b/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
index b77e97d..14d5b1a 100644
--- a/opends/src/server/org/opends/server/admin/AbstractManagedObjectDefinition.java
+++ b/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);
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/AdminException.java b/opends/src/server/org/opends/server/admin/AdminException.java
index f6b2b93..dea9632 100644
--- a/opends/src/server/org/opends/server/admin/AdminException.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/AdminRuntimeException.java b/opends/src/server/org/opends/server/admin/AdminRuntimeException.java
index 47dfbd0..379e2d1 100644
--- a/opends/src/server/org/opends/server/admin/AdminRuntimeException.java
+++ b/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;
 
diff --git a/opends/src/server/org/opends/server/admin/DecodingException.java b/opends/src/server/org/opends/server/admin/DecodingException.java
index aa5c4a9..1affb2a 100644
--- a/opends/src/server/org/opends/server/admin/DecodingException.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/InstantiableRelationDefinition.java b/opends/src/server/org/opends/server/admin/InstantiableRelationDefinition.java
index 4b0bfed..2600a97 100644
--- a/opends/src/server/org/opends/server/admin/InstantiableRelationDefinition.java
+++ b/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");
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/LDAPProfile.java b/opends/src/server/org/opends/server/admin/LDAPProfile.java
index f5422e8..027b896 100644
--- a/opends/src/server/org/opends/server/admin/LDAPProfile.java
+++ b/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.
    *
diff --git a/opends/src/server/org/opends/server/admin/ManagedObjectPath.java b/opends/src/server/org/opends/server/admin/ManagedObjectPath.java
index eb2edb6..3d79b04 100644
--- a/opends/src/server/org/opends/server/admin/ManagedObjectPath.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java b/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
index 1bfd698..405f975 100644
--- a/opends/src/server/org/opends/server/admin/ManagedObjectPathSerializer.java
+++ b/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);
+
 }
diff --git a/opends/src/server/org/opends/server/admin/OptionalRelationDefinition.java b/opends/src/server/org/opends/server/admin/OptionalRelationDefinition.java
index 56cec94..2b5bf54 100644
--- a/opends/src/server/org/opends/server/admin/OptionalRelationDefinition.java
+++ b/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");
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/RelationDefinition.java b/opends/src/server/org/opends/server/admin/RelationDefinition.java
index e3cbce1..bc6a863 100644
--- a/opends/src/server/org/opends/server/admin/RelationDefinition.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/RelationDefinitionVisitor.java b/opends/src/server/org/opends/server/admin/RelationDefinitionVisitor.java
index 8a763cc..acbb429 100644
--- a/opends/src/server/org/opends/server/admin/RelationDefinitionVisitor.java
+++ b/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>
diff --git a/opends/src/server/org/opends/server/admin/SetRelationDefinition.java b/opends/src/server/org/opends/server/admin/SetRelationDefinition.java
new file mode 100644
index 0000000..426c97d
--- /dev/null
+++ b/opends/src/server/org/opends/server/admin/SetRelationDefinition.java
@@ -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();
+    }
+  }
+}
diff --git a/opends/src/server/org/opends/server/admin/SingletonRelationDefinition.java b/opends/src/server/org/opends/server/admin/SingletonRelationDefinition.java
index 1d965f6..2f4f746 100644
--- a/opends/src/server/org/opends/server/admin/SingletonRelationDefinition.java
+++ b/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");
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/client/AdminSecurityException.java b/opends/src/server/org/opends/server/admin/client/AdminSecurityException.java
index b24970a..2f92e71 100644
--- a/opends/src/server/org/opends/server/admin/client/AdminSecurityException.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/client/ManagedObject.java b/opends/src/server/org/opends/server/admin/client/ManagedObject.java
index 16af3a9..3b16697 100644
--- a/opends/src/server/org/opends/server/admin/client/ManagedObject.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/client/ManagementContext.java b/opends/src/server/org/opends/server/admin/client/ManagementContext.java
index e971c34..0a1d047 100644
--- a/opends/src/server/org/opends/server/admin/client/ManagementContext.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java b/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java
index de2d34c..9212cd1 100644
--- a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliGlobalAdmin.java
+++ b/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;
diff --git a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java b/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java
index 9383cc1..4fe04e5 100644
--- a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliParser.java
+++ b/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;
diff --git a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java b/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java
index ce2bb3f..f2fb276 100644
--- a/opends/src/server/org/opends/server/admin/client/cli/DsFrameworkCliServer.java
+++ b/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;
diff --git a/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliParser.java b/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliParser.java
index b7b455b..175ff7c 100644
--- a/opends/src/server/org/opends/server/admin/client/cli/SecureConnectionCliParser.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
index 5e0f300..c979a74 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPDriver.java
+++ b/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 {
diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
index 30c1c43..70a46b1 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPManagedObject.java
+++ b/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);
diff --git a/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java b/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
index a943e3a..0bbc756 100644
--- a/opends/src/server/org/opends/server/admin/client/ldap/LDAPNameBuilder.java
+++ b/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);
+    }
   }
 
 
diff --git a/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java b/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
index 0f1902b..947256d 100644
--- a/opends/src/server/org/opends/server/admin/client/spi/AbstractManagedObject.java
+++ b/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 {
diff --git a/opends/src/server/org/opends/server/admin/client/spi/Driver.java b/opends/src/server/org/opends/server/admin/client/spi/Driver.java
index 8befab9..ea501a0 100644
--- a/opends/src/server/org/opends/server/admin/client/spi/Driver.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java b/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
index 2129d2b..cb4093c 100644
--- a/opends/src/server/org/opends/server/admin/doc/ConfigGuideGeneration.java
+++ b/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;
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
index b07ab50..318e3b7 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigAddListenerAdaptor.java
+++ b/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) {
diff --git a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java b/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
index ea75d06..52eddb6 100644
--- a/opends/src/server/org/opends/server/admin/server/ConfigDeleteListenerAdaptor.java
+++ b/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) {
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java b/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
index 6efaf10..991e19c 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagedObject.java
+++ b/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);
   }
diff --git a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java b/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
index eba7c34..42fd5fd 100644
--- a/opends/src/server/org/opends/server/admin/server/ServerManagementContext.java
+++ b/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);
     }
diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index e2be916..e7d4e34 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/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();
     }
   }
 
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPolicy.java b/opends/src/server/org/opends/server/api/QOSPolicy.java
similarity index 67%
rename from opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPolicy.java
rename to opends/src/server/org/opends/server/api/QOSPolicy.java
index ec0d10b..d02ec5d 100644
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPolicy.java
+++ b/opends/src/server/org/opends/server/api/QOSPolicy.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.
+  }
 }
diff --git a/opends/src/server/org/opends/server/api/QOSPolicyFactory.java b/opends/src/server/org/opends/server/api/QOSPolicyFactory.java
new file mode 100644
index 0000000..6ee790d
--- /dev/null
+++ b/opends/src/server/org/opends/server/api/QOSPolicyFactory.java
@@ -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);
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteria.java
new file mode 100644
index 0000000..5c500d4
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteria.java
@@ -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;
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteria.java
new file mode 100644
index 0000000..202b91a
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteria.java
@@ -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;
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/AuthMethodCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/AuthMethodCriteria.java
deleted file mode 100644
index 34edede..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/AuthMethodCriteria.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.TreeSet;
-import
- org.opends.server.admin.std.meta.NetworkGroupCriteriaCfgDefn.AllowedAuthMethod;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.types.AuthenticationType;
-import org.opends.server.types.DN;
-
-/**
- * This class defines the authentication method criteria.
- * A connection matches the criteria if the authentication
- * method used on this connection is one of the allowed
- * authentication methods specified in the criteria.
- */
-
-public class AuthMethodCriteria implements NetworkGroupCriterion {
-  private Collection<AllowedAuthMethod> authMethods;
-
-  /**
-   * Constructor.
-   */
-  public AuthMethodCriteria() {
-    authMethods = new TreeSet<AllowedAuthMethod>();
-  }
-
-  /**
-   * Adds a new allowed authentication method to the list of allowed
-   * authentication methods.
-   * @param method The authentication method
-   */
-  public void addAuthMethod(AllowedAuthMethod method) {
-    authMethods.add(method);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    Collection<AuthenticationType> authTypes =
-         new ArrayList<AuthenticationType>();
-
-    for (AllowedAuthMethod method:authMethods) {
-      if (method == AllowedAuthMethod.ANONYMOUS) {
-        if (connection.getAuthenticationInfo().isAuthenticated() == false) {
-          return (true);
-        }
-      } else if (method == AllowedAuthMethod.SASL) {
-        authTypes.add(AuthenticationType.SASL);
-      } else if (method == AllowedAuthMethod.SIMPLE) {
-        authTypes.add(AuthenticationType.SIMPLE);
-      }
-    }
-    return (connection.getAuthenticationInfo().hasAnyAuthenticationType(
-         authTypes));
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection, DN bindDN,
-            AuthenticationType authType, boolean isSecure) {
-    for (AllowedAuthMethod method:authMethods) {
-      if (method == AllowedAuthMethod.ANONYMOUS
-          && bindDN.toNormalizedString().equals("")) {
-        return true;
-      }
-      if (method == AllowedAuthMethod.SASL
-          && authType == AuthenticationType.SASL) {
-        return true;
-      }
-      if (method == AllowedAuthMethod.SIMPLE
-          && authType == AuthenticationType.SIMPLE
-          && !bindDN.toNormalizedString().equals("")) {
-        return true;
-      }
-    }
-    return false;
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteria.java
new file mode 100644
index 0000000..f6b9aa9
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteria.java
@@ -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;
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/BindDnCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/BindDnCriteria.java
deleted file mode 100644
index c4ac72f..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/BindDnCriteria.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-import java.util.ArrayList;
-import java.util.Collection;
-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;
-
-/**
- * This class defines the client bind DN criteria.
- * A client connection matches the criteria when the DN used by
- * the client to bind matches at least one of the DN patterns.
- */
-public class BindDnCriteria implements NetworkGroupCriterion {
-
-  private Collection<PatternDN> patternDNs;
-
-  /**
-   * Constructor.
-   */
-  public BindDnCriteria() {
-    patternDNs = new ArrayList<PatternDN>();
-  }
-
-  /**
-   * Adds a new bind DN filter to the list of bind DN filters.
-   * @param filter The bind DN filter
-   * @throws DirectoryException if the filter is not a valid filter
-   */
-  public void addBindDnFilter(String filter)
-  throws DirectoryException {
-    patternDNs.add(PatternDN.decode(filter));
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    DN dn = connection.getAuthenticationInfo().getAuthenticationDN();
-    return matchAfterBind(connection, dn, null, false);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection, DN bindDN,
-            AuthenticationType authType, boolean isSecure) {
-    if (bindDN == null) {
-      return false;
-    }
-    for (PatternDN patternDN:patternDNs) {
-      if (patternDN.matchesDN(bindDN)) {
-        return (true);
-      }
-    }
-    return (false);
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ClientConnectionAffinityPolicy.java b/opends/src/server/org/opends/server/core/networkgroups/ClientConnectionAffinityPolicy.java
deleted file mode 100644
index af53156..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/ClientConnectionAffinityPolicy.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.std.meta.NetworkGroupCfgDefn.AffinityPolicy;
-
-
-/**
- * This enumerate defines the client connection affinity policy.
- * An affinity is the ability for the server to bypass some
- * route algorithm so that requests are always sent to a specific
- * data source.
- */
-public enum ClientConnectionAffinityPolicy
-{
-  /**
-   * Disables the client connection affinity.
-   */
-  NONE,
-
-  /**
-   * 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.
-   */
-  FIRST_READ_REQUEST_AFTER_WRITE_REQUEST,
-
-  /**
-   * 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.
-   */
-  ALL_WRITE_REQUESTS_AFTER_FIRST_WRITE_REQUEST,
-
-  /**
-   * Routes all the requests to the data source to which a
-   * previous write request has been routed to.
-   */
-  ALL_REQUESTS_AFTER_FIRST_WRITE_REQUEST,
-
-  /**
-   * 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.
-   */
-  ALL_REQUESTS_AFTER_FIRST_REQUEST;
-
-
-  /**
-   * Indicates whether the current policy defines an active affinity
-   * policy or not.
-   *
-   * @return <code>true</code> if the current value of the policy indicates
-   *         an active affinity.
-   */
-  public boolean affinityIsActive()
-  {
-    return (this != NONE);
-  }
-
-
-  /**
-   * Returns the client connection affinity policy that matches the
-   * affinity policy as defined by the administration framework.
-   *
-   * @param  affinityPolicy
-   *         The administration framework affinity policy for which we
-   *         want to make a determination.
-   *
-   * @return the client connection affinity policy that matches an affinity
-   *         policy as defined by the administration framework.
-   */
-  public static ClientConnectionAffinityPolicy
-      toClientConnectionAffinityPolicy(AffinityPolicy affinityPolicy)
-  {
-    switch (affinityPolicy)
-    {
-      case NONE:
-        return NONE;
-
-      case ALL_REQUESTS_AFTER_FIRST_WRITE_REQUEST:
-        return ALL_REQUESTS_AFTER_FIRST_WRITE_REQUEST;
-
-      case ALL_REQUESTS_AFTER_FIRST_REQUEST:
-        return ALL_REQUESTS_AFTER_FIRST_REQUEST;
-
-      case ALL_WRITE_REQUESTS_AFTER_FIRST_WRITE_REQUEST:
-        return ALL_WRITE_REQUESTS_AFTER_FIRST_WRITE_REQUEST;
-
-      case FIRST_READ_REQUEST_AFTER_WRITE_REQUEST:
-        return FIRST_READ_REQUEST_AFTER_WRITE_REQUEST;
-
-      default:
-        throw new AssertionError(
-          "Unexpected afinity policy value " + affinityPolicy);
-    }
-  }
-
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/ConnectionCriteria.java
new file mode 100644
index 0000000..e801a5e
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ConnectionCriteria.java
@@ -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);
+
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/IPConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/IPConnectionCriteria.java
new file mode 100644
index 0000000..6575a22
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/IPConnectionCriteria.java
@@ -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);
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/IpFilterCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/IpFilterCriteria.java
deleted file mode 100644
index 32e4c0d..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/IpFilterCriteria.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.HashSet;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.types.AddressMask;
-import org.opends.server.types.AuthenticationType;
-import org.opends.server.types.DN;
-
-/**
- * This class defines the IP filter criteria.
- * A client connection matches the criteria when it is performed from
- * a host whose IP address matches at least one of the specified filters.
- */
-public class IpFilterCriteria implements NetworkGroupCriterion {
-
-  private Collection<AddressMask> ipFilters;
-
-  /**
-   * Constructor.
-   */
-  public IpFilterCriteria() {
-    ipFilters = new HashSet<AddressMask>();
-  }
-
-  /**
-   * Adds a new Ip Filter to the list of allowed IP filters.
-   * @param filter The new IP filter
-   */
-  public void addIpFilter(AddressMask filter) {
-    ipFilters.add(filter);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    InetAddress ipAddr = connection.getRemoteAddress();
-    if (AddressMask.maskListContains(ipAddr.getAddress(),
-                                     ipAddr.getCanonicalHostName(),
-                                     ipFilters.toArray(new AddressMask[0]))) {
-      return (true);
-    } else {
-      return false;
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection, DN bindDN,
-                                AuthenticationType authType, boolean isSecure) {
-    return (match(connection));
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
index 9afa115..60c1f11 100644
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroup.java
+++ b/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);
-  }
 }
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
index 781574e..5608ad9 100644
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupConfigManager.java
+++ b/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;
   }
 
 }
-
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriteria.java
deleted file mode 100644
index f8b3c7a..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriteria.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-
-import org.opends.server.core.*;
-import java.util.ArrayList;
-import java.util.List;
-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.meta.NetworkGroupCriteriaCfgDefn.AllowedAuthMethod;
-import
- org.opends.server.admin.std.meta.NetworkGroupCriteriaCfgDefn.AllowedLDAPPort;
-import org.opends.server.admin.std.server.NetworkGroupCriteriaCfg;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.types.AddressMask;
-import org.opends.server.types.AuthenticationType;
-import org.opends.server.types.ConfigChangeResult;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
-import org.opends.server.types.ResultCode;
-
-
-/**
- * This class defines the network group criteria. A criterion is used
- * by the network groups to determine whether a client connection belongs
- * to the network group or not.
- */
-public class NetworkGroupCriteria
-        implements ConfigurationAddListener<NetworkGroupCriteriaCfg>,
-                   ConfigurationDeleteListener<NetworkGroupCriteriaCfg>,
-                   ConfigurationChangeListener<NetworkGroupCriteriaCfg>,
-                   NetworkGroupCriterion
-
-{
-  // Indicates whether the criteria are defined through the config
-  private boolean isConfigured = false;
-
-  // The network group criteria.
-  private AuthMethodCriteria authMethodCriteria;
-  private BindDnCriteria bindDnCriteria;
-  private IpFilterCriteria ipFilterCriteria;
-  private PortCriteria portCriteria;
-  private SecurityCriteria securityCriteria;
-
-  // The current config
-  private NetworkGroupCriteriaCfg config;
-
-  /**
-   * Constructor.
-   *
-   * @param criteriaCfg the configuration object used to build the Network
-   *        Group Criteria.
-   * @throws DirectoryException If the criteria could not be created because
-   *                            of an invalid configuration parameter
-   */
-  public NetworkGroupCriteria(NetworkGroupCriteriaCfg criteriaCfg)
-          throws DirectoryException {
-    createCriteria(criteriaCfg);
-  }
-
-  /**
-   * Resets all the fields.
-   */
-  private void resetCriteria() {
-    authMethodCriteria = null;
-    bindDnCriteria = null;
-    ipFilterCriteria = null;
-    portCriteria = null;
-    securityCriteria = null;
-    isConfigured = false;
-    if (config != null) {
-      config.removeChangeListener(this);
-      config = null;
-    }
-  }
-
-  /**
-   * Creates a new NetworkGroupCriteria based on the configuration object.
-   *
-   * @param criteriaCfg the configuration
-   * @throws DirectoryException If the bind-dn-filter is not a valid DN filter
-   */
-  private void createCriteria(NetworkGroupCriteriaCfg criteriaCfg)
-          throws DirectoryException {
-    if (criteriaCfg != null) {
-      if (!criteriaCfg.getAllowedAuthMethod().isEmpty()) {
-        authMethodCriteria = new AuthMethodCriteria();
-        for (AllowedAuthMethod method: criteriaCfg.getAllowedAuthMethod()) {
-          authMethodCriteria.addAuthMethod(method);
-        }
-      } else {
-        authMethodCriteria = null;
-      }
-      if (!criteriaCfg.getBindDNFilter().isEmpty()) {
-        bindDnCriteria = new BindDnCriteria();
-        for (String filter: criteriaCfg.getBindDNFilter()) {
-          bindDnCriteria.addBindDnFilter(filter);
-        }
-      } else {
-        bindDnCriteria = null;
-      }
-      if (!criteriaCfg.getIPAddressFilter().isEmpty()) {
-        ipFilterCriteria = new IpFilterCriteria();
-        for (AddressMask filter: criteriaCfg.getIPAddressFilter()) {
-          ipFilterCriteria.addIpFilter(filter);
-        }
-      } else {
-        ipFilterCriteria = null;
-      }
-      if (!criteriaCfg.getAllowedLDAPPort().isEmpty()) {
-        portCriteria = new PortCriteria();
-        for (AllowedLDAPPort port : criteriaCfg.getAllowedLDAPPort()) {
-          portCriteria.addPort(port);
-        }
-      } else {
-        portCriteria = null;
-      }
-      if (criteriaCfg.isIsSecurityMandatory()) {
-        securityCriteria = new SecurityCriteria(true);
-      } else {
-        securityCriteria = null;
-      }
-      isConfigured = true;
-      if (config == null) {
-        criteriaCfg.addChangeListener(this);
-      }
-      config = criteriaCfg;
-    } else {
-      resetCriteria();
-    }
-  }
-
-  /**
-   * Sets the authentication method criteria.
-   * @param criteria The authentication method criteria
-   */
-  public void setAuthMethodCriteria(AuthMethodCriteria criteria) {
-    authMethodCriteria = criteria;
-    isConfigured = true;
-  }
-
-  /**
-   * Sets the bind dn criteria.
-   * @param criteria The bind DN criteria
-   */
-  public void setBindDnCriteria(BindDnCriteria criteria) {
-    bindDnCriteria = criteria;
-    isConfigured = true;
-  }
-
-  /**
-   * Sets the IP filter criteria.
-   * @param criteria The IP filter criteria
-   */
-  public void setIpFilterCriteria(IpFilterCriteria criteria) {
-    ipFilterCriteria = criteria;
-    isConfigured = true;
-  }
-
-  /**
-   * Sets the port criteria.
-   * @param criteria The IP filter criteria
-   */
-  public void setPortCriteria(PortCriteria criteria) {
-    portCriteria = criteria;
-    isConfigured = true;
-  }
-
-  /**
-   * Sets the IP filter criteria.
-   * @param criteria The IP filter criteria
-   */
-  public void setSecurityCriteria(SecurityCriteria criteria) {
-    securityCriteria = criteria;
-    isConfigured = true;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    if ((authMethodCriteria != null)
-    && (!authMethodCriteria.match(connection))) {
-      return (false);
-    }
-    if ((bindDnCriteria != null) && (!bindDnCriteria.match(connection))) {
-      return (false);
-    }
-    if ((ipFilterCriteria != null) && (!ipFilterCriteria.match(connection))) {
-      return (false);
-    }
-    if ((portCriteria != null) && (!portCriteria.match(connection))) {
-      return (false);
-    }
-    if ((securityCriteria != null) && (!securityCriteria.match(connection))) {
-      return (false);
-    }
-    return (true);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection,
-          DN bindDN,
-          AuthenticationType authType,
-          boolean isSecure) {
-    if ((authMethodCriteria != null) && (!authMethodCriteria.matchAfterBind(
-            connection, bindDN, authType, isSecure))) {
-      return (false);
-    }
-    if ((bindDnCriteria != null) && (!bindDnCriteria.matchAfterBind(
-            connection, bindDN, authType, isSecure))) {
-      return (false);
-    }
-    if ((ipFilterCriteria != null) && (!ipFilterCriteria.matchAfterBind(
-            connection, bindDN, authType, isSecure))) {
-      return (false);
-    }
-    if ((portCriteria != null) && (!portCriteria.matchAfterBind(
-            connection, bindDN, authType, isSecure))) {
-      return (false);
-    }
-    if ((securityCriteria != null) && (!securityCriteria.matchAfterBind(
-            connection, bindDN, authType, isSecure))) {
-      return (false);
-    }
-    return (true);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationAddAcceptable(
-          NetworkGroupCriteriaCfg configuration,
-          List<Message> unacceptableReasons) {
-    return (!isConfigured);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationAdd(
-          NetworkGroupCriteriaCfg cfg) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-          new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    try {
-      createCriteria(cfg);
-    } catch (DirectoryException de) {
-      if (resultCode == ResultCode.SUCCESS) {
-        resultCode = DirectoryServer.getServerErrorResultCode();
-      }
-      messages.add(de.getMessageObject());
-    }
-    return configChangeResult;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationDeleteAcceptable(
-          NetworkGroupCriteriaCfg configuration,
-          List<Message> unacceptableReasons) {
-    return (isConfigured);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationDelete(
-          NetworkGroupCriteriaCfg configuration) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-      new ConfigChangeResult(resultCode, adminActionRequired, messages);
-
-    resetCriteria();
-
-    return configChangeResult;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationChangeAcceptable(
-          NetworkGroupCriteriaCfg configuration,
-          List<Message> unacceptableReasons) {
-    return true;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationChange(
-          NetworkGroupCriteriaCfg cfg) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-          new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    try {
-      createCriteria(cfg);
-    } catch (DirectoryException de) {
-      if (resultCode == ResultCode.SUCCESS) {
-        resultCode = DirectoryServer.getServerErrorResultCode();
-      }
-      messages.add(de.getMessageObject());
-    }
-    return configChangeResult;
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriterion.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriterion.java
deleted file mode 100644
index ad39a13..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupCriterion.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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 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;
-
-/**
- * This class defines the network group criteria. A criterion is used
- * by the network groups to determine whether a client connection belongs
- * to the network group or not.
- */
-public interface NetworkGroupCriterion
-{
-
-  /**
-   * Checks whether the client connection matches the criteria.
-   *
-   * @param connection the ClientConnection
-   * @return a boolean indicating whether the connection matches the criteria
-   */
-  public boolean match(ClientConnection connection);
-
-  /**
-   * 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
-   */
-  public boolean matchAfterBind(ClientConnection connection,
-          DN bindDN,
-          AuthenticationType authType,
-          boolean isSecure);
-
-}
diff --git a/opends/src/server/org/opends/server/plugins/NetworkGroupPlugin.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPlugin.java
similarity index 97%
rename from opends/src/server/org/opends/server/plugins/NetworkGroupPlugin.java
rename to opends/src/server/org/opends/server/core/networkgroups/NetworkGroupPlugin.java
index b643a5f..468f69f 100644
--- a/opends/src/server/org/opends/server/plugins/NetworkGroupPlugin.java
+++ b/opends/src/server/org/opends/server/core/networkgroups/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);
   }
 }
diff --git a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupStatistics.java b/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupStatistics.java
index c323e1d..b3041f8 100644
--- a/opends/src/server/org/opends/server/core/networkgroups/NetworkGroupStatistics.java
+++ b/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",
diff --git a/opends/src/server/org/opends/server/core/networkgroups/PortCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/PortCriteria.java
deleted file mode 100644
index cba4fd0..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/PortCriteria.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import
-  org.opends.server.admin.std.meta.NetworkGroupCriteriaCfgDefn.AllowedLDAPPort;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.types.AuthenticationType;
-import org.opends.server.types.DN;
-
-/**
- * This class defines the port criteria.
- * A client connection matches the criteria when it is received in
- * a port matching at least one of the specified ports.
- * The port can be "ldap" or "ldaps".
- */
-public class PortCriteria implements NetworkGroupCriterion {
-  private Collection<String> allowedPorts;
-
-  /**
-   * Constructor.
-   */
-  public PortCriteria() {
-    allowedPorts = new ArrayList<String>();
-  }
-
-  /**
-   * Adds a new allowed LDAP port to the list of allowed LDAP ports.
-   * @param port The new LDAP port
-   */
-  public void addPort(AllowedLDAPPort port) {
-    if (port.toString().equals("ldap")) {
-      allowedPorts.add("LDAP");
-    } else if (port.toString().equals("ldaps")) {
-      allowedPorts.add("LDAP+SSL");
-    }
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    String connectionPort = connection.getConnectionHandler().getProtocol();
-    for (String port:allowedPorts) {
-      if (connectionPort.equalsIgnoreCase(port)) {
-        return true;
-      }
-    }
-    return (false);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection, DN bindDN,
-                                AuthenticationType authType, boolean isSecure) {
-    return (match(connection));
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ProtocolConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/ProtocolConnectionCriteria.java
new file mode 100644
index 0000000..eab4a65
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ProtocolConnectionCriteria.java
@@ -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);
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicy.java b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicy.java
index 8d1f938..1fab56d 100644
--- a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicy.java
+++ b/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);
 }
diff --git a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyFactory.java b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyFactory.java
new file mode 100644
index 0000000..945f25f
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyFactory.java
@@ -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);
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStat.java b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStat.java
deleted file mode 100644
index 3985208..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStat.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-
-package org.opends.server.core.networkgroups;
-
-/**
- * This class implements the statistics associated to a
- * network group request filtering policy.
- */
-public class RequestFilteringPolicyStat {
-  private long rejectedAttributes;
-  private long rejectedOperations;
-  private long rejectedSubtrees;
-  private long rejectedScopes;
-  private Object statLock;
-
-  /**
-   * Constructor.
-   */
-  public RequestFilteringPolicyStat() {
-    rejectedAttributes = 0;
-    rejectedOperations = 0;
-    rejectedSubtrees = 0;
-    rejectedScopes = 0;
-    statLock = new Object();
-  }
-
-  /**
-   * Increments the number of rejected operations due to an
-   * attribute not allowed by the request filtering policy.
-   */
-  public void updateRejectedAttributes() {
-    synchronized(statLock) {
-      rejectedAttributes++;
-    }
-  }
-
-  /**
-   * Increments the number of rejected operations due to an
-   * operation type not allowed by the request filtering policy.
-   */
-  public void updateRejectedOperations() {
-    synchronized(statLock) {
-      rejectedOperations++;
-    }
-  }
-
-  /**
-   * Increments the number of rejected operations due to a subtree
-   * not allowed by the request filtering policy.
-   */
-  public void updateRejectedSubtrees() {
-    synchronized(statLock) {
-      rejectedSubtrees++;
-    }
-  }
-
-  /**
-   * Increments the number of rejected operations due to a search scope
-   * not allowed by the request filtering policy.
-   */
-  public void updateRejectedScopes() {
-    synchronized(statLock) {
-      rejectedScopes++;
-    }
-  }
-
-  /**
-   * Retrieves the number of rejected operations due to an
-   * attribute not allowed by the request filtering policy.
-   * @return number of rejected operations due to an invalid attribute
-   */
-  public long getRejectedAttributes() {
-    return rejectedAttributes;
-  }
-
-  /**
-   * Retrieves the number of rejected operations due to an
-   * operation type not allowed by the request filtering policy.
-   * @return number of rejected operations due to an invalid op type
-   */
-  public long getRejectedOperations() {
-    return rejectedOperations;
-  }
-
-  /**
-   * Retrieves the number of rejected operations due to a
-   * subtree not allowed by the request filtering policy.
-   * @return number of rejected operations due to an invalid subtree
-   */
-  public long getRejectedSubtrees() {
-    return rejectedSubtrees;
-  }
-
-  /**
-   * Retrieves the number of rejected operations due to a
-   * scope not allowed by the request filtering policy.
-   * @return number of rejected operations due to an invalid scope
-   */
-  public long getRejectedScopes() {
-    return rejectedScopes;
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStatistics.java b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStatistics.java
new file mode 100644
index 0000000..1f9da11
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyStatistics.java
@@ -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();
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimits.java b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimits.java
deleted file mode 100644
index b1f116b..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimits.java
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-package org.opends.server.core.networkgroups;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-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.meta.
-        NetworkGroupResourceLimitsCfgDefn.ReferralBindPolicy;
-import org.opends.server.admin.std.meta.
-        NetworkGroupResourceLimitsCfgDefn.ReferralPolicy;
-import org.opends.server.admin.std.server.NetworkGroupResourceLimitsCfg;
-import org.opends.server.api.ClientConnection;
-import org.opends.server.types.ByteString;
-import org.opends.server.types.ConfigChangeResult;
-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;
-import static org.opends.messages.CoreMessages.*;
-
-
-/**
- * This class defines the resource limits applicable to all
- * connections inside the same network group.
- */
-public class ResourceLimits
-        implements ConfigurationAddListener<NetworkGroupResourceLimitsCfg>,
-                   ConfigurationDeleteListener<NetworkGroupResourceLimitsCfg>,
-                   ConfigurationChangeListener<NetworkGroupResourceLimitsCfg>
-{
-  // The resource limits are defined through the config
-  private boolean isConfigured = false;
-
-  // 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 maximum number of concurrent operations per connection
-  private int maxConcurrentOpsPerConnection;
-
-  // The maximum size for a search
-  private int searchSizeLimit;
-
-  // The maximum duration for a search
-  private int searchTimeLimit;
-
-  // The minimum substring length in a search
-  private int minSearchSubstringLength;
-
-  // The referral policy
-  private ReferralPolicy referralPolicy = ReferralPolicy.FORWARD;
-
-  // The referral policy
-  private ReferralBindPolicy referralBindPolicy = ReferralBindPolicy.ANONYMOUS;
-
-  // The referral hop limit
-  private int referralHopLimit = 0;
-
-  // The number of connections in the group
-  private int numConnections = 0;
-
-  // The maximum number of simultaneous connections in the group
-  // since group creation
-  private int maxNumConnections = 0;
-
-  // The total number of connections managed by the group
-  private int totalNumConnections = 0;
-
-  // Map containing the connections sorted by incoming IP address
-  HashMap<String, Integer> connectionsPerIpMap = new HashMap<String, Integer>();
-
-  // The lock for the counter numConnections and the map connectionsPerIpMap
-  Object connMutex = new Object();
-
-  // The current configuration
-  private NetworkGroupResourceLimitsCfg config = null;
-
-  /**
-   * Constructor.
-   *
-   * @param resourceLimitsCfg configuration
-   */
-  public ResourceLimits(NetworkGroupResourceLimitsCfg resourceLimitsCfg) {
-    createLimits(resourceLimitsCfg);
-  }
-
-  /**
-   * Resets all the fields.
-   */
-  private void resetLimits() {
-    maxConnections = 0;
-    maxConnectionsFromSameIP = 0;
-    maxOpsPerConnection = 0;
-    maxConcurrentOpsPerConnection = 0;
-
-    searchSizeLimit = -1;
-    searchTimeLimit = -1;
-    minSearchSubstringLength = 0;
-
-    referralPolicy = ReferralPolicy.FORWARD;
-    referralBindPolicy = ReferralBindPolicy.ANONYMOUS;
-    referralHopLimit = 0;
-
-    isConfigured = false;
-    if (config != null) {
-      config.removeChangeListener(this);
-      config = null;
-    }
-  }
-
-  /**
-   * Creates a ResourceLimits from a configuration object.
-   *
-   * @param resourcesCfg the configuration
-   */
-  private void createLimits(NetworkGroupResourceLimitsCfg resourcesCfg) {
-    if (resourcesCfg != null) {
-      maxConnections = resourcesCfg.getMaxConnections();
-      maxConnectionsFromSameIP = resourcesCfg.getMaxConnectionsFromSameIP();
-      maxOpsPerConnection = resourcesCfg.getMaxOpsPerConnection();
-      maxConcurrentOpsPerConnection =
-            resourcesCfg.getMaxConcurrentOpsPerConnection();
-
-      Integer tmpSizeLimit = resourcesCfg.getSearchSizeLimit();
-      if (tmpSizeLimit != null) {
-        searchSizeLimit = tmpSizeLimit;
-      } else {
-        searchSizeLimit = -1;
-      }
-      Long tmpTimeLimit = resourcesCfg.getSearchTimeLimit();
-      if (tmpTimeLimit != null) {
-        searchTimeLimit = tmpTimeLimit.intValue();
-      } else {
-        searchTimeLimit = -1;
-      }
-      minSearchSubstringLength = resourcesCfg.getMinSubstringLength();
-
-      referralPolicy = resourcesCfg.getReferralPolicy();
-      referralBindPolicy = resourcesCfg.getReferralBindPolicy();
-      referralHopLimit = resourcesCfg.getReferralHopLimit();
-
-      if (config == null) {
-        resourcesCfg.addChangeListener(this);
-      }
-      config = resourcesCfg;
-      isConfigured = true;
-    } else {
-      resetLimits();
-    }
-  }
-
-  /**
-   * Sets the maximum number of connections allowed in this network group.
-   * @param maxConn The maximum number of connections handled by this
-   *                network group
-   */
-  public void setMaxConnections(int maxConn) {
-    maxConnections = maxConn;
-  }
-
-  /**
-   * Sets the maximum number of connections coming from the same client
-   * in this network group.
-   * @param maxConnFromSameClient The maximum number of connections coming
-   *                              from the same client in this network group
-   */
-  public void setMaxConnectionsFromSameIP(int maxConnFromSameClient) {
-    maxConnectionsFromSameIP = maxConnFromSameClient;
-  }
-
-  /**
-   * Sets the maximum number of operations performed on the same connection
-   * in this network group.
-   * @param maxOpsPerConn The maximum number of operations performed on
-   *                      the same connection
-   */
-  public void setMaxOpsPerConnection(int maxOpsPerConn) {
-    maxOpsPerConnection = maxOpsPerConn;
-  }
-
-  /**
-   * Sets the maximum number of concurrent operations performed on the same
-   * connection in this network group.
-   * @param maxConcurrentOpsPerConn The maximum number of simultaneous
-   *                                operations per connection on the same
-   *                                connection
-   */
-  public void setMaxConcurrentOpsPerConnection(int maxConcurrentOpsPerConn) {
-    maxConcurrentOpsPerConnection = maxConcurrentOpsPerConn;
-  }
-
-  /**
-   * Sets the search time limit for operations performed in this network group.
-   * @param maxSearchTime The search time limit
-   */
-  public void setSearchTimeLimit(int maxSearchTime) {
-    searchTimeLimit = maxSearchTime;
-  }
-
-  /**
-   * Sets the search size limit for operations performed in this network group.
-   * @param maxSearchSize The search size limit
-   */
-  public void setSearchSizeLimit(int maxSearchSize) {
-    searchSizeLimit = maxSearchSize;
-  }
-
-  /**
-   * Sets the minimum substring length for a search filter in this network
-   * group.
-   * @param minLength The minimum substring length
-   */
-  public void setMinSearchSubstringLength(int minLength) {
-    minSearchSubstringLength = minLength;
-  }
-
-  /**
-   * Returns the maximum number of entries returned by a search operation
-   * performed in this network group.
-   * @return the maximum number of entries
-   */
-  public int getSizeLimit() {
-      return searchSizeLimit;
-  }
-
-  /**
-   * Returns the maximum duration for a search operation performed in this
-   * network group.
-   * @return the maximum duration in ms
-   */
-  public int getTimeLimit() {
-      return searchTimeLimit;
-  }
-
-  /**
-   * Returns the minimum string length for a substring filter.
-   * @return minimum string length
-   */
-  public int getMinSubstring() {
-      return minSearchSubstringLength;
-  }
-
-  /**
-   * Returns the referral policy.
-   * @return referral policy
-   */
-  public ReferralPolicy getReferralPolicy() {
-    return referralPolicy;
-  }
-
-  /**
-   * Returns the referralBindPolicy.
-   * @return referral bind policy
-   */
-  public ReferralBindPolicy getReferralBindPolicy() {
-    return referralBindPolicy;
-  }
-
-  /**
-   * Returns the referral hop limit.
-   * @return referral hop limit
-   */
-  public int getReferralHopLimit() {
-    return referralHopLimit;
-  }
-
-  /**
-   * Adds a connection to the resource group.
-   *
-   * @param connection the ClientConnection to ad
-   */
-  public void addConnection(ClientConnection connection) {
-    synchronized(connMutex) {
-      // increment the number of connections managed by the network group
-      numConnections++;
-      totalNumConnections++;
-      if (numConnections > maxNumConnections) {
-        maxNumConnections = numConnections;
-      }
-
-      // increment the number of connections from the given IP address
-      String ip = connection.getClientAddress();
-      Integer currentCount = connectionsPerIpMap.get(ip);
-      if (currentCount == null) {
-        currentCount = new Integer(0);
-      }
-
-      connectionsPerIpMap.put(ip, currentCount + 1);
-    }
-  }
-
-  /**
-   * Removes a connection from the nerwork group.
-   *
-   * @param connection the ClientConnection to remove
-   */
-  public void removeConnection(ClientConnection connection) {
-    synchronized(connMutex) {
-      // decrement the number of connections managed by the network group
-      numConnections--;
-
-      // decrement the number of connections from the given IP address
-      String ip = connection.getClientAddress();
-      Integer currentCount = connectionsPerIpMap.get(ip);
-      if (currentCount == null) {
-        // Should be error!
-        currentCount = new Integer(1);
-      }
-      if (currentCount == 1) {
-        // This was the last connection
-        connectionsPerIpMap.remove(ip);
-      } else {
-        connectionsPerIpMap.put(ip, currentCount - 1);
-      }
-    }
-  }
-
-  /**
-   * Checks the resource limits.
-   *
-   * @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
-   */
-  public boolean checkLimits(
-          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(connMutex) {
-        if ((maxConnections > 0) && (numConnections > 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(connMutex) {
-        // 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;
-  }
-
-  /**
-   * 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;
-    }
-  }
-
-  /**
-   * Retrieves the statistics associated to the resource limits.
-   * @return the statistics
-   */
-  public ResourceLimitsStat getStat() {
-    ResourceLimitsStat stat;
-    synchronized(connMutex) {
-      stat = new ResourceLimitsStat(
-          numConnections, maxNumConnections, totalNumConnections);
-    }
-    return stat;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationAddAcceptable(
-          NetworkGroupResourceLimitsCfg configuration,
-          List<Message> unacceptableReasons) {
-    return (!isConfigured);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationAdd(
-          NetworkGroupResourceLimitsCfg configuration) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-          new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    createLimits(configuration);
-    return configChangeResult;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationDeleteAcceptable(
-          NetworkGroupResourceLimitsCfg configuration,
-          List<Message> unacceptableReasons) {
-    return isConfigured;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationDelete(
-          NetworkGroupResourceLimitsCfg configuration) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-      new ConfigChangeResult(resultCode, adminActionRequired, messages);
-
-    resetLimits();
-    isConfigured = false;
-
-    return configChangeResult;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isConfigurationChangeAcceptable(
-          NetworkGroupResourceLimitsCfg configuration,
-          List<Message> unacceptableReasons) {
-    return true;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public ConfigChangeResult applyConfigurationChange(
-          NetworkGroupResourceLimitsCfg configuration) {
-    ResultCode resultCode = ResultCode.SUCCESS;
-    boolean adminActionRequired = false;
-    ArrayList<Message> messages = new ArrayList<Message>();
-
-    ConfigChangeResult configChangeResult =
-          new ConfigChangeResult(resultCode, adminActionRequired, messages);
-    createLimits(configuration);
-    return configChangeResult;
-  }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicy.java b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicy.java
new file mode 100644
index 0000000..157808b
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicy.java
@@ -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);
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyFactory.java b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyFactory.java
new file mode 100644
index 0000000..3516a5f
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyFactory.java
@@ -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);
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyStatistics.java b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyStatistics.java
new file mode 100644
index 0000000..bb74270
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyStatistics.java
@@ -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--;
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsStat.java b/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsStat.java
deleted file mode 100644
index 2aad532..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/ResourceLimitsStat.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 Sun Microsystems, Inc.
- */
-
-package org.opends.server.core.networkgroups;
-
-
-/**
- * This class implements the statistics associated to a network group
- * resource limit.
- */
-public class ResourceLimitsStat {
-    private long clientConnections;
-    private long maxClientConnections;
-    private long totalClientConnections;
-
-    /**
-     * Constructor.
-     * @param clientConnections number of client connections currently
-     *        in the network group
-     * @param maxClientConnections maximum number of simultaneous
-     *        connections in the network group
-     * @param totalClientConnections total number of client connections
-     *        managed by the network group since its creation
-     */
-    public ResourceLimitsStat(
-        long clientConnections,
-        long maxClientConnections,
-        long totalClientConnections) {
-      this.clientConnections = clientConnections;
-      this.maxClientConnections = maxClientConnections;
-      this.totalClientConnections = totalClientConnections;
-    }
-
-    /**
-     * Returns the number of client connections currently in the network
-     * group.
-     * @return number of client connections currently in the network
-     * group
-     */
-    public 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
-     */
-    public long getMaxClientConnections() {
-      return maxClientConnections;
-    }
-
-    /**
-     * Returns the total number of client connections managed by the
-     * network group since its creation.
-     * @return the cumulated number of client connections managed by
-     * the network group since its creation
-     */
-    public long getTotalClientConnections() {
-      return totalClientConnections;
-    }
-}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteria.java
new file mode 100644
index 0000000..ac5ded8
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteria.java
@@ -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;
+    }
+  }
+}
diff --git a/opends/src/server/org/opends/server/core/networkgroups/SecurityCriteria.java b/opends/src/server/org/opends/server/core/networkgroups/SecurityCriteria.java
deleted file mode 100644
index b84a2be..0000000
--- a/opends/src/server/org/opends/server/core/networkgroups/SecurityCriteria.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 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;
-
-/**
- * This class defines a Security criteria.
- * The criteria specifies whether all connections are allowed
- * or only secured connections.
- * A connection is considered secure if it takes place over
- * SSL or with StartTLS.
- */
-public class SecurityCriteria implements NetworkGroupCriterion {
-
-  // If the Security Criteria is enabled,
-  // a connection matches only if it is secured
-  // Otherwise (Security Criteria disabled),
-  // all connections match
-  private boolean enabled = true;
-
-  /**
-   * Constructor.
-   *
-   * @param isEnabled boolean indicating if security is mandatory
-   */
-  public SecurityCriteria(boolean isEnabled) {
-    enabled = isEnabled;
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean match(ClientConnection connection) {
-    return (matchAfterBind(null, null, null, connection.isSecure()));
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean matchAfterBind(ClientConnection connection, DN bindDN,
-      AuthenticationType authType, boolean isSecure) {
-    if (enabled) {
-      return isSecure;
-    } else {
-      return true;
-    }
-  }
-}
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/CLIProfile.java b/opends/src/server/org/opends/server/tools/dsconfig/CLIProfile.java
index fa9d2d1..8fbb0b6 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/CLIProfile.java
+++ b/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();
     }
 
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
index b9a23c2..c2281d2 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/CreateSubCommandHandler.java
+++ b/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;
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java b/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
index 3052c92..19333af 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/DSConfig.java
+++ b/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();
   }
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
index cb0c959..6ab4000 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/DeleteSubCommandHandler.java
+++ b/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();
         }
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
index d073944..1c292fd 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/GetPropSubCommandHandler.java
+++ b/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
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
index 2dfe238..247da4f 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/ListSubCommandHandler.java
+++ b/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.
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
index e77a5f0..29a77c7 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/SetPropSubCommandHandler.java
+++ b/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
                         {
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java b/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
index 735e3ef..1737bf8 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandler.java
+++ b/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();
+  }
 }
diff --git a/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java b/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java
index 202bbf9..20d5df9 100644
--- a/opends/src/server/org/opends/server/tools/dsconfig/SubCommandHandlerFactory.java
+++ b/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(
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
index e837e7e..b7939d1 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/MockLDAPProfile.java
+++ b/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()
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteriaTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteriaTest.java
new file mode 100644
index 0000000..aa48391
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ANDConnectionCriteriaTest.java
@@ -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);
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteriaTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteriaTest.java
new file mode 100644
index 0000000..501439c
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/AuthMethodConnectionCriteriaTest.java
@@ -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);
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteriaTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteriaTest.java
new file mode 100644
index 0000000..b8b0afa
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/BindDNConnectionCriteriaTest.java
@@ -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);
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/IPConnectionCriteriaTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/IPConnectionCriteriaTest.java
new file mode 100644
index 0000000..0cd08c0
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/IPConnectionCriteriaTest.java
@@ -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);
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockClientConnection.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockClientConnection.java
new file mode 100644
index 0000000..63a9776
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockClientConnection.java
@@ -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.
+  }
+}
\ No newline at end of file
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockRequestFilteringQOSPolicyCfg.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockRequestFilteringQOSPolicyCfg.java
new file mode 100644
index 0000000..ea5f73b
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockRequestFilteringQOSPolicyCfg.java
@@ -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>());
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockResourceLimitsQOSPolicyCfg.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockResourceLimitsQOSPolicyCfg.java
new file mode 100644
index 0000000..3485165
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/MockResourceLimitsQOSPolicyCfg.java
@@ -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;
+  }
+
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java
index bb043dd..ae3775b 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/NetworkGroupTest.java
+++ b/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
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyTest.java
index 80ec06d..3a2b674 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/RequestFilteringPolicyTest.java
+++ b/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 {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyTest.java
similarity index 74%
rename from opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsTest.java
rename to opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyTest.java
index 86ff103..1a9e55c 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/ResourceLimitsPolicyTest.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 {
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteriaTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteriaTest.java
new file mode 100644
index 0000000..6525955
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/networkgroups/SecurityConnectionCriteriaTest.java
@@ -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);
+  }
+
+}

--
Gitblit v1.10.0