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

jdemendi
30.09.2007 403b6a83e7d68de2b5159c3421b8d91d704566bb
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' )
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>
opends/src/admin/defn/org/opends/server/admin/std/LocalBackendWorkflowElementConfiguration.xml
New file
@@ -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>
opends/src/admin/defn/org/opends/server/admin/std/NetworkGroupConfiguration.xml
New file
@@ -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>
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>
opends/src/admin/defn/org/opends/server/admin/std/WorkflowConfiguration.xml
New file
@@ -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>
opends/src/admin/defn/org/opends/server/admin/std/WorkflowElementConfiguration.xml
New file
@@ -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>
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
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);
    }
  }
  /**
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;
  }
}
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();
    }
  }
}
opends/src/server/org/opends/server/core/NetworkGroupConfigManager.java
New file
@@ -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();
  }
}
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;
  }
opends/src/server/org/opends/server/core/WorkflowConfigManager.java
New file
@@ -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);
  }
}
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>();
    }
  }
}
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 + "            "));
      }
    }
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);
  }
}
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>();
    }
  }
}
opends/src/server/org/opends/server/workflowelement/WorkflowElementConfigManager.java
New file
@@ -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);
    }
  }
}
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);
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"
          });
opends/tests/unit-tests-testng/src/server/org/opends/server/core/WorkflowConfigurationTest.java
New file
@@ -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);
  }
}