/*
* 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.workflowelement;
import java.lang.reflect.InvocationTargetException;
import static org.opends.messages.ConfigMessages.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
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.DirectoryException;
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>
{
/**
* Creates a new instance of this workflow config manager.
*/
public WorkflowElementConfigManager()
{
}
/**
* 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())
{
loadAndRegisterWorkflowElement(workflowName);
}
}
/**
* Return the associated workflowElement is enabled if the
* workflow is enabled.
*
* @param workflowName workflow identifier
* @return workflowelement associated with the workflowName of null
* @throws org.opends.server.config.ConfigException Exception will reading
* the config
* @throws org.opends.server.types.InitializationException Exception while
* initializing the workflow element
*/
public WorkflowElement<?> loadAndRegisterWorkflowElement(String workflowName)
throws ConfigException, InitializationException {
ServerManagementContext managementContext =
ServerManagementContext.getInstance();
RootCfg rootConfiguration =
managementContext.getRootConfiguration();
WorkflowElementCfg workflowConfiguration =
rootConfiguration.getWorkflowElement(workflowName);
workflowConfiguration.addChangeListener(this);
if (workflowConfiguration.isEnabled())
{
return (loadAndRegisterWorkflowElement(workflowConfiguration));
}
return (null);
}
/**
* {@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
{
WorkflowElement<?> we = loadAndRegisterWorkflowElement(configuration);
// Notify observers who want to be notify when new workflow elements
// are created.
WorkflowElement.notifyStateUpdate(we);
}
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
// workflow 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 =
DirectoryServer.getWorkflowElement(
configuration.dn().getRDN().getAttributeValue(0).toString());
if (workflowElement != null)
{
// Notify to observers that the workflow element is now disabled
ObservableWorkflowElementState observableState =
workflowElement.getObservableState();
observableState.setWorkflowElementEnabled(false);
observableState.notifyObservers();
// Remove the workflow element
DirectoryServer.deregisterWorkflowElement(workflowElement);
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 =
DirectoryServer.getWorkflowElement(
configuration.dn().getRDN().getAttributeValue(0).toString());
// 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)
{
// Notify to observers that the workflow element is now disabled
ObservableWorkflowElementState observableState =
existingWorkflowElement.getObservableState();
observableState.setWorkflowElementEnabled(false);
observableState.notifyObservers();
// Remove the workflow element
DirectoryServer.deregisterWorkflowElement(existingWorkflowElement);
existingWorkflowElement.finalizeWorkflowElement();
}
return changeResult;
}
// If the workflow element is disabled then create it and register it.
if (existingWorkflowElement == null)
{
try
{
WorkflowElement<?> we = loadAndRegisterWorkflowElement(configuration);
// Notify observers who want to be notify when new workflow elements
// are created.
WorkflowElement.notifyStateUpdate(we);
}
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 workflowElementCfg the workflow element configuration
* @return WorkflowElement
* @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.
*/
WorkflowElement<?> 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
DirectoryServer.registerWorkflowElement(workflowElement);
}
catch (DirectoryException de)
{
throw new InitializationException(de.getMessageObject());
}
return (workflowElement);
}
/**
* 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;
// I cannot use the parameterized type WorflowElement<?>
// because it would break the line WorkflowElement.class below.
// Use SuppressWarning because we know the cast is safe.
@SuppressWarnings("unchecked")
Class<? extends WorkflowElement> workflowElementClass;
definition = WorkflowElementCfgDefn.getInstance();
propertyDefinition =
definition.getJavaClassPropertyDefinition();
workflowElementClass =
propertyDefinition.loadClass(className, WorkflowElement.class);
// Again, use SuppressWarning because we know the cast is safe
@SuppressWarnings("unchecked")
WorkflowElement<? extends WorkflowElementCfg> workflowElement =
(WorkflowElement<? extends WorkflowElementCfg>)
workflowElementClass.newInstance();
if (initialize)
{
Method method = workflowElement.getClass().getMethod(
"initializeWorkflowElement", configuration.configurationClass());
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)
{
Throwable t = e;
if (e instanceof InvocationTargetException && e.getCause() != null) {
t = e.getCause();
}
Message message =
ERR_CONFIG_WORKFLOW_ELEMENT_CANNOT_INITIALIZE.get(
className, String.valueOf(configuration.dn()),
t.getMessage());
throw new InitializationException(message);
}
}
}