From 403b6a83e7d68de2b5159c3421b8d91d704566bb Mon Sep 17 00:00:00 2001
From: jdemendi <jdemendi@localhost>
Date: Tue, 30 Oct 2007 13:09:42 +0000
Subject: [PATCH] s set of files provides the workflow configuration manual mode.

---
 opends/resource/schema/02-config.ldif                                                                |   66 +
 opends/src/server/org/opends/server/core/NetworkGroup.java                                           |  115 +
 opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml                      |  107 +
 opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java    |  142 ++
 opends/src/server/org/opends/server/workflowelement/WorkflowElementConfigManager.java                |  448 ++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java |    4 
 opends/src/server/org/opends/server/core/WorkflowConfigManager.java                                  |  313 +++++
 opends/src/admin/defn/org/opends/server/admin/std/RootConfiguration.xml                              |   41 
 opends/src/admin/defn/org/opends/server/admin/std/LocalBackendWorkflowElementConfiguration.xml       |   71 +
 opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java                         |   27 
 opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml                            |   34 
 opends/src/admin/defn/org/opends/server/admin/std/WorkflowConfiguration.xml                          |  132 ++
 opends/src/messages/messages/config.properties                                                       |   20 
 opends/src/server/org/opends/server/core/DirectoryServer.java                                        |  390 +++++-
 opends/src/server/org/opends/server/core/WorkflowTopologyNode.java                                   |   17 
 opends/src/server/org/opends/server/core/NetworkGroupConfigManager.java                              |  305 +++++
 opends/src/admin/defn/org/opends/server/admin/std/WorkflowElementConfiguration.xml                   |  111 ++
 opends/src/server/org/opends/server/workflowelement/WorkflowElement.java                             |  142 ++
 opends/src/server/org/opends/server/core/RootDseWorkflowTopology.java                                |    6 
 opends/src/server/org/opends/server/core/CoreConfigManager.java                                      |   20 
 opends/src/server/org/opends/server/core/WorkflowImpl.java                                           |   61 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowConfigurationTest.java      |  689 ++++++++++++
 22 files changed, 3,091 insertions(+), 170 deletions(-)

diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index 2a0ebe5..d052a4a 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -2133,6 +2133,40 @@
   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.12
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.437
+  NAME 'ds-cfg-workflow'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.438
+  NAME 'ds-cfg-workflow-element-id'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.439
+  NAME 'ds-cfg-workflow-element'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.440
+  NAME 'ds-cfg-workflow-configuration-mode'
+  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.441
+  NAME 'ds-cfg-backend'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -2531,6 +2565,7 @@
         ds-cfg-disabled-privilege $
         ds-cfg-return-bind-error-messages $
         ds-cfg-idle-time-limit $
+        ds-cfg-workflow-configuration-mode $
         ds-cfg-save-config-on-successful-startup )
   X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.40
@@ -3599,3 +3634,34 @@
   ds-cfg-key-length-bits $ ds-cfg-symmetric-key )
   MAY ds-cfg-key-compromised-time
   X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.176
+  NAME 'ds-cfg-network-group'
+  SUP top
+  STRUCTURAL
+  MUST ( ds-cfg-network-group-id $
+         ds-cfg-enabled $
+         ds-cfg-workflow )
+  X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.177
+  NAME 'ds-cfg-workflow'
+  SUP top
+  STRUCTURAL
+  MUST ( ds-cfg-workflow-id $
+         ds-cfg-enabled $
+         ds-cfg-workflow-element $
+         ds-cfg-base-dn )
+  X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.178
+  NAME 'ds-cfg-workflow-element'
+  SUP top
+  STRUCTURAL
+  MUST ( ds-cfg-workflow-element-id $
+         ds-cfg-enabled $
+         ds-cfg-java-class )
+  X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.179
+  NAME 'ds-cfg-local-backend-workflow-element'
+  SUP ds-cfg-workflow-element
+  STRUCTURAL
+  MUST ( ds-cfg-backend )
+  X-ORIGIN 'OpenDS Directory Server' )
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 f149772..e6e2f59 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
@@ -725,5 +725,39 @@
     </adm:profile>
   </adm:property>
 
+  <adm:property name="workflow-configuration-mode">
+    <adm:synopsis>
+      Specifies the workflow configuration mode (auto vs. manual).
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>auto</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:enumeration>
+        <adm:value name="auto">
+          <adm:synopsis>
+             In the "auto" configuration mode there is no workflow
+             configuration. The workflows are created automatically
+             based on the backend configuration. There will be one
+             workflow per backend base DN.
+          </adm:synopsis>
+        </adm:value>
+        <adm:value name="manual">
+          <adm:synopsis>
+             In the "manual" configuration mode each workflow is created
+             according to its description in the configuration.
+          </adm:synopsis>
+        </adm:value>
+      </adm:enumeration>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-workflow-configuration-mode</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
 </adm:managed-object>
 
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/LocalBackendWorkflowElementConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/LocalBackendWorkflowElementConfiguration.xml
new file mode 100644
index 0000000..d32d2e5
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/LocalBackendWorkflowElementConfiguration.xml
@@ -0,0 +1,71 @@
+<?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
+ !
+ !
+ !      Portions Copyright 2007 Sun Microsystems, Inc.
+ ! -->
+
+<adm:managed-object
+  name="local-backend-workflow-element"
+  plural-name="local-backend-workflow-elements"
+  package="org.opends.server.admin.std"
+  extends="workflow-element"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+
+  <adm:synopsis>
+    The <adm:user-friendly-name /> provides access to a local backend.
+  </adm:synopsis>
+
+  <adm:tag name="user-management"/>
+
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-local-backend-workflow-element</ldap:name>
+      <ldap:superior>ds-cfg-workflow-element</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:property name="backend"
+    mandatory="true"
+    read-only="true"
+    multi-valued="false">
+    <adm:synopsis>
+      Identifies the backend accessed by the workflow element.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:aggregation relation-name="backend" parent-path="/">
+        <adm:target-is-enabled-condition>
+          <adm:contains property="enabled" value="true" />
+        </adm:target-is-enabled-condition>
+      </adm:aggregation>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-backend</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+</adm:managed-object>
+
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
new file mode 100644
index 0000000..0c30178
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml
@@ -0,0 +1,107 @@
+<?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
+ !
+ !
+ !      Portions Copyright 2007 Sun Microsystems, Inc.
+ ! -->
+
+<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.
+  </adm:synopsis>
+  <adm:tag name="user-management"/>
+  <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:property name="enabled"
+    mandatory="true"
+    multi-valued="false">
+    <adm:synopsis>
+      Indicates whether the <adm:user-friendly-name />
+      is enabled for use in the server.
+    </adm:synopsis>
+    <adm:description>
+      If a network group is not enabled, then its contents will not be
+      accessible when processing operations.
+    </adm:description>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-enabled</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="network-group-id" mandatory="true" read-only="true"
+    multi-valued="false">
+    <adm:synopsis>
+      Provides a name that will be used to identify the associated
+      <adm:user-friendly-name />.
+    </adm:synopsis>
+    <adm:description>
+      The name must be unique among all <adm:user-friendly-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="workflow" mandatory="true" read-only="true"
+    multi-valued="true">
+    <adm:synopsis>
+      Identifies a workflow in the network group.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:aggregation relation-name="workflow" parent-path="/">
+        <adm:target-is-enabled-condition>
+          <adm:contains property="enabled" value="true" />
+        </adm:target-is-enabled-condition>
+      </adm:aggregation>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-workflow</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 2e662c8..f00a95c 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
@@ -422,6 +422,47 @@
       </cli:relation>
     </adm:profile>
   </adm:relation>
+  
+  <adm:relation name="network-group">
+    <adm:one-to-many naming-property="network-group-id"/>
+    <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:relation>
+    </adm:profile>
+  </adm:relation>
+  <adm:relation name="workflow">
+    <adm:one-to-many naming-property="workflow-id"/>
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>
+        cn=Workflows,cn=config
+      </ldap:rdn-sequence>
+    </adm:profile>
+    <adm:profile name="cli">
+      <cli:relation>
+        <cli:default-property name="enabled" />
+      </cli:relation>
+    </adm:profile>
+  </adm:relation>
+  <adm:relation name="workflow-element">
+    <adm:one-to-many naming-property="workflow-element-id"/>
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>
+        cn=Workflow elements,cn=config
+      </ldap:rdn-sequence>
+    </adm:profile>
+    <adm:profile name="cli">
+      <cli:relation>
+        <cli:default-property name="enabled" />
+      </cli:relation>
+    </adm:profile>
+  </adm:relation>
+
   <adm:product-name>OpenDS Directory Server</adm:product-name>
   <adm:tag-definition name="logging">
     <adm:synopsis>Logging</adm:synopsis>
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/WorkflowConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/WorkflowConfiguration.xml
new file mode 100644
index 0000000..72711f1
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/WorkflowConfiguration.xml
@@ -0,0 +1,132 @@
+<?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
+ !
+ !
+ !      Portions Copyright 2007 Sun Microsystems, Inc.
+ ! -->
+
+<adm:managed-object
+  name="workflow"
+  plural-name="workflows"
+  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 list of tasks applied on a DIT.
+  </adm:synopsis>
+  <adm:tag name="user-management"/>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-workflow</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:property name="enabled"
+    mandatory="true"
+    read-only="false"
+    multi-valued="false">
+    <adm:synopsis>
+      Indicates whether the <adm:user-friendly-name />
+      is enabled for use in the server.
+    </adm:synopsis>
+    <adm:description>
+      If a workflow is not enabled, then its contents will not be
+      accessible when processing operations.
+    </adm:description>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-enabled</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="workflow-id"
+    mandatory="true"
+    read-only="true"
+    multi-valued="false">
+    <adm:synopsis>
+      Provides a name that will be used to identify the associated
+      <adm:user-friendly-name />.
+    </adm:synopsis>
+    <adm:description>
+      The name must be unique among all <adm:user-friendly-name />
+      in the server.
+    </adm:description>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-workflow-id</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="workflow-element"
+    mandatory="true"
+    read-only="false"
+    multi-valued="false">
+    <adm:synopsis>
+      The <adm:user-friendly-name /> identifies the root task of the worklfow.
+    </adm:synopsis>
+    <adm:description>
+      All the tasks in the worklfow are organized in a tree. The root element
+      of the tree is identified by the <adm:user-friendly-name />.
+    </adm:description>
+    <adm:syntax>
+      <adm:aggregation relation-name="workflow-element" parent-path="/">
+        <adm:target-is-enabled-condition>
+          <adm:contains property="enabled" value="true" />
+        </adm:target-is-enabled-condition>
+      </adm:aggregation>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-workflow-element</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="base-dn"
+    mandatory="true"
+    read-only="false"
+    multi-valued="false">
+    <adm:synopsis>
+      The <adm:user-friendly-name /> specifies the base DN of the data
+      targeted by the worlflow.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-base-dn</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property></adm:managed-object>
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/WorkflowElementConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/WorkflowElementConfiguration.xml
new file mode 100644
index 0000000..7eaaa2a
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/WorkflowElementConfiguration.xml
@@ -0,0 +1,111 @@
+<?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
+ !
+ !
+ !      Portions Copyright 2007 Sun Microsystems, Inc.
+ ! -->
+
+<adm:managed-object
+  name="workflow-element"
+  plural-name="workflow-elements"
+  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 a task part of a worklfow.
+  </adm:synopsis>
+
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-workflow-element</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+
+  <adm:property name="enabled"
+    mandatory="true"
+    read-only="false"
+    multi-valued="false">
+    <adm:synopsis>
+      Indicates whether the <adm:user-friendly-name />
+      is enabled for use in the server.
+    </adm:synopsis>
+    <adm:description>
+      If a workflow element is not enabled, then its contents will not be
+      accessible when processing operations.
+    </adm:description>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-enabled</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="workflow-element-id"
+    mandatory="true"
+    read-only="true"
+    multi-valued="false">
+    <adm:synopsis>
+      Provides a name that will be used to identify the associated
+      <adm:user-friendly-name />.
+    </adm:synopsis>
+    <adm:description>
+      The name must be unique among all <adm:user-friendly-name />
+      in the server.
+    </adm:description>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-workflow-element-id</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
+  <adm:property name="java-class" mandatory="true">
+    <adm:synopsis>
+      The fully-qualified name of the Java class that provides the
+      <adm:user-friendly-name />
+      implementation.
+    </adm:synopsis>
+    <adm:syntax>
+      <adm:java-class>
+        <adm:instance-of>
+          org.opends.server.workflowelement.WorkflowElement
+        </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/messages/messages/config.properties b/opends/src/messages/messages/config.properties
index 156c3f7..d25ed4f 100644
--- a/opends/src/messages/messages/config.properties
+++ b/opends/src/messages/messages/config.properties
@@ -2109,3 +2109,23 @@
  %s will not take effect until the component for which it is set is restarted
 SEVERE_ERR_CONFIG_LOGGING_CANNOT_OPEN_FILE_709=An error occurred while \
  attempting to open the configured log file %s for logger %s:  %s
+SEVERE_ERR_CONFIG_WORKFLOW_ELEMENT_CONFIG_NOT_ACCEPTABLE_710=The configuration \
+ for the workflow element defined in configuration entry %s was not \
+ acceptable: %s
+SEVERE_ERR_CONFIG_WORKFLOW_ELEMENT_CANNOT_INITIALIZE_711=An error occurred \
+ while trying to initialize a workflow element from class %s with the \
+ information in configuration entry %s:  %s.  This workflow element will be \
+ disabled
+MILD_ERR_CONFIG_WORKFLOW_ELEMENT_ALREADY_REGISTERED_712=The workflow \
+ element %s is already registered with the Directory Server. This workflow \
+ element will be ignored
+SEVERE_ERR_CONFIG_WORKFLOW_CANNOT_CONFIGURE_MANUAL_713=An error occurred \
+ while trying to configure in manual mode the workflows in the \
+ Directory Server, and rollback to automatic configuration mode has failed \
+ too. If the server is in an unstable state restart it with the last \
+ valid configuration
+SEVERE_ERR_CONFIG_WORKFLOW_CANNOT_CONFIGURE_AUTO_714=An error occurred \
+ while trying to configure in automatic mode the workflows in the \
+ Directory Server, and rollback to manual configuration mode has failed \
+ too. If the server is in an unstable state restart it with the last \
+ valid configuration
diff --git a/opends/src/server/org/opends/server/core/CoreConfigManager.java b/opends/src/server/org/opends/server/core/CoreConfigManager.java
index c170ac7..f9fa411 100644
--- a/opends/src/server/org/opends/server/core/CoreConfigManager.java
+++ b/opends/src/server/org/opends/server/core/CoreConfigManager.java
@@ -37,6 +37,7 @@
 
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.meta.GlobalCfgDefn;
+import org.opends.server.admin.std.meta.GlobalCfgDefn.WorkflowConfigurationMode;
 import org.opends.server.admin.std.server.GlobalCfg;
 import org.opends.server.admin.std.server.RootCfg;
 import org.opends.server.admin.server.ServerManagementContext;
@@ -338,8 +339,25 @@
 
     DirectoryServer.setSaveConfigOnSuccessfulStartup(
          globalConfig.isSaveConfigOnSuccessfulStartup());
-  }
 
+    // If the workflow configuration mode has changed then reconfigure
+    // the workflows-only if the server is running. If the server is not
+    // running (ie. the server is starting up) simply update the workflow
+    // configuration mode as the workflow configuration is processed
+    // elsewhere.
+    WorkflowConfigurationMode oldMode =
+      DirectoryServer.getWorkflowConfigurationMode();
+    WorkflowConfigurationMode newMode =
+      globalConfig.getWorkflowConfigurationMode();
+    if (DirectoryServer.isRunning())
+    {
+      DirectoryServer.reconfigureWorkflows(oldMode, newMode);
+    }
+    else
+    {
+      DirectoryServer.setWorkflowConfigurationMode(newMode);
+    }
+  }
 
 
   /**
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 4807b44..3305fc7 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -30,6 +30,7 @@
 
 import org.opends.server.admin.ClassLoaderProvider;
 import org.opends.server.admin.server.ServerManagementContext;
+import org.opends.server.admin.std.meta.GlobalCfgDefn.WorkflowConfigurationMode;
 import org.opends.server.admin.std.server.*;
 import org.opends.server.api.AccountStatusNotificationHandler;
 import org.opends.server.api.AlertGenerator;
@@ -191,6 +192,7 @@
 import org.opends.server.protocols.internal.InternalConnectionHandler;
 import org.opends.server.protocols.internal.InternalClientConnection;
 import org.opends.server.crypto.CryptoManagerSync;
+import static org.opends.messages.ConfigMessages.*;
 
 import javax.management.MBeanServer;
 import javax.management.MBeanServerFactory;
@@ -707,6 +709,24 @@
   // The writability mode for the Directory Server.
   private WritabilityMode writabilityMode;
 
+  // The workflow configuration mode (auto or manual).
+  private WorkflowConfigurationMode workflowConfigurationMode;
+
+  // The network group config manager for the Directory Server.
+  // This config manager is used when the workflow configuration
+  // mode is 'manual'.
+  private NetworkGroupConfigManager networkGroupConfigManager;
+
+  // The workflow config manager for the Directory Server.
+  // This config manager is used when the workflow configuration
+  // mode is 'manual'.
+  private WorkflowConfigManager workflowConfigManager;
+
+  // The workflow element config manager for the Directory Server.
+  // This config manager is used when the workflow configuration
+  // mode is 'manual'.
+  private WorkflowElementConfigManager workflowElementConfigManager;
+
 
 
   /**
@@ -1346,14 +1366,22 @@
       // Initialize the access control handler.
       AccessControlConfigManager.getInstance().initializeAccessControl();
 
-      // Initialize all the backends and their associated suffixes.
+      // Initialize all the backends and their associated suffixes
+      // and initialize the workflows when workflow configuration mode
+      // is auto.
       initializeBackends();
 
-      // A first set of workflows had been created in the registerBackend
-      // method. We now need to complete the workflow creation for the
-      // backends that were not registered through the registerBackend
-      // method (ie. cn=config and RootDSE).
-      createAndRegisterRemainingWorkflows();
+      // When workflow configuration mode is manual, do configure the
+      // workflows now, else just configure the remaining workflows
+      // (rootDSE and config backend).
+      if (workflowConfigurationModeIsAuto())
+      {
+        createAndRegisterRemainingWorkflows();
+      }
+      else
+      {
+        configureWorkflowsManual();
+      }
 
       // Check for and initialize user configured entry cache if any,
       // if not stick with default entry cache initialized earlier.
@@ -2582,51 +2610,32 @@
 
 
   /**
-   * Deregisters a set of workflows each of which is identified with
-   * a baseDN.
-   *
-   * In the first implementation, workflows are stored in the default network
-   * group only.
-   *
-   * @param baseDNs  the DNs of the workflows to deregister
-   */
-  private static void deregisterWorkflows(
-      DN[] baseDNs
-      )
-  {
-    for (DN baseDN: baseDNs)
-    {
-      deregisterWorkflow(baseDN);
-    }
-  }
-
-
-  /**
-   * Deregisters one workflow with the appropriate network group.
-   *
-   * In the first implementation, workflows are stored in the default network
-   * group only.
+   * Deregisters a workflow with the default network group and
+   * deregisters the workflow with the server. This method is
+   * intended to be called when workflow configuration mode is
+   * auto.
    *
    * @param baseDN  the DN of the workflow to deregister
    */
-  private static void deregisterWorkflow(
+  private static void deregisterWorkflowWithDefaultNetworkGroup(
       DN baseDN
       )
   {
     // Get the default network group and deregister all the workflows
-    // being configured for the backend (reminder: there is one worklfow
-    // per base DN configured in the backend).
+    // being configured for the backend (there is one worklfow per
+    // backend base DN).
     NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
-    defaultNetworkGroup.deregisterWorkflow (baseDN);
+    Workflow workflow = defaultNetworkGroup.deregisterWorkflow(baseDN);
+    WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
+    workflowImpl.deregister();
   }
 
 
   /**
-   * Creates a set of workflows for a given backend. There are as many
-   * workflows as base DNs defined in the backend. Each workflow is
-   * registered with the appropriate network group.
-   *
-   * TODO implement the registration with the appropriate network group.
+   * Creates a set of workflows for a given backend and registers the
+   * workflows with the default network group. There are as many workflows
+   * as base DNs defined in the backend. This method is intended
+   * to be called when workflow configuration mode is auto.
    *
    * @param backend  the backend handled by the workflow
    *
@@ -2634,35 +2643,33 @@
    *                              workflow conflicts with the workflow
    *                              ID of an existing workflow.
    */
-  public static void createAndRegisterWorkflows(
+  public static void createAndRegisterWorkflowsWithDefaultNetworkGroup(
       Backend backend
       ) throws DirectoryException
   {
-    // Create a worklfow for each baseDN being configured
-    // in the backend and register the workflow with the network groups.
-    // In the automatic configuration mode, the workflow identifier is
-    // set to the backend ID.
+    // Create a worklfow for each backend base DN and register the workflow
+    // with the default network group.
     for (DN curBaseDN: backend.getBaseDNs())
     {
-      createAndRegisterWorkflow(curBaseDN, backend);
+      WorkflowImpl workflowImpl = createWorkflow(curBaseDN, backend);
+      registerWorkflowWithDefaultNetworkGroup(workflowImpl);
     }
   }
 
 
   /**
-   * Creates one workflow for a given base DN in a backend. The workflow
-   * is registered with the appropriate network group.
-   *
-   * TODO implement the registration with the appropriate network group.
+   * Creates one workflow for a given base DN in a backend.
    *
    * @param baseDN   the base DN of the workflow to create
    * @param backend  the backend handled by the workflow
    *
+   * @return the newly created workflow
+   *
    * @throws  DirectoryException  If the workflow ID for the provided
    *                              workflow conflicts with the workflow
    *                              ID of an existing workflow.
    */
-  public static void createAndRegisterWorkflow(
+  public static WorkflowImpl createWorkflow(
       DN      baseDN,
       Backend backend
       ) throws DirectoryException
@@ -2671,49 +2678,52 @@
 
     // Create a root workflow element to encapsulate the backend
     LocalBackendWorkflowElement rootWE =
-        LocalBackendWorkflowElement.create(backendID, backend);
+        LocalBackendWorkflowElement.createAndRegister(backendID, backend);
+
+    // The workflow ID is "backendID + baseDN".
+    // We cannot use backendID as workflow identifier because a backend
+    // may handle several base DNs. We cannot use baseDN either because
+    // we might want to configure several workflows handling the same
+    // baseDN through different network groups. So a mix of both
+    // backendID and baseDN should be ok.
+    String workflowID = backend.getBackendID() + "#" + baseDN.toString();
 
     // Create the worklfow for the base DN and register the workflow with
-    // the appropriate network groups.
+    // the server.
     WorkflowImpl workflowImpl = new WorkflowImpl(
-        baseDN.toString(), baseDN, (WorkflowElement) rootWE);
-    registerWorkflowInNetworkGroups(workflowImpl);
+        workflowID, baseDN, (WorkflowElement) rootWE);
+    workflowImpl.register();
+
+    return workflowImpl;
   }
 
 
   /**
-   * Registers a workflow with the appropriate network groups.
+   * Registers a workflow with the default network group. This method
+   * is intended to be called when workflow configuration mode is auto.
    *
-   * In the first implementation, the workflow is registered with the
-   * default network group only.
+   * @param workflowImpl  The workflow to register with the
+   *                      default network group
    *
-   * TODO implement the registration with the appropriate network group.
-   *
-   * @param workflowImpl  the workflow to register
-   *
-   * @throws  DirectoryException  If the workflow ID for the provided
-   *                              workflow conflicts with the workflow
-   *                              ID of an existing workflow in a
-   *                              network group.
+   * @throws  DirectoryException  If the workflow is already registered with
+   *                              the default network group
    */
-  private static void registerWorkflowInNetworkGroups(
+  private static void registerWorkflowWithDefaultNetworkGroup(
       WorkflowImpl workflowImpl
       ) throws DirectoryException
   {
     NetworkGroup defaultNetworkGroup = NetworkGroup.getDefaultNetworkGroup();
     defaultNetworkGroup.registerWorkflow(workflowImpl);
-
-    // Now for each network group that exposes the baseDN of the workflow
-    // create an instance of the workflow and register it with the network
-    // group.
-    // TODO jdemendi - we need the network group configuration to configure
-    // the workflows per network group.
   }
 
 
   /**
-   * Creates the workflows for the backends whose baseDNs were not registered
-   * with registerBaseDN method, namely config backend and RootDSE backend.
+   * Creates the missing workflows, one for the config backend and one for
+   * the rootDSE backend.
+   *
+   * This method should be invoked whatever may be the workflow
+   * configuration mode because config backend and rootDSE backend
+   * will not have any configuration section, ever.
    *
    * @throws  ConfigException  If there is a configuration problem with any of
    *                           the workflows.
@@ -2723,8 +2733,8 @@
   {
     try
     {
-      createAndRegisterWorkflows (configHandler);
-      createAndRegisterWorkflows (rootDSEBackend);
+      createAndRegisterWorkflowsWithDefaultNetworkGroup (configHandler);
+      createAndRegisterWorkflowsWithDefaultNetworkGroup (rootDSEBackend);
     }
     catch (DirectoryException de)
     {
@@ -2734,6 +2744,151 @@
 
 
   /**
+   * Reconfigures the workflows when configuration mode has changed.
+   * This method is invoked when workflows need to be reconfigured
+   * while the server is running. If the reconfiguration is valid
+   * then the method update the workflow configuration mode.
+   *
+   * @param oldMode  the current workflow configuration mode
+   * @param newMode  the new workflow configuration mode
+   */
+  public static void reconfigureWorkflows(
+      WorkflowConfigurationMode oldMode,
+      WorkflowConfigurationMode newMode)
+  {
+    if ((oldMode == WorkflowConfigurationMode.AUTO)
+        && (newMode == WorkflowConfigurationMode.MANUAL))
+    {
+      // move to manual mode
+      try
+      {
+        directoryServer.configureWorkflowsManual();
+        setWorkflowConfigurationMode(newMode);
+      }
+      catch (Exception e)
+      {
+        // rollback to auto mode
+        try
+        {
+           directoryServer.configureWorkflowsAuto();
+        }
+        catch (Exception ee)
+        {
+          // rollback to auto mode is failing too!!
+          // well, just log an error message and suggest the admin
+          // to restart the server with the last valid config...
+          Message message = ERR_CONFIG_WORKFLOW_CANNOT_CONFIGURE_MANUAL.get();
+          logError(message);
+        }
+      }
+    }
+    else if ((oldMode == WorkflowConfigurationMode.MANUAL)
+        && (newMode == WorkflowConfigurationMode.AUTO))
+    {
+      // move to auto mode
+      try
+      {
+        directoryServer.configureWorkflowsAuto();
+        setWorkflowConfigurationMode(newMode);
+      }
+      catch (Exception e)
+      {
+        // rollback to manual mode
+        try
+        {
+           directoryServer.configureWorkflowsManual();
+        }
+        catch (Exception ee)
+        {
+          // rollback to auto mode is failing too!!
+          // well, just log an error message and suggest the admin
+          // to restart the server with the last valid config...
+          Message message = ERR_CONFIG_WORKFLOW_CANNOT_CONFIGURE_AUTO.get();
+          logError(message);
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Configures the workflows when configuration mode is manual.
+   *
+   * @throws  ConfigException  If there is a problem with the Directory Server
+   *                           configuration that prevents a critical component
+   *                           from being instantiated.
+   *
+   * @throws  InitializationException  If some other problem occurs while
+   *                                   attempting to initialize and start the
+   *                                   Directory Server.
+   */
+  private void configureWorkflowsManual()
+      throws ConfigException, InitializationException
+  {
+    // First of all re-initialize the current workflow configuration
+    NetworkGroup.resetConfig();
+    WorkflowImpl.resetConfig();
+    WorkflowElement.resetConfig();
+
+    // Then configure the workflows
+    workflowElementConfigManager = new WorkflowElementConfigManager();
+    workflowElementConfigManager.initializeWorkflowElements();
+
+    workflowConfigManager = new WorkflowConfigManager();
+    workflowConfigManager.initializeWorkflows();
+
+    networkGroupConfigManager = new NetworkGroupConfigManager();
+    networkGroupConfigManager.initializeNetworkGroups();
+
+    // We now need to complete the workflow creation for the
+    // config backend and rootDSE backend.
+    createAndRegisterRemainingWorkflows();
+  }
+
+
+  /**
+   * Configures the workflows when configuration mode is auto.
+   *
+   * @throws  ConfigException  If there is a problem with the Directory Server
+   *                           configuration that prevents a critical component
+   *                           from being instantiated.
+   */
+  private void configureWorkflowsAuto() throws ConfigException
+  {
+    // First of all re-initialize the current workflow configuration
+    NetworkGroup.resetConfig();
+    WorkflowImpl.resetConfig();
+    WorkflowElement.resetConfig();
+
+    // For each base DN in a backend create a workflow and register
+    // the workflow with the default network group
+    Map<String, Backend> backends = getBackends();
+    for (String backendID: backends.keySet())
+    {
+      Backend backend = backends.get(backendID);
+      for (DN baseDN: backend.getBaseDNs())
+      {
+        WorkflowImpl workflowImpl;
+        try
+        {
+          workflowImpl = createWorkflow(baseDN, backend);
+          registerWorkflowWithDefaultNetworkGroup(workflowImpl);
+        }
+        catch (DirectoryException e)
+        {
+          // TODO Auto-generated catch block
+          throw new ConfigException(e.getMessageObject());
+        }
+      }
+    }
+
+    // We now need to complete the workflow creation for the
+    // config backend and rootDSE backend.
+    createAndRegisterRemainingWorkflows();
+  }
+
+
+  /**
    * Initializes the Directory Server group manager.
    *
    * @throws  ConfigException  If there is a configuration problem with any of
@@ -6246,8 +6401,14 @@
 
       directoryServer.backends = newBackends;
 
-      // Don't need anymore the local backend workflow element
-      LocalBackendWorkflowElement.remove(backend.getBackendID());
+      // Don't need anymore the local backend workflow element so we
+      // can remove it. We do remove the workflow element only when
+      // the workflow configuration mode is auto because in manual
+      // mode the config manager is doing the job.
+      if (workflowConfigurationModeIsAuto())
+      {
+        LocalBackendWorkflowElement.remove(backend.getBackendID());
+      }
 
 
       BackendMonitor monitor = backend.getBackendMonitor();
@@ -6409,13 +6570,21 @@
         }
       }
 
-      // Now create a workflow for the registered baseDN and register
-      // the workflow with the network groups, but don't register the
-      // workflow if the backend happens to be the configuration backend
-      // because it's too soon.
-      if (! baseDN.equals(DN.decode("cn=config")))
+      // When a new baseDN is registered with the server we have to create
+      // a new workflow to handle the base DN. We do not need to create
+      // the workflow in manual mode because in that case the workflows
+      // are created explicitely.
+      if (workflowConfigurationModeIsAuto())
       {
-        createAndRegisterWorkflow(baseDN, backend);
+        // Now create a workflow for the registered baseDN and register
+        // the workflow with the default network group, but don't register
+        // the workflow if the backend happens to be the configuration
+        // backend because it's too soon for the config backend.
+        if (! baseDN.equals(DN.decode("cn=config")))
+        {
+          WorkflowImpl workflowImpl = createWorkflow(baseDN, backend);
+          registerWorkflowWithDefaultNetworkGroup(workflowImpl);
+        }
       }
     }
   }
@@ -6449,8 +6618,14 @@
         }
       }
 
-      // Now deregister the workflow that was associated with the base DN.
-      deregisterWorkflow(baseDN);
+      // Now we need to deregister the workflow that was associated with
+      // the base DN but we can do it only when the workflow configuration
+      // mode is auto, because in manual mode the deregistration is done
+      // by the workflow config manager.
+      if (workflowConfigurationModeIsAuto())
+      {
+        deregisterWorkflowWithDefaultNetworkGroup(baseDN);
+      }
     }
   }
 
@@ -9610,5 +9785,48 @@
     }
     return isRunningAsWindowsService;
   }
+
+
+  /**
+   * Specifies whether the workflows are configured automatically or manually.
+   * In auto configuration mode one workflow is created for each and every
+   * base DN in the local backends. In the auto configuration mode the
+   * workflows are created according to their description in the configuration
+   * file.
+   *
+   * @param  workflowConfigurationMode  Indicates whether the workflows are
+   *                                    configured automatically or manually
+   */
+  public static void setWorkflowConfigurationMode(
+      WorkflowConfigurationMode workflowConfigurationMode)
+  {
+    directoryServer.workflowConfigurationMode = workflowConfigurationMode;
+  }
+
+
+  /**
+   * Indicates whether the workflow configuration mode is 'auto' or not.
+   *
+   * @return the workflow configuration mode
+   */
+  public static boolean workflowConfigurationModeIsAuto()
+  {
+    boolean isAuto =
+      (directoryServer.workflowConfigurationMode
+       == WorkflowConfigurationMode.AUTO);
+    return isAuto;
+  }
+
+
+
+  /**
+   * Retrieves the workflow configuration mode.
+   *
+   * @return the workflow configuration mode
+   */
+  public static WorkflowConfigurationMode getWorkflowConfigurationMode()
+  {
+    return directoryServer.workflowConfigurationMode;
+  }
 }
 
diff --git a/opends/src/server/org/opends/server/core/NetworkGroup.java b/opends/src/server/org/opends/server/core/NetworkGroup.java
index aef2519..64eb290 100644
--- a/opends/src/server/org/opends/server/core/NetworkGroup.java
+++ b/opends/src/server/org/opends/server/core/NetworkGroup.java
@@ -56,7 +56,7 @@
 
 
   // A lock to protect concurrent access to the registered Workflow nodes.
-  private static Object registeredWorkflowNodesLock = new Object();
+  private Object registeredWorkflowNodesLock = new Object();
 
 
   // The workflow node for the rootDSE entry. The RootDSE workflow node
@@ -107,6 +107,17 @@
 
 
   /**
+   * Performs any finalization that might be required when this
+   * network group is unloaded.  No action is taken in the
+   * default implementation.
+   */
+  public void finalizeNetworkGroup()
+  {
+    // No action is required by default.
+  }
+
+
+  /**
    * Registers the current network group (this) with the server.
    *
    * @throws  DirectoryException  If the network group ID for the provided
@@ -188,9 +199,6 @@
       WorkflowElement[] postWorkflowElements
       ) throws DirectoryException
   {
-    // true as soon as the workflow has been registered
-    boolean registered = false;
-
     // Is it the rootDSE workflow?
     DN baseDN = workflow.getBaseDN();
     if (baseDN.isNullDN())
@@ -198,7 +206,6 @@
       // NOTE - The rootDSE workflow is stored with the registeredWorkflows.
       rootDSEWorkflowNode =
         new RootDseWorkflowTopology(workflow, namingContexts);
-      registered = true;
     }
     else
     {
@@ -210,7 +217,6 @@
       // Register the workflow node with the network group. If the workflow
       // ID is already existing then an exception is raised.
       registerWorkflowNode(workflowNode);
-      registered = true;
 
       // Now add the workflow in the workflow topology...
       for (WorkflowTopologyNode curNode: registeredWorkflowNodes.values())
@@ -234,17 +240,6 @@
       // Rebuild the list of naming context handled by the network group
       rebuildNamingContextList();
     }
-
-    // If the workflow has been registered successfully then register it
-    // with the default network group
-    if (registered)
-    {
-      if (this != defaultNetworkGroup)
-      {
-        defaultNetworkGroup.registerWorkflow(
-            workflow, preWorkflowElements, postWorkflowElements);
-      }
-    }
   }
 
 
@@ -253,20 +248,25 @@
    * deregister is identified by its baseDN.
    *
    * @param baseDN  the baseDN of the workflow to deregister, may be null
+   *
+   * @return the deregistered workflow
    */
-  public void deregisterWorkflow(
+  public Workflow deregisterWorkflow(
       DN baseDN
       )
   {
+    Workflow workflow = null;
+
     if (baseDN == null)
     {
-      return;
+      return workflow;
     }
 
     if (baseDN.isNullDN())
     {
       // deregister the rootDSE
       deregisterWorkflow(rootDSEWorkflowNode);
+      workflow = rootDSEWorkflowNode.getWorkflowImpl();
     }
     else
     {
@@ -281,6 +281,7 @@
             // 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.
@@ -289,6 +290,8 @@
         }
       }
     }
+
+    return workflow;
   }
 
 
@@ -505,38 +508,6 @@
 
 
   /**
-   * Checks whether a base DN has been already registered with
-   * the network group.
-   *
-   * @param baseDN  the base DN to check
-   * @return <code>false</code> if the base DN is registered with the
-   *         network group, <code>false</code> otherwise
-   */
-  private boolean baseDNAlreadyRegistered(
-      DN baseDN
-      )
-  {
-    // returned result
-    boolean alreadyRegistered = false;
-
-    // go through the list of registered workflow and check whether a base DN
-    // has already been used in a registered workflow
-    for (WorkflowTopologyNode workflowNode: registeredWorkflowNodes.values())
-    {
-      DN curDN = workflowNode.getBaseDN();
-      if (baseDN.equals (curDN))
-      {
-        alreadyRegistered = true;
-        break;
-      }
-    }
-
-    // check done
-    return alreadyRegistered;
-  }
-
-
-  /**
    * Returns the list of naming contexts handled by the network group.
    *
    * @return the list of naming contexts
@@ -614,5 +585,47 @@
     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();
+  }
+
+
+  /**
+   * Resets the configuration of all the registered network groups.
+   */
+  public static void resetConfig()
+  {
+    // Reset the default network group
+    defaultNetworkGroup.reset();
+
+    // Reset all the registered network group
+    synchronized (registeredNetworkGroupsLock)
+    {
+      registeredNetworkGroups = new TreeMap<String, NetworkGroup>();
+    }
+  }
+
+
+  /**
+   * Resets the configuration of the current network group.
+   */
+  public void reset()
+  {
+    synchronized (registeredWorkflowNodesLock)
+    {
+      registeredWorkflowNodes = new TreeMap<String, WorkflowTopologyNode>();
+      rootDSEWorkflowNode = null;
+      namingContexts = new NetworkGroupNamingContexts();
+    }
   }
 }
diff --git a/opends/src/server/org/opends/server/core/NetworkGroupConfigManager.java b/opends/src/server/org/opends/server/core/NetworkGroupConfigManager.java
new file mode 100644
index 0000000..c44db66
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/NetworkGroupConfigManager.java
@@ -0,0 +1,305 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+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.server.ServerManagementContext;
+import org.opends.server.admin.std.server.NetworkGroupCfg;
+import org.opends.server.admin.std.server.RootCfg;
+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.ResultCode;
+
+
+/**
+ * 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>
+
+{
+  // A mapping between the DNs of the config entries and the associated
+  // network groups.
+  private ConcurrentHashMap<DN, NetworkGroup> networkGroups;
+
+
+
+  /**
+   * Creates a new instance of this network group config manager.
+   */
+  public NetworkGroupConfigManager()
+  {
+    networkGroups = new ConcurrentHashMap<DN, NetworkGroup>();
+  }
+
+
+
+  /**
+   * 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;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    configuration.addChangeListener(this);
+
+    // If the new network group is enabled then create it and register it.
+    if (configuration.isEnabled())
+    {
+      try
+      {
+        createAndRegisterNetworkGroup(configuration);
+      }
+      catch (DirectoryException de)
+      {
+        if (resultCode == ResultCode.SUCCESS)
+        {
+          resultCode = DirectoryServer.getServerErrorResultCode();
+        }
+
+        messages.add(de.getMessageObject());
+      }
+
+    }
+
+    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;
+    ArrayList<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)
+  {
+    // Nothing to check.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      NetworkGroupCfg configuration)
+  {
+    ResultCode         resultCode          = ResultCode.SUCCESS;
+    boolean            adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    ConfigChangeResult configChangeResult =
+      new ConfigChangeResult(resultCode, adminActionRequired, messages);
+
+
+    // 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 (existingNetworkGroup != null)
+      {
+        networkGroups.remove(configuration.dn());
+        existingNetworkGroup.deregister();
+        existingNetworkGroup.finalizeNetworkGroup();
+      }
+
+      return configChangeResult;
+    }
+
+    // If the network group is disabled then create it and register it.
+    if (existingNetworkGroup == null)
+    {
+      try
+      {
+        createAndRegisterNetworkGroup(configuration);
+      }
+      catch (DirectoryException de)
+      {
+        if (resultCode == ResultCode.SUCCESS)
+        {
+          resultCode = DirectoryServer.getServerErrorResultCode();
+        }
+
+        messages.add(de.getMessageObject());
+      }
+    }
+
+    return configChangeResult;
+  }
+
+
+  /**
+   * 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.
+   */
+  private void createAndRegisterNetworkGroup(
+      NetworkGroupCfg networkGroupCfg
+      ) throws DirectoryException
+  {
+    // create the network group
+    String networkGroupId = networkGroupCfg.getNetworkGroupId();
+    NetworkGroup networkGroup = new NetworkGroup(networkGroupId);
+
+    // register the workflows with the network group
+    for (String workflowID: networkGroupCfg.getWorkflow())
+    {
+      WorkflowImpl workflowImpl =
+        (WorkflowImpl) WorkflowImpl.getWorkflow(workflowID);
+      networkGroup.registerWorkflow(workflowImpl);
+    }
+
+    // finally register the network group with the server
+    networkGroups.put(networkGroupCfg.dn(), networkGroup);
+    networkGroup.register();
+  }
+
+}
+
diff --git a/opends/src/server/org/opends/server/core/RootDseWorkflowTopology.java b/opends/src/server/org/opends/server/core/RootDseWorkflowTopology.java
index eb0ff47..7ebb1a8 100644
--- a/opends/src/server/org/opends/server/core/RootDseWorkflowTopology.java
+++ b/opends/src/server/org/opends/server/core/RootDseWorkflowTopology.java
@@ -166,8 +166,10 @@
   {
     StringBuilder sb = new StringBuilder();
 
-    // display the baseDN
-    sb.append(leftMargin + "Workflow baseDN:[ \"\" ]\n");
+    // display the identifier and baseDN
+    String workflowID = this.getWorkflowImpl().getWorkflowId();
+    sb.append(leftMargin + "Workflow ID = " + workflowID + "\n");
+    sb.append(leftMargin + "         baseDN:[ \"\" ]\n");
 
     return sb;
   }
diff --git a/opends/src/server/org/opends/server/core/WorkflowConfigManager.java b/opends/src/server/org/opends/server/core/WorkflowConfigManager.java
new file mode 100644
index 0000000..cca4243
--- /dev/null
+++ b/opends/src/server/org/opends/server/core/WorkflowConfigManager.java
@@ -0,0 +1,313 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+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.server.ServerManagementContext;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.admin.std.server.WorkflowCfg;
+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.ResultCode;
+import org.opends.server.workflowelement.WorkflowElement;
+
+
+/**
+ * This class defines a utility that will be used to manage the configuration
+ * for the set of workflows defined in the Directory Server.  It will perform
+ * the necessary initialization of those workflows when the server is first
+ * started, and then will manage any changes to them while the server is
+ * running.
+ */
+public class WorkflowConfigManager
+       implements ConfigurationChangeListener<WorkflowCfg>,
+                  ConfigurationAddListener<WorkflowCfg>,
+                  ConfigurationDeleteListener<WorkflowCfg>
+
+{
+  // A mapping between the DNs of the config entries and the associated
+  // workflows.
+  private ConcurrentHashMap<DN, WorkflowImpl> workflows;
+
+
+
+  /**
+   * Creates a new instance of this workflow config manager.
+   */
+  public WorkflowConfigManager()
+  {
+    workflows = new ConcurrentHashMap<DN, WorkflowImpl>();
+  }
+
+
+
+  /**
+   * Initializes all workflows currently defined in the Directory
+   * Server configuration.  This should only be called at Directory Server
+   * startup.
+   *
+   * @throws  ConfigException  If a configuration problem causes the workflow
+   *                           initialization process to fail.
+   */
+  public void initializeWorkflows()
+      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 workflow entries are added or removed.
+    rootConfiguration.addWorkflowAddListener(this);
+    rootConfiguration.addWorkflowDeleteListener(this);
+
+
+    //Initialize the existing workflows.
+    for (String workflowName : rootConfiguration.listWorkflows())
+    {
+      WorkflowCfg workflowConfiguration =
+        rootConfiguration.getWorkflow(workflowName);
+      workflowConfiguration.addChangeListener(this);
+
+      if (workflowConfiguration.isEnabled())
+      {
+        try
+        {
+          createAndRegisterWorkflow(workflowConfiguration);
+        }
+        catch (DirectoryException de)
+        {
+          throw new ConfigException(de.getMessageObject());
+        }
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationAddAcceptable(
+      WorkflowCfg   configuration,
+      List<Message> unacceptableReasons)
+  {
+    // Nothing to check.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationAdd(
+      WorkflowCfg configuration)
+  {
+    ResultCode         resultCode          = ResultCode.SUCCESS;
+    boolean            adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    configuration.addChangeListener(this);
+
+    // If the new network group is enabled then create it and register it.
+    if (configuration.isEnabled())
+    {
+      try
+      {
+        createAndRegisterWorkflow(configuration);
+      }
+      catch (DirectoryException de)
+      {
+        if (resultCode == ResultCode.SUCCESS)
+        {
+          resultCode = DirectoryServer.getServerErrorResultCode();
+        }
+
+        messages.add(de.getMessageObject());
+      }
+    }
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationDeleteAcceptable(
+      WorkflowCfg   configuration,
+      List<Message> unacceptableReasons)
+  {
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(
+      WorkflowCfg configuration)
+  {
+    ResultCode         resultCode          = ResultCode.SUCCESS;
+    boolean            adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+
+    WorkflowImpl workflow = workflows.remove(configuration.dn());
+    if (workflow != null)
+    {
+      workflow.deregister();
+      workflow.finalizeWorkflow();
+    }
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+      WorkflowCfg   configuration,
+      List<Message> unacceptableReasons)
+  {
+    // Nothing to check.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      WorkflowCfg configuration)
+  {
+    ResultCode         resultCode          = ResultCode.SUCCESS;
+    boolean            adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+
+    ConfigChangeResult configChangeResult =
+      new ConfigChangeResult(resultCode, adminActionRequired, messages);
+
+
+    // Get the existing network group if it's already enabled.
+    WorkflowImpl existingWorkflow = workflows.get(configuration.dn());
+
+    // If the new configuration has the validator disabled, then disable it if
+    // it is enabled, or do nothing if it's already disabled.
+    if (! configuration.isEnabled())
+    {
+      if (existingWorkflow != null)
+      {
+        workflows.remove(configuration.dn());
+        existingWorkflow.deregister();
+        existingWorkflow.finalizeWorkflow();
+      }
+
+      return configChangeResult;
+    }
+
+    // If the network group is disabled then create and register it.
+    if (existingWorkflow == null)
+    {
+      try
+      {
+        createAndRegisterWorkflow(configuration);
+      }
+      catch (DirectoryException de)
+      {
+        if (resultCode == ResultCode.SUCCESS)
+        {
+          resultCode = DirectoryServer.getServerErrorResultCode();
+        }
+
+        messages.add(de.getMessageObject());
+      }
+    }
+
+    return configChangeResult;
+  }
+
+
+  /**
+   * Creates a workflow, registers the workflow with the server
+   * and registers the workflow with the default network group.
+   *
+   * @param workflowCfg  the workflow configuration
+   *
+   * @throws DirectoryException If a problem occurs while trying to
+   *                            decode a provided string as a DN or if
+   *                            the workflow ID for a provided workflow
+   *                            conflicts with the workflow ID of an existing
+   *                            workflow during workflow registration.
+   */
+  private void createAndRegisterWorkflow(
+      WorkflowCfg workflowCfg
+      ) throws DirectoryException
+  {
+    // The ID of the workflow to create
+    String workflowId = workflowCfg.getWorkflowId();
+
+    // Create the root workflow element to associate with the workflow
+    String rootWorkflowElementID = workflowCfg.getWorkflowElement();
+    WorkflowElement rootWorkflowElement =
+      WorkflowElement.getWorkflowElement(rootWorkflowElementID);
+
+    // Get the base DN targeted by the workflow
+    DN baseDN = DN.decode(workflowCfg.getBaseDN());
+
+    // Create the workflow and register it with the server
+    WorkflowImpl workflowImpl =
+      new WorkflowImpl(workflowId, baseDN, rootWorkflowElement);
+    workflows.put(workflowCfg.dn(), workflowImpl);
+    workflowImpl.register();
+
+    // Register the workflow with the default network group
+    NetworkGroup.getDefaultNetworkGroup().registerWorkflow(workflowImpl);
+  }
+
+}
+
diff --git a/opends/src/server/org/opends/server/core/WorkflowImpl.java b/opends/src/server/org/opends/server/core/WorkflowImpl.java
index dd1f551..0d09a43 100644
--- a/opends/src/server/org/opends/server/core/WorkflowImpl.java
+++ b/opends/src/server/org/opends/server/core/WorkflowImpl.java
@@ -30,6 +30,7 @@
 import org.opends.messages.Message;
 import static org.opends.server.util.Validator.ensureNotNull;
 
+import java.util.Collection;
 import java.util.TreeMap;
 
 import org.opends.server.types.DN;
@@ -108,6 +109,17 @@
 
 
   /**
+   * Performs any finalization that might be required when this
+   * workflow is unloaded.  No action is taken in the default
+   * implementation.
+   */
+  public void finalizeWorkflow()
+  {
+    // No action is required by default.
+  }
+
+
+  /**
    * Gets the base DN of the data set being handled by the workflow.
    *
    * @return the workflow base DN
@@ -230,6 +242,7 @@
     return workflowToDeregister;
   }
 
+
   /**
    * Deregisters all Workflows that have been registered.  This should be
    * called when the server is shutting down.
@@ -242,4 +255,52 @@
         new TreeMap<String, Workflow>();
     }
   }
+
+
+  /**
+   * Gets a workflow that was registered with the server.
+   *
+   * @param workflowID  the ID of the workflow to get
+   * @return the requested workflow
+   */
+  public static Workflow getWorkflow(
+      String workflowID)
+  {
+    return registeredWorkflows.get(workflowID);
+  }
+
+
+  /**
+   * Gets all the workflows that were registered with the server.
+   *
+   * @return the list of registered workflows
+   */
+  public static Collection<Workflow> getWorkflows()
+  {
+    return registeredWorkflows.values();
+  }
+
+
+  /**
+   * Gets the root workflow element for test purpose only.
+   *
+   * @return the root workflow element.
+   */
+  WorkflowElement getRootWorkflowElement()
+  {
+    return rootWorkflowElement;
+  }
+
+
+  /**
+   * Resets all the registered workflows.
+   */
+  public static void resetConfig()
+  {
+    synchronized (registeredWorkflowsLock)
+    {
+      registeredWorkflows = new TreeMap<String, Workflow>();
+    }
+  }
+
 }
diff --git a/opends/src/server/org/opends/server/core/WorkflowTopologyNode.java b/opends/src/server/org/opends/server/core/WorkflowTopologyNode.java
index fa5457f..e86bac5 100644
--- a/opends/src/server/org/opends/server/core/WorkflowTopologyNode.java
+++ b/opends/src/server/org/opends/server/core/WorkflowTopologyNode.java
@@ -500,7 +500,9 @@
 
     // display the baseDN
     DN baseDN = getBaseDN();
-    sb.append(leftMargin + "Workflow baseDN:[");
+    String workflowID = this.getWorkflowImpl().getWorkflowId();
+    sb.append(leftMargin + "Workflow ID = " + workflowID + "\n");
+    sb.append(leftMargin + "         baseDN:[");
     if (baseDN.isNullDN())
     {
       sb.append(" \"\"");
@@ -511,21 +513,26 @@
     }
     sb.append(" ]\n");
 
+    // display the root workflow element
+    sb.append(leftMargin
+        + "         Root Workflow Element: "
+        + getWorkflowImpl().getRootWorkflowElement() + "\n");
+
     // display parent workflow
-    sb.append(leftMargin + "Parent: " + getParent() + "\n");
+    sb.append(leftMargin + "         Parent: " + getParent() + "\n");
 
     // dump each subordinate
-    sb.append(leftMargin + "List of subordinates:\n");
+    sb.append(leftMargin + "         List of subordinates:\n");
     ArrayList<WorkflowTopologyNode> subordinates = getSubordinates();
     if (subordinates.isEmpty())
     {
-      sb.append(leftMargin + "   NONE\n");
+      sb.append(leftMargin + "            NONE\n");
     }
     else
     {
       for (WorkflowTopologyNode subordinate: getSubordinates())
       {
-        sb.append(subordinate.toString(leftMargin + "   "));
+        sb.append(subordinate.toString(leftMargin + "            "));
       }
     }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
index 47a377b..192a9a4 100644
--- a/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/LeafWorkflowElement.java
@@ -26,24 +26,21 @@
  */
 package org.opends.server.workflowelement;
 
+import org.opends.server.admin.std.server.WorkflowElementCfg;
+
 
 
 /**
- * This class gathers all the workflow elements that are encapsulating
- * physical repositories such as local database, remote LDAP servers,
- * JDBC repository or LDIF flat file.
+ * This class defines the super class for all the workflow elements
+ * used to wrap physical repositories. A physical repository contains
+ * data (for example, a local backend, a remote LDAP servers or an
+ * LDIF flat file). Such workflow element is a leaf in the sense that
+ * the workflow element can be used by another workflow element but
+ * cannot use an other workflow element.
+ *
+ * @param  <T>  The type of configuration handled by this workflow elelemnt.
  */
-public abstract class LeafWorkflowElement
-  extends WorkflowElement
+public abstract class LeafWorkflowElement <T extends WorkflowElementCfg>
+  extends WorkflowElement<WorkflowElementCfg>
 {
-  /**
-   * Creates a new instance of the leaf workflow element.
-   *
-   * @param workflowElementID  the workflow element identifier as defined
-   *                           in the configuration.
-   */
-  protected LeafWorkflowElement(String workflowElementID)
-  {
-    super(workflowElementID);
-  }
 }
diff --git a/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
index ce81dd2..eb9eeaa 100644
--- a/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/WorkflowElement.java
@@ -26,6 +26,15 @@
  */
 package org.opends.server.workflowelement;
 
+import static org.opends.server.util.Validator.ensureNotNull;
+import static org.opends.messages.ConfigMessages.*;
+
+import java.util.List;
+import java.util.TreeMap;
+
+import org.opends.messages.Message;
+import org.opends.server.admin.std.server.WorkflowElementCfg;
+import org.opends.server.config.ConfigException;
 import org.opends.server.types.Operation;
 
 
@@ -37,25 +46,48 @@
  * case for load balancing and distribution. And workflow element can be used
  * in a virtual environment to transform data (DN and attribute renaming,
  * attribute value renaming...).
+ *
+ * @param  <T>  The type of configuration handled by this workflow elelemnt.
  */
 public abstract class WorkflowElement
+       <T extends WorkflowElementCfg>
 {
   // Indicates whether the workflow element encapsulates a private local
   // backend.
   private boolean isPrivate = false;
 
+
   // The workflow element identifier.
   private String workflowElementID = null;
 
 
+  // The set of workflow elements registered with the server.
+  // The workflow element identifier is used as a key in the map.
+  private static TreeMap<String, WorkflowElement> registeredWorkflowElements =
+    new TreeMap<String, WorkflowElement>();
+
+
+  // A lock to protect access to the registered workflow elements.
+  private static Object registeredWorkflowElementsLock = new Object();
+
 
   /**
    * Creates a new instance of the workflow element.
+   */
+  public WorkflowElement()
+  {
+    // There is nothing to do in the constructor.
+  }
+
+
+
+  /**
+   * Initializes the instance of the workflow element.
    *
    * @param workflowElementID  the workflow element identifier as defined
    *                           in the configuration.
    */
-  public WorkflowElement(String workflowElementID)
+  public void initialize(String workflowElementID)
   {
     this.workflowElementID = workflowElementID;
   }
@@ -63,6 +95,41 @@
 
 
   /**
+   * Indicates whether the provided configuration is acceptable for
+   * this workflow elelement.
+   *
+   * @param  configuration        The workflow element configuration for
+   *                              which to make the determination.
+   * @param  unacceptableReasons  A list that may be used to hold the
+   *                              reasons that the provided
+   *                              configuration is not acceptable.
+   *
+   * @return  {@code true} if the provided configuration is acceptable
+   *          for this workflow element, or {@code false} if not.
+   */
+  public boolean isConfigurationAcceptable(
+      WorkflowElementCfg configuration,
+      List<String> unacceptableReasons)
+  {
+    // This default implementation does not perform any special
+    // validation.  It should be overridden by workflow element
+    // implementations that wish to perform more detailed validation.
+    return true;
+  }
+
+
+  /**
+   * Performs any finalization that might be required when this
+   * workflow element is unloaded.  No action is taken in the default
+   * implementation.
+   */
+  public void finalizeWorkflowElement()
+  {
+    // No action is required by default.
+  }
+
+
+  /**
    * Executes the workflow element for an operation.
    *
    * @param operation the operation to execute
@@ -108,5 +175,78 @@
   {
     return workflowElementID;
   }
+
+
+  /**
+   * Registers the workflow element (this) with the server.
+   *
+   * @throws  ConfigException  If the workflow element ID for the provided
+   *                           workflow element conflicts with the workflow
+   *                           element ID of an existing workflow element.
+   */
+  public void register()
+      throws ConfigException
+  {
+    ensureNotNull(workflowElementID);
+
+    synchronized (registeredWorkflowElementsLock)
+    {
+      // the workflow element must not be already registered
+      if (registeredWorkflowElements.containsKey(workflowElementID))
+      {
+        Message message = ERR_CONFIG_WORKFLOW_ELEMENT_ALREADY_REGISTERED.get(
+            workflowElementID);
+        throw new ConfigException(message);
+      }
+
+      TreeMap<String, WorkflowElement> newWorkflowElements =
+        new TreeMap<String, WorkflowElement>(registeredWorkflowElements);
+      newWorkflowElements.put(workflowElementID, this);
+      registeredWorkflowElements = newWorkflowElements;
+    }
+  }
+
+
+  /**
+   * Deregisters the workflow element (this) with the server.
+   */
+  public void deregister()
+  {
+    ensureNotNull(workflowElementID);
+
+    synchronized (registeredWorkflowElementsLock)
+    {
+      TreeMap<String, WorkflowElement> newWorkflowElements =
+        new TreeMap<String, WorkflowElement>(registeredWorkflowElements);
+      newWorkflowElements.remove(workflowElementID);
+      registeredWorkflowElements = newWorkflowElements;
+    }
+  }
+
+
+  /**
+   * Gets a workflow element that was registered with the server.
+   *
+   * @param workflowElementID  the ID of the workflow element to get
+   * @return the requested workflow element
+   */
+  public static WorkflowElement getWorkflowElement(
+      String workflowElementID)
+  {
+    return registeredWorkflowElements.get(workflowElementID);
+  }
+
+
+  /**
+   * Resets all the registered workflows.
+   */
+  public static void resetConfig()
+  {
+    synchronized (registeredWorkflowElementsLock)
+    {
+      registeredWorkflowElements = new TreeMap<String, WorkflowElement>();
+    }
+  }
+
 }
 
diff --git a/opends/src/server/org/opends/server/workflowelement/WorkflowElementConfigManager.java b/opends/src/server/org/opends/server/workflowelement/WorkflowElementConfigManager.java
new file mode 100644
index 0000000..115e0e3
--- /dev/null
+++ b/opends/src/server/org/opends/server/workflowelement/WorkflowElementConfigManager.java
@@ -0,0 +1,448 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.workflowelement;
+
+
+
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+import static org.opends.messages.ConfigMessages.*;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+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.server.ServerManagementContext;
+import org.opends.server.admin.std.meta.WorkflowElementCfgDefn;
+import org.opends.server.admin.std.server.RootCfg;
+import org.opends.server.admin.std.server.WorkflowElementCfg;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.DN;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+
+
+/**
+ * This class defines a utility that will be used to manage the configuration
+ * for the set of workflow elements defined in the Directory Server.
+ * It will perform the necessary initialization of those backends when the
+ * server is first started, and then will manage any changes to them while
+ * the server is running.
+ */
+public class WorkflowElementConfigManager
+       implements ConfigurationChangeListener<WorkflowElementCfg>,
+                  ConfigurationAddListener   <WorkflowElementCfg>,
+                  ConfigurationDeleteListener<WorkflowElementCfg>
+
+{
+  // A mapping between the DNs of the config entries and the associated
+  // workflow elements.
+  private ConcurrentHashMap<DN, WorkflowElement> workflowElements;
+
+
+
+  /**
+   * Creates a new instance of this workflow config manager.
+   */
+  public WorkflowElementConfigManager()
+  {
+    workflowElements = new ConcurrentHashMap<DN, WorkflowElement>();
+  }
+
+
+
+  /**
+   * Initializes all workflow elements currently defined in the Directory
+   * Server configuration.  This should only be called at Directory Server
+   * startup.
+   *
+   * @throws  ConfigException  If a configuration problem causes the workflow
+   *                           element initialization process to fail.
+   * @throws InitializationException If a problem occurs while the workflow
+   *                                 element is loaded and registered with
+   *                                 the server
+   */
+  public void initializeWorkflowElements()
+      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 workflow element entries are added or removed.
+    rootConfiguration.addWorkflowElementAddListener(this);
+    rootConfiguration.addWorkflowElementDeleteListener(this);
+
+
+    //Initialize the existing workflows.
+    for (String workflowName : rootConfiguration.listWorkflowElements())
+    {
+      WorkflowElementCfg workflowConfiguration =
+        rootConfiguration.getWorkflowElement(workflowName);
+      workflowConfiguration.addChangeListener(this);
+
+      if (workflowConfiguration.isEnabled())
+      {
+        loadAndRegisterWorkflowElement(workflowConfiguration);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationAddAcceptable(
+      WorkflowElementCfg configuration,
+      List<Message> unacceptableReasons)
+  {
+    boolean isAcceptable = true;
+
+    if (configuration.isEnabled())
+    {
+      // Get the name of the class and make sure we can instantiate it as
+      // a workflow element.
+      String className = configuration.getJavaClass();
+      try
+      {
+        // Load the class but don't initialize it.
+        loadWorkflowElement(className, configuration, false);
+      }
+      catch (InitializationException ie)
+      {
+        unacceptableReasons.add (ie.getMessageObject());
+        isAcceptable = false;
+      }
+    }
+
+    return isAcceptable;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationAdd(
+      WorkflowElementCfg configuration)
+  {
+    // Returned result.
+    ConfigChangeResult changeResult = new ConfigChangeResult(
+        ResultCode.SUCCESS, false, new ArrayList<Message>()
+        );
+
+    configuration.addChangeListener(this);
+
+    // If the new workflow element is enabled then create it and register it.
+    if (configuration.isEnabled())
+    {
+      try
+      {
+        loadAndRegisterWorkflowElement(configuration);
+      }
+      catch (InitializationException de)
+      {
+        if (changeResult.getResultCode() == ResultCode.SUCCESS)
+        {
+          changeResult.setResultCode(
+              DirectoryServer.getServerErrorResultCode());
+        }
+        changeResult.addMessage(de.getMessageObject());
+      }
+    }
+
+    return changeResult;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationDeleteAcceptable(
+      WorkflowElementCfg configuration,
+      List<Message> unacceptableReasons)
+  {
+    // FIXME -- We should try to perform some check to determine whether the
+    // worklfow element is in use.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationDelete(
+      WorkflowElementCfg configuration)
+  {
+    // Returned result.
+    ConfigChangeResult changeResult = new ConfigChangeResult(
+        ResultCode.SUCCESS, false, new ArrayList<Message>()
+        );
+
+
+    WorkflowElement workflowElement =
+      workflowElements.remove(configuration.dn());
+    if (workflowElement != null)
+    {
+      workflowElement.deregister();
+      workflowElement.finalizeWorkflowElement();
+    }
+
+
+    return changeResult;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+      WorkflowElementCfg configuration,
+      List<Message> unacceptableReasons)
+  {
+    boolean isAcceptable = true;
+
+    if (configuration.isEnabled())
+    {
+      // Get the name of the class and make sure we can instantiate it as
+      // a workflow element.
+      String className = configuration.getJavaClass();
+      try
+      {
+        // Load the class but don't initialize it.
+        loadWorkflowElement(className, configuration, false);
+      }
+      catch (InitializationException ie)
+      {
+        unacceptableReasons.add (ie.getMessageObject());
+        isAcceptable = false;
+      }
+    }
+
+    return isAcceptable;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      WorkflowElementCfg configuration)
+  {
+    // Returned result.
+    ConfigChangeResult changeResult = new ConfigChangeResult(
+        ResultCode.SUCCESS, false, new ArrayList<Message>()
+        );
+
+
+    // Get the existing workflow element if it's already enabled.
+    WorkflowElement existingWorkflowElement =
+      workflowElements.get(configuration.dn());
+
+    // If the new configuration has the workflow element disabled,
+    // then disable it if it is enabled, or do nothing if it's already disabled.
+    if (! configuration.isEnabled())
+    {
+      if (existingWorkflowElement != null)
+      {
+        workflowElements.remove(configuration.dn());
+        existingWorkflowElement.deregister();
+        existingWorkflowElement.finalizeWorkflowElement();
+      }
+
+      return changeResult;
+    }
+
+    // If the workflow element is disabled then create it and register it.
+    if (existingWorkflowElement == null)
+    {
+      try
+      {
+        loadAndRegisterWorkflowElement(configuration);
+      }
+      catch (InitializationException de)
+      {
+        if (changeResult.getResultCode() == ResultCode.SUCCESS)
+        {
+          changeResult.setResultCode(
+              DirectoryServer.getServerErrorResultCode());
+        }
+        changeResult.addMessage(de.getMessageObject());
+      }
+    }
+
+    return changeResult;
+  }
+
+
+  /**
+   * Loads a class and instanciates it as a workflow element. The workflow
+   * element is initialized and registered with the server.
+   *
+   * @param workflowCfg  the workflow element configuration
+   *
+   * @throws InitializationException If a problem occurs while trying to
+   *                            decode a provided string as a DN or if
+   *                            the workflow element ID for a provided
+   *                            workflow element conflicts with the workflow
+   *                            ID of an existing workflow during workflow
+   *                            registration.
+   */
+  private void loadAndRegisterWorkflowElement(
+      WorkflowElementCfg workflowElementCfg
+      ) throws InitializationException
+  {
+    // Load the workflow element class
+    String className = workflowElementCfg.getJavaClass();
+    WorkflowElement workflowElement =
+      loadWorkflowElement(className, workflowElementCfg, true);
+
+    try
+    {
+      // register the workflow element
+      workflowElement.register();
+
+      // keep the workflow element in the list of configured workflow
+      // elements
+      workflowElements.put(workflowElementCfg.dn(), workflowElement);
+    }
+    catch (ConfigException de)
+    {
+      throw new InitializationException(de.getMessageObject());
+    }
+  }
+
+
+  /**
+   * Loads a class and instanciates it as a workflow element. If requested
+   * initializes the newly created instance.
+   *
+   * @param  className      The fully-qualified name of the workflow element
+   *                        class to load, instantiate, and initialize.
+   * @param  configuration  The configuration to use to initialize the workflow
+   *                        element.  It must not be {@code null}.
+   * @param  initialize     Indicates whether the workflow element instance
+   *                        should be initialized.
+   *
+   * @return  The possibly initialized workflow element.
+   *
+   * @throws  InitializationException  If a problem occurred while attempting
+   *                                   to initialize the workflow element.
+   */
+  private WorkflowElement loadWorkflowElement(
+      String className,
+      WorkflowElementCfg configuration,
+      boolean initialize
+      ) throws InitializationException
+  {
+    try
+    {
+      WorkflowElementCfgDefn              definition;
+      ClassPropertyDefinition             propertyDefinition;
+      Class<? extends WorkflowElement>    workflowElementClass;
+      WorkflowElement<? extends WorkflowElementCfg> workflowElement;
+
+      definition = WorkflowElementCfgDefn.getInstance();
+      propertyDefinition =
+        definition.getJavaClassPropertyDefinition();
+      workflowElementClass =
+        propertyDefinition.loadClass(className, WorkflowElement.class);
+      workflowElement =
+        (WorkflowElement<? extends WorkflowElementCfg>)
+          workflowElementClass.newInstance();
+
+      if (initialize)
+      {
+        Method method = workflowElement.getClass().getMethod(
+            "initializeWorkflowElement",
+            configuration.definition().getServerConfigurationClass()
+            );
+        method.invoke(workflowElement, configuration);
+      }
+      else
+      {
+        Method method = workflowElement.getClass().getMethod(
+            "isConfigurationAcceptable",
+            WorkflowElementCfg.class,
+            List.class);
+
+        List<String> unacceptableReasons = new ArrayList<String>();
+        Boolean acceptable = (Boolean) method.invoke(
+            workflowElement, configuration, unacceptableReasons);
+
+        if (! acceptable)
+        {
+          StringBuilder buffer = new StringBuilder();
+          if (! unacceptableReasons.isEmpty())
+          {
+            Iterator<String> iterator = unacceptableReasons.iterator();
+            buffer.append(iterator.next());
+            while (iterator.hasNext())
+            {
+              buffer.append(".  ");
+              buffer.append(iterator.next());
+            }
+          }
+
+          Message message =
+            ERR_CONFIG_WORKFLOW_ELEMENT_CONFIG_NOT_ACCEPTABLE.get(
+              String.valueOf(configuration.dn()), buffer.toString());
+          throw new InitializationException(message);
+        }
+      }
+
+      return workflowElement;
+    }
+    catch (Exception e)
+    {
+      Message message =
+        ERR_CONFIG_WORKFLOW_ELEMENT_CANNOT_INITIALIZE.get(
+            className, String.valueOf(configuration.dn()),
+            stackTraceToSingleLineString(e));
+      throw new InitializationException(message);
+    }
+  }
+
+}
+
diff --git a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
index 2cfc903..5a4d1dc 100644
--- a/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
+++ b/opends/src/server/org/opends/server/workflowelement/localbackend/LocalBackendWorkflowElement.java
@@ -32,15 +32,23 @@
 import java.util.List;
 import java.util.TreeMap;
 
+import org.opends.messages.Message;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.LocalBackendWorkflowElementCfg;
 import org.opends.server.api.Backend;
+import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.BindOperation;
 import org.opends.server.core.CompareOperation;
 import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.ModifyDNOperation;
 import org.opends.server.core.ModifyOperation;
 import org.opends.server.core.SearchOperation;
+import org.opends.server.types.ConfigChangeResult;
+import org.opends.server.types.InitializationException;
 import org.opends.server.types.Operation;
+import org.opends.server.types.ResultCode;
 import org.opends.server.workflowelement.LeafWorkflowElement;
 
 
@@ -49,37 +57,152 @@
  * This class defines a local backend workflow element; e-g an entity that
  * handle the processing of an operation aginst a local backend.
  */
-public class LocalBackendWorkflowElement extends LeafWorkflowElement
+public class LocalBackendWorkflowElement extends
+    LeafWorkflowElement<LocalBackendWorkflowElementCfg>
+    implements ConfigurationChangeListener<LocalBackendWorkflowElementCfg>
 {
   // the backend associated with the local workflow element
   private Backend backend;
 
+
   // the set of local backend workflow elements registered with the server
   private static TreeMap<String, LocalBackendWorkflowElement>
        registeredLocalBackends =
             new TreeMap<String, LocalBackendWorkflowElement>();
 
+
   // a lock to guarantee safe concurrent access to the registeredLocalBackends
   // variable
   private static Object registeredLocalBackendsLock = new Object();
 
 
-
   /**
    * Creates a new instance of the local backend workflow element.
+   */
+  public LocalBackendWorkflowElement()
+  {
+    // There is nothing to do in this constructor.
+  }
+
+
+  /**
+   * Initializes a new instance of the local backend workflow element.
+   * This method is intended to be called by DirectoryServer when
+   * workflow configuration mode is auto as opposed to
+   * initializeWorkflowElement which is invoked when workflow
+   * configuration mode is manual.
    *
    * @param workflowElementID  the workflow element identifier
    * @param backend  the backend associated to that workflow element
    */
-  private LocalBackendWorkflowElement(String workflowElementID, Backend backend)
+  private void initialize(String workflowElementID, Backend backend)
   {
-    super(workflowElementID);
+    // Initialize the workflow ID
+    super.initialize(workflowElementID);
 
     this.backend  = backend;
-    setPrivate(backend.isPrivateBackend());
+
+    if (this.backend != null)
+    {
+      setPrivate(this.backend.isPrivateBackend());
+    }
   }
 
 
+  /**
+   * {@inheritDoc}
+   */
+  public void initializeWorkflowElement(
+      LocalBackendWorkflowElementCfg configuration
+      ) throws ConfigException, InitializationException
+  {
+    configuration.addLocalBackendChangeListener(this);
+
+    // Read configuration and apply changes.
+    processWorkflowElementConfig(configuration, true);
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void finalizeWorkflowElement()
+  {
+    // null all fields so that any use of the finalized object will raise
+    // an NPE
+    super.initialize(null);
+    backend = null;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+      LocalBackendWorkflowElementCfg configuration,
+      List<Message>                  unacceptableReasons
+      )
+  {
+    boolean isAcceptable =
+      processWorkflowElementConfig(configuration, false);
+
+    return isAcceptable;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(
+      LocalBackendWorkflowElementCfg configuration
+      )
+  {
+    // Returned result.
+    ConfigChangeResult changeResult = new ConfigChangeResult(
+        ResultCode.SUCCESS, false, new ArrayList<Message>()
+        );
+
+    processWorkflowElementConfig(configuration, true);
+
+    return changeResult;
+  }
+
+
+  /**
+   * Parses the provided configuration and configure the workflow element.
+   *
+   * @param configuration  The new configuration containing the changes.
+   * @param applyChanges   If true then take into account the new configuration.
+   *
+   * @return  <code>true</code> if the configuration is acceptable.
+   */
+  private boolean processWorkflowElementConfig(
+      LocalBackendWorkflowElementCfg configuration,
+      boolean                        applyChanges
+      )
+  {
+    // returned status
+    boolean isAcceptable = true;
+
+    // If the workflow element is disabled then do nothing. Note that the
+    // config manager could have finalized the object right before.
+    if (configuration.isEnabled())
+    {
+      // Read configuration.
+      String newBackendID = configuration.getBackend();
+      Backend newBackend  = DirectoryServer.getBackend(newBackendID);
+
+      // Get the new config
+      if (applyChanges)
+      {
+        super.initialize(configuration.getWorkflowElementId());
+        backend = newBackend;
+      }
+    }
+
+    return isAcceptable;
+  }
+
 
   /**
    * Creates and registers a local backend with the server.
@@ -92,8 +215,9 @@
    *         already created or a newly created local backend workflow
    *         element.
    */
-  public static LocalBackendWorkflowElement create(String workflowElementID,
-                                                   Backend backend)
+  public static LocalBackendWorkflowElement createAndRegister(
+      String workflowElementID,
+      Backend backend)
   {
     LocalBackendWorkflowElement localBackend = null;
 
@@ -101,8 +225,8 @@
     localBackend = registeredLocalBackends.get(workflowElementID);
     if (localBackend == null)
     {
-      localBackend = new LocalBackendWorkflowElement(workflowElementID,
-                                                     backend);
+      localBackend = new LocalBackendWorkflowElement();
+      localBackend.initialize(workflowElementID, backend);
 
       // store the new local backend in the list of registered backends
       registerLocalBackend(localBackend);
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
index ef3e60b..72d879d 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/admin/ValidateConfigDefinitionsTest.java
@@ -133,6 +133,10 @@
                   "backend-id",
                   "plugin-type",
                   "replication-server-id",
+                  "network-group-id",
+                  "workflow-id",
+                  "workflow-element-id",
+                  "workflow-element"
                   // e.g. "prop-name-starting-with-object-prefix"
           });
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowConfigurationTest.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowConfigurationTest.java
new file mode 100644
index 0000000..7eea3ad
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowConfigurationTest.java
@@ -0,0 +1,689 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+import static org.opends.server.util.StaticUtils.createEntry;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.ArrayList;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.api.Backend;
+import org.opends.server.api.ClientConnection;
+import org.opends.server.config.ConfigConstants;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPAttribute;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.Entry;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.RawModification;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchScope;
+import org.opends.server.util.StaticUtils;
+import org.opends.server.util.UtilTestCase;
+import org.opends.server.workflowelement.localbackend.LocalBackendWorkflowElement;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+
+
+/**
+ * This class tests the 'manual' workflow configuration mode. The 'auto'
+ * configuration mode does not require any specific unit test because by
+ * default the server is running with the 'auto' mode.
+ *
+ * With the manual configuration mode, all the network groups, workflows
+ * and workflow elements must be defined in the configuration file.
+ */
+public class WorkflowConfigurationTest extends UtilTestCase
+{
+  // The base DN of the config backend
+  private static final String configBaseDN = ConfigConstants.DN_CONFIG_ROOT;
+
+  // The base DN of the rootDSE backend
+  private static final String rootDSEBaseDN = "";
+
+  // The workflow configuration mode attribute
+  private static final String workflowModeAttributeType =
+      "ds-cfg-workflow-configuration-mode";
+
+  // The suffix attribute in a backend
+  private static final String suffixAttributeType =
+      "ds-cfg-base-dn";
+
+  // The auto/manual modes
+  private static final String workflowConfigModeAuto   = "auto";
+  private static final String workflowConfigModeManual = "manual";
+
+  
+
+  //===========================================================================
+  //                      B E F O R E    C L A S S
+  //===========================================================================
+
+  /**
+   * Set 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
+  {
+    // Start the server so that we can update the configuration and execute
+    // some LDAP operations
+    TestCaseUtils.startServer();
+
+    // Add the attribute ds-cfg-workflow-configuration-mode with the
+    // value 'auto
+    initializeConfigurationMode();
+    checkBackendIsAccessible("o=test");
+  }
+
+
+
+  //===========================================================================
+  //                    D A T A    P R O V I D E R
+  //===========================================================================
+
+
+
+  //===========================================================================
+  //                           U T I L S
+  //===========================================================================
+
+  /**
+   * Adds an attribute ds-cfg-workflow-configuration-mode in the entry
+   * cn=config. The added value is 'auto'.
+   */
+  private void initializeConfigurationMode()
+      throws Exception
+  {
+    // Add the ds-cfg-workflow-configuration-mode attribute and set
+    // its value to "auto"
+    ModifyOperationBasis modifyOperation = getModifyOperation(
+        configBaseDN,
+        ModificationType.ADD,
+        workflowModeAttributeType,
+        workflowConfigModeAuto);
+
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+  
+  /**
+   * Checks that test backend is accessible as well as config backend
+   * and rootDSE backend.
+   *
+   * @param baseDN  the baseDN of the backend to check
+   */
+  private void checkBackendIsAccessible(String baseDN)
+      throws Exception
+  {
+    // The config backend and rootDSE backend should always be accessible
+    doSearch(rootDSEBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+    doSearch(configBaseDN,  SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+
+    // The test backend should be accessible
+    doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+  }
+
+
+  /**
+   * Checks that test backend is not accessible while config backend
+   * and rootDSE backend are.
+   *
+   * @param baseDN  the baseDN of the backend to check
+   */
+  private void checkBackendIsNotAccessible(String baseDN)
+      throws Exception
+  {
+    // The config backend and rootDSE should always be accessible
+    doSearch(rootDSEBaseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+    doSearch(configBaseDN,  SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+
+    // The test backend should be accessible
+    doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.NO_SUCH_OBJECT);
+  }
+
+
+  /**
+   * Sets the ds-cfg-workflow-configuration-mode attribute to 'auto'
+   */
+  private void setModeAuto() throws Exception
+  {
+    ModifyOperationBasis modifyOperation = getModifyOperation(
+        configBaseDN,
+        ModificationType.REPLACE,
+        workflowModeAttributeType,
+        workflowConfigModeAuto);
+
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+ 
+
+  /**
+   * Sets the ds-cfg-workflow-configuration-mode attribute to 'auto'
+   */
+  private void setModeManual() throws Exception
+  {
+    ModifyOperationBasis modifyOperation = getModifyOperation(
+        configBaseDN,
+        ModificationType.REPLACE,
+        workflowModeAttributeType,
+        workflowConfigModeManual);
+
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+ 
+  /**
+   * Performs a search on a provided base DN.
+   *
+   * @param baseDN              the search base DN
+   * @param scope               the scope of the search
+   * @param expectedResultCode  the expected result code
+   *
+   * @return the search operation used for the test
+   */
+  private InternalSearchOperation doSearch(
+      String      baseDN,
+      SearchScope scope,
+      ResultCode  expectedResultCode
+      ) throws Exception
+  {
+    InternalSearchOperation searchOperation = new InternalSearchOperation(
+       InternalClientConnection.getRootConnection(),
+       InternalClientConnection.nextOperationID(),
+       InternalClientConnection.nextMessageID(),
+       new ArrayList<Control>(),
+       new ASN1OctetString(baseDN),
+       scope,
+       DereferencePolicy.NEVER_DEREF_ALIASES,
+       Integer.MAX_VALUE,
+       Integer.MAX_VALUE,
+       false,
+       LDAPFilter.decode("(objectClass=*)"),
+       null, null);
+
+    searchOperation.run();
+    assertEquals(searchOperation.getResultCode(), expectedResultCode);
+    
+    return searchOperation;
+  }
+
+
+  /**
+   * Provides a modify operation.
+   *
+   * @param entryDN        the DN of the entry targeted by the modify operation
+   * @param modType        the type of the modification
+   * @param attributeType  the type of the attribute to modify
+   * @param attributeValue the value of the attribute to modify
+   */
+  private static ModifyOperationBasis getModifyOperation(
+      String           entryDN,
+      ModificationType modType,
+      String           attributeType,
+      String           attributeValue)
+  {
+    ArrayList<ASN1OctetString> ldapValues = new ArrayList<ASN1OctetString>();
+    ldapValues.add(new ASN1OctetString(attributeValue));
+
+    LDAPAttribute ldapAttr = new LDAPAttribute(attributeType, ldapValues);
+
+    ArrayList<RawModification> ldapMods = new ArrayList<RawModification>();
+    ldapMods.add(new LDAPModification(modType, ldapAttr));
+    
+    ModifyOperationBasis modifyOperation = new ModifyOperationBasis(
+        InternalClientConnection.getRootConnection(),
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(),
+        new ArrayList<Control>(),
+        new ASN1OctetString(entryDN),
+        ldapMods);
+
+    return modifyOperation;
+  }
+
+  
+  /**
+   * Creates a workflow to handle a local backend. The default network
+   * group is used.
+   *
+   * @param baseDN     the base DN of the workflow
+   * @param backendID  the backend which contains the baseDN
+   *
+   * @return the newly created workflow
+   */
+  private WorkflowImpl createWorkflow(String baseDN, String backendID)
+      throws Exception
+  {
+    // Get the backend
+    Backend backend = DirectoryServer.getBackend(backendID);
+    assertNotNull(backend);
+    
+    // Create the workflow element that wraps the local backend
+    String workflowElementID = baseDN + "#" + backendID;
+    LocalBackendWorkflowElement workflowElement =
+      LocalBackendWorkflowElement.createAndRegister(workflowElementID, backend);
+    
+    // Create a workflow and register it with the server
+    String workflowID = baseDN + "#" + backendID;
+    WorkflowImpl workflowImpl = new WorkflowImpl(
+        workflowID, DN.decode(baseDN), workflowElement);
+    workflowImpl.register();
+    
+    // Register the workflow with the default network group
+    NetworkGroup.getDefaultNetworkGroup().registerWorkflow(workflowImpl);
+    
+    return workflowImpl;
+  }
+  
+  
+  /**
+   * Removes a workflow.
+   *
+   * @param baseDN     the base DN of the workflow
+   * @param backendID  the backend which contains the baseDN
+   */
+  private void removeWorkflow(String baseDN, String backendID)
+      throws Exception
+  {
+    // Elaborate the workflow ID
+    String workflowID = baseDN + "#" + backendID;
+
+    // Deregister the workflow with the default network group
+    NetworkGroup.getDefaultNetworkGroup().deregisterWorkflow(workflowID);
+
+    // Deregister the workflow with the server
+    Workflow workflow = WorkflowImpl.getWorkflow(workflowID);
+    WorkflowImpl workflowImpl = (WorkflowImpl) workflow;
+    workflowImpl.deregister();
+  }
+  
+  
+  /**
+   * Adds a new suffix in a backend.
+   *
+   * @param baseDN     the DN of the suffix to add
+   * @param backendID  the identifier of the backend to which the suffix
+   *                   is added
+   */
+  private void addSuffix(String baseDN, String backendID)
+      throws Exception
+  {
+    // Elaborate the DN of the backend config entry
+    String backendDN = elaborateBackendDN(backendID);
+
+    // Add a new suffix in the backend
+    ModifyOperationBasis modifyOperation = getModifyOperation(
+        backendDN,
+        ModificationType.ADD,
+        suffixAttributeType,
+        baseDN);
+
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+  
+  
+  /**
+   * Create a base entry for a new suffix.
+   *
+   * @param baseDN     the DN of the new base entry
+   * @param backendID  the identifier of the backend
+   */
+  private void createBaseEntry(String baseDN, String backendID)
+      throws Exception
+  {
+    Entry entry = StaticUtils.createEntry(DN.decode(baseDN));
+
+    AddOperationBasis addOperation = new AddOperationBasis(
+        InternalClientConnection.getRootConnection(),
+        InternalClientConnection.nextOperationID(),
+        InternalClientConnection.nextMessageID(),
+        null,
+        entry.getDN(),
+        entry.getObjectClasses(),
+        entry.getUserAttributes(),
+        entry.getOperationalAttributes());
+
+    addOperation.run();
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+  
+  
+  /**
+   * Removes a new suffix in a backend.
+   *
+   * @param baseDN     the DN of the suffix to remove
+   * @param backendID  the identifier of the backend to which the suffix
+   *                   is removed
+   *
+   * @throw Exception  if the backend does not exist or if the suffix
+   *                   already exist in the backend
+   */
+  private void removeSuffix(String baseDN, String backendID)
+      throws Exception
+  {
+    // Elaborate the DN of the backend config entry
+    String backendDN = elaborateBackendDN(backendID);
+
+    // Add a new suffix in the backend
+    ModifyOperationBasis modifyOperation = getModifyOperation(
+        backendDN,
+        ModificationType.DELETE,
+        suffixAttributeType,
+        baseDN);
+
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+  
+  
+  /**
+   * Elaborates a DN for a backend config entry.
+   *
+   * @param backendID  the identifier of the backend to retrieve
+   */
+  private String elaborateBackendDN(String backendID)
+  {
+    String backendDN =
+        "ds-cfg-backend-id=" + backendID + ",cn=Backends,cn=config";
+    return backendDN;
+  }
+
+  
+  /**
+   * Initializes a memory-based backend.
+   *
+   * @param  backendID        the identifier of the backend to create
+   * @param  baseDN           the DN of the suffix to create
+   * @param  createBaseEntry  indicate whether to automatically create the base
+   *                          entry and add it to the backend.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  private static void createBackend(
+      String  backendID,
+      String  baseDN,
+      boolean createBaseEntry
+      ) throws Exception
+  {
+    TestCaseUtils.dsconfig(
+        "create-backend",
+        "--backend-name", backendID,
+        "--type", "memory",
+        "--set", "base-dn:" + baseDN,
+        "--set", "writability-mode:enabled",
+        "--set", "enabled:true");
+    
+    if (createBaseEntry)
+    {
+      Backend backend = DirectoryServer.getBackend(backendID);
+      Entry e = createEntry(DN.decode(baseDN));
+      backend.addEntry(e, null);
+    }
+  }
+
+
+  /**
+   * Remove a backend.
+   *
+   * @param  backendID  the identifier of the backend to remove
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  private static void removeMemoryBackend(
+      String backendID
+      ) throws Exception
+  {
+    TestCaseUtils.dsconfig(
+        "delete-backend",
+        "--backend-name", backendID);
+  }
+
+
+
+  //===========================================================================
+  //                      T E S T    C A S E S
+  //===========================================================================
+
+  /**
+   * This test checks the transition from mode 'auto' to 'manual' and
+   * 'manual' back to 'auto'. In this test there is no configuration for
+   * network group, workflow and workflow element.
+   */
+  @Test
+  public void transitionAutoManualAuto() throws Exception
+  {
+    // Settings
+    String testBaseDN = "o=test";
+
+    // The ds-cfg-workflow-configuration-mode attribute value is "auto"
+    // (default value), let's put the same value again. Putting the same
+    // value should have no impact and we should be able to perform a search
+    // on the test backend.
+    setModeAuto();
+    checkBackendIsAccessible(testBaseDN);
+
+    // Change the ds-cfg-workflow-configuration-mode attribute value
+    // to "manual". The workflows should be fully reconfigured. But as
+    // there is no configuration for the workflows, only cn=config and
+    // rootDSE should be accessible.
+    setModeManual();
+    checkBackendIsNotAccessible(testBaseDN);
+
+    // Change the ds-cfg-workflow-configuration-mode attribute value
+    // back to "auto". All the local backends should be accessible again.
+    setModeAuto();
+    checkBackendIsAccessible(testBaseDN);
+  }
+
+ 
+  /**
+   * This test checks the basic operation routing when configuration
+   * mode is 'manual'. Few workflows are configured for the test.
+   */
+  @Test
+  public void basicRoutingManualMode() throws Exception
+  {
+    // Settings
+    String testBaseDN    = "o=test";
+    String testBackendID = "test";
+
+    // Workflow configuration mode is auto, so test backend should
+    // be accessible
+    checkBackendIsAccessible(testBaseDN);
+
+    // Set the workflow configuration mode to manual. In this mode
+    // no there is no workflow by default (but the config and rootDSE
+    // workflows) so seaarches on the test backend should fail.
+    setModeManual();
+    checkBackendIsNotAccessible(testBaseDN);
+    
+    // Create a workflow to handle o=test backend then check that test
+    // backend is now accessible
+    createWorkflow(testBaseDN, testBackendID);
+    checkBackendIsAccessible(testBaseDN);
+    
+    // Change workflow configuration mode back to auto and check that
+    // test backend is still accessible
+    setModeAuto();
+    checkBackendIsAccessible(testBaseDN);
+  }
+
+ 
+  /**
+   * This test checks the add/remove of suffix in a backend in manual
+   * configuration mode.
+   */
+  @Test
+  public void addRemoveSuffix() throws Exception
+  {
+    // Settings
+    String testBaseDN2    = "o=addRemoveSuffix_1";
+    String testBaseDN3    = "o=addRemoveSuffix_2";
+    String testBackendID2 = "userRoot";
+
+    // make sure we are in auto mode and check that the new suffixes are
+    // not already defined on the server.
+    setModeAuto();
+    checkBackendIsNotAccessible(testBaseDN2);
+    checkBackendIsNotAccessible(testBaseDN3);
+
+    // Add a new suffix to the test backend and check that the new
+    // suffix is accessible (we are in auto mode).
+    addSuffix(testBaseDN2, testBackendID2);
+    createBaseEntry(testBaseDN2, testBackendID2);
+    checkBackendIsAccessible(testBaseDN2);
+
+    // Remove the suffix and check that the removed suffix is no
+    // more accessible.
+    removeSuffix(testBaseDN2, testBackendID2);
+    checkBackendIsNotAccessible(testBaseDN2);
+
+    // Now move to the manual mode.
+    setModeManual();
+
+    // Add a new suffix and configure a workflow to route operation
+    // to this new suffix, then check that the new suffix is accessible.
+    // Note that before we can create a base entry we need to configure
+    // first a workflow to route the ADD operation to the right suffix.
+    // This need not be to be done in auto mode because with the auto mode
+    // the workflow is automatically created when a new suffix is added.
+    addSuffix(testBaseDN3, testBackendID2);
+    createWorkflow(testBaseDN3, testBackendID2);
+    createBaseEntry(testBaseDN3, testBackendID2);
+    checkBackendIsAccessible(testBaseDN3);
+
+    // Finally remove the new workflow and suffix and check that the suffix
+    // is no more accessible.
+    removeWorkflow(testBaseDN3, testBackendID2);
+    removeSuffix(testBaseDN3, testBackendID2);
+    checkBackendIsNotAccessible(testBaseDN3);    
+  }
+
+ 
+  /**
+   * This test checks the add/remove of a backend in manual configuration
+   * mode.
+   */
+  @Test
+  public void addRemoveBackend() throws Exception
+  {
+    // Local settings
+    String backendID1 = "addRemoveBackend_1";
+    String backendID2 = "addRemoveBackend_2";
+    String baseDN1    = "o=addRemoveBackendBaseDN_1";
+    String baseDN2    = "o=addRemoveBackendBaseDN_2";
+    
+    // Make sure we are in auto mode and check the suffix is not accessible
+    setModeAuto();
+    checkBackendIsNotAccessible(baseDN1);
+    
+    // Create a backend and check that the base entry is accessible.
+    createBackend(backendID1, baseDN1, true);
+    checkBackendIsAccessible(baseDN1);
+    
+    // Remove the backend and check that the suffix is no more accessible.
+    removeMemoryBackend(backendID1);
+    checkBackendIsNotAccessible(baseDN1);
+    
+    // Now move to the manual mode
+    setModeManual();
+    checkBackendIsNotAccessible(baseDN2);
+    
+    // Create a backend and create a workflow to route operations to that
+    // new backend. Then check that the base entry is accessible.
+    createBackend(backendID2, baseDN2, true);
+    createWorkflow(baseDN2, backendID2);
+    checkBackendIsAccessible(baseDN2);
+    
+    // Remove the workflow and the backend and check that the base entry
+    // is no more accessible.
+    removeWorkflow(baseDN2, backendID2);
+    removeMemoryBackend(backendID2);
+    checkBackendIsNotAccessible(baseDN2);
+  }
+  
+  
+  /**
+   * This test checks the creation and utilization of network group
+   * in the route process.
+   */
+  @Test
+  public void useNetworkGroup() throws Exception
+  {
+    // Local settings
+    String backendID = "test";
+    String baseDN    = "o=test";
+
+    // Now move to the manual mode
+    setModeManual();
+    
+    // Create a route for o=test suffix in the default network group.
+    // Search on o=test should succeed.
+    WorkflowImpl workflowImpl = createWorkflow(baseDN, backendID);
+    InternalSearchOperation searchOperation =
+      doSearch(baseDN, SearchScope.BASE_OBJECT, ResultCode.SUCCESS);
+    
+    // Create a network group and store it in the client connection.
+    // As the network group is empty, all searches should fail with a
+    // no such object result code.
+    String networkGroupID = "useNetworkGroupID";
+    NetworkGroup networkGroup = new NetworkGroup(networkGroupID);
+    ClientConnection clientConnection = searchOperation.getClientConnection();
+    clientConnection.setNetworkGroup(networkGroup);
+    searchOperation.run();
+    assertEquals(searchOperation.getResultCode(), ResultCode.NO_SUCH_OBJECT);
+    
+    // Now register the o=test workflow and search again. The search
+    // should succeed.
+    networkGroup.registerWorkflow(workflowImpl);
+    searchOperation.run();
+    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+    
+    // Put back the default network group in the client conenction
+    // and check that searches are still working.
+    clientConnection.setNetworkGroup(NetworkGroup.getDefaultNetworkGroup());
+    searchOperation.run();
+    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+}

--
Gitblit v1.10.0