/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2007-2010 Sun Microsystems, Inc. */ package org.opends.server.core; import static org.opends.messages.CoreMessages.*; 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.core.networkgroups.NetworkGroup; 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; List<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 = de.getResultCode(); } messages.add(de.getMessageObject()); } } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean isConfigurationDeleteAcceptable( WorkflowCfg configuration, List<Message> unacceptableReasons) { boolean acceptable = true; WorkflowImpl existingWorkflow = workflows.get(configuration.dn()); if (existingWorkflow != null) { // check whether we can delete the workflow acceptable = checkReferenceCounter( existingWorkflow, unacceptableReasons); } return acceptable; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationDelete( WorkflowCfg configuration) { ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; List<Message> messages = new ArrayList<Message>(); // check first whether we can remove the workflow WorkflowImpl workflow = workflows.remove(configuration.dn()); if (workflow != null) { boolean acceptable = checkReferenceCounter(workflow, messages); if (acceptable) { // The workflow is not used anymore, we can remove it workflow.deregister(); workflow.finalizeWorkflow(); // Deregister the workflow with the internal network group NetworkGroup.getInternalNetworkGroup().deregisterWorkflow( workflow.getWorkflowId()); // Deregister the workflow with the admin network group NetworkGroup.getAdminNetworkGroup().deregisterWorkflow( workflow.getWorkflowId()); } else { resultCode = ResultCode.UNWILLING_TO_PERFORM; } } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * {@inheritDoc} */ public boolean isConfigurationChangeAcceptable( WorkflowCfg configuration, List<Message> unacceptableReasons) { // Get the existing workflow if it's already enabled. WorkflowImpl existingWorkflow = workflows.get(configuration.dn()); // Is this a request to disable the workflow? boolean acceptable = true; if (! configuration.isEnabled() && (existingWorkflow != null)) { // check whether we can disable the workflow acceptable = checkReferenceCounter( existingWorkflow, unacceptableReasons); } return acceptable; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationChange( WorkflowCfg configuration) { ResultCode resultCode = ResultCode.SUCCESS; boolean adminActionRequired = false; List<Message> messages = new ArrayList<Message>(); // Get the existing workflow if it's already enabled. WorkflowImpl existingWorkflow = workflows.get(configuration.dn()); // If the new configuration has the workflow disabled, then disable it if // it is enabled, or do nothing if it's already disabled. if (! configuration.isEnabled()) { if (existingWorkflow != null) { // check whether we can disable the workflow boolean acceptable = checkReferenceCounter(existingWorkflow, messages); if (acceptable) { // The workflow is not used anymore, we can remove it workflows.remove(configuration.dn()); existingWorkflow.deregister(); existingWorkflow.finalizeWorkflow(); // Deregister the workflow with the internal network group NetworkGroup.getInternalNetworkGroup().deregisterWorkflow( existingWorkflow.getWorkflowId()); // Deregister the workflow with the admin network group NetworkGroup.getAdminNetworkGroup().deregisterWorkflow( existingWorkflow.getWorkflowId()); } else { resultCode = ResultCode.UNWILLING_TO_PERFORM; } } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } // If the workflow is disabled then create and register it. if (existingWorkflow == null) { try { createAndRegisterWorkflow(configuration); } catch (DirectoryException de) { if (resultCode == ResultCode.SUCCESS) { resultCode = de.getResultCode(); } messages.add(de.getMessageObject()); } } else { // The workflow already exist, just notify the changes to the workflow existingWorkflow.updateConfig(configuration); } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } /** * 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.dn().getRDN().getAttributeValue(0).toString(); // Create the root workflow element to associate with the workflow String rootWorkflowElementID = workflowCfg.getWorkflowElement(); WorkflowElement<?> rootWorkflowElement = DirectoryServer.getWorkflowElement(rootWorkflowElementID); // Get the base DN targeted by the workflow DN baseDN = workflowCfg.getBaseDN(); // Create the workflow and register it with the server WorkflowImpl workflowImpl = new WorkflowImpl( workflowId, baseDN, rootWorkflowElementID, rootWorkflowElement); workflows.put(workflowCfg.dn(), workflowImpl); workflowImpl.register(); // Register the workflow with the internal network group NetworkGroup.getInternalNetworkGroup().registerWorkflow(workflowImpl); // Register the workflow with the admin network group NetworkGroup.getAdminNetworkGroup().registerWorkflow(workflowImpl); } /** * Checks whether a workflow is no more used so that we can delete * or disable it. * * @param workflow the workflow to check * @param messages a list of reasons that prevent the workflow to be * deleted or disabled * @return <code>true</code> when the workflow can be deleted or disabled */ private boolean checkReferenceCounter( WorkflowImpl workflow, List<Message> messages ) { boolean acceptable = true; int refCounter = workflow.getReferenceCounter(); if (refCounter != 0) { Message message = INFO_ERR_WORKFLOW_IN_USE.get( workflow.getWorkflowId(), workflow.getReferenceCounter()); messages.add(message); acceptable = false; } return acceptable; } }