/*
* 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;
}
}