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