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/core/networkgroups/NetworkGroup.java | 2777 +++++++++++++++++++++++++++++++++++++---------------------
 1 files changed, 1,757 insertions(+), 1,020 deletions(-)

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);
-  }
 }

--
Gitblit v1.10.0