/*
* 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 2008-2010 Sun Microsystems, Inc.
* Portions copyright 2013 ForgeRock AS.
*/
package org.opends.server.workflowelement;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.opends.server.admin.std.server.MonitorProviderCfg;
import org.opends.server.admin.std.server.WorkflowElementCfg;
import org.opends.server.api.MonitorProvider;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.Operation;
import org.opends.server.types.CanceledOperationException;
/**
* This class defines the super class for all the workflow elements. A workflow
* element is a task in a workflow. A workflow element can wrap a physical
* repository such as a local backend, a remote LDAP server or a local LDIF
* file. A workflow element can also be used to route operations. This is the
* 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 element.
*/
public abstract class WorkflowElement <T extends WorkflowElementCfg>
implements Observer
{
// Indicates whether the workflow element encapsulates a private local
// backend.
private boolean isPrivate = false;
// An information indicating the type of the current workflow element.
// This information is for debug and tooling purpose only.
private String workflowElementTypeInfo = "not defined";
// The workflow element identifier.
private String workflowElementID = null;
// The observable state of the workflow element.
private ObservableWorkflowElementState observableState =
new ObservableWorkflowElementState(this);
// The list of observers who want to be notified when a workflow element
// required by the observer is created. The key of the map is a string
// that identifies the newly created workflow element.
private static ConcurrentMap<String, List<Observer>>
newWorkflowElementNotificationList =
new ConcurrentHashMap<String, List<Observer>>();
// The observable status of the workflow element.
// The status contains the health indicator (aka saturation index)
// of the workflow element.
private ObservableWorkflowElementStatus observableStatus =
new ObservableWorkflowElementStatus(this);
// The statistics exported by the workflow element
private MonitorProvider<MonitorProviderCfg> statistics;
/**
* Provides the observable state of the workflow element.
* This method is intended to be called by the WorkflowElementConfigManager
* that wants to notify observers that the workflow element state has
* changed (in particular when a workflow element has been disabled).
*
* @return the observable state of the workflow element
*/
protected ObservableWorkflowElementState getObservableState()
{
return observableState;
}
/**
* Provides the observable status of the workflow element.
*
* @return the observable status of the workflow element.
*/
protected ObservableWorkflowElementStatus getObservableStatus()
{
return observableStatus;
}
/**
* Registers with a specific workflow element to be notified when the
* workflow element state has changed. This notification system is
* mainly used to be warned when a workflow element is enabled or
* disabled.
* <p>
* If the workflow element <code>we</code> is not <code>null</code>
* then the <code>observer</code> is registered with the list of objects
* to notify when <code>we</code> has changed.
* <p>
* If the workflow element <code>we</code> is <code>null</code> then
* the <code>observer</code> is registered with a static list of objects
* to notify when a workflow element named <code>weid</code> is created.
*
* @param we the workflow element. If <code>null</code> then observer
* is registered with a list of workflow element
* identifiers.
* @param weid the identifier of the workflow element. This parameter
* is useless when <code>we</code> is not <code>null</code>
* @param observer the observer to notify when the workflow element state
* has been modified
*/
public static void registereForStateUpdate(
WorkflowElement<?> we,
String weid,
Observer observer
)
{
// If the workflow element "we" exists then register the observer with "we"
// else register the observer with a static list of workflow element
// identifiers
if (we != null)
{
ObservableWorkflowElementState westate = we.getObservableState();
westate.addObserver(observer);
}
else
{
if (weid == null)
{
return;
}
List<Observer> observers = newWorkflowElementNotificationList.get(weid);
if (observers == null)
{
// create the list of observers
observers = new CopyOnWriteArrayList<Observer>();
observers.add(observer);
newWorkflowElementNotificationList.put(weid, observers);
}
else
{
// update the observer list
observers.add(observer);
}
}
}
/**
* Deregisters an observer that was registered with a specific workflow
* element.
* <p>
* If the workflow element <code>we</code> is not <code>null</code>
* then the <code>observer</code> is deregistered with the list of objects
* to notify when <code>we</code> has changed.
* <p>
* If the workflow element <code>we</code> is <code>null</code> then
* the <code>observer</code> is deregistered with a static list of objects
* to notify when a workflow element named <code>weid</code> is created.
*
* @param we the workflow element. If <code>null</code> then observer
* is deregistered with a list of workflow element
* identifiers.
* @param weid the identifier of the workflow element. This parameter
* is useless when <code>we</code> is not <code>null</code>
* @param observer the observer to deregister
*/
public static void deregistereForStateUpdate(
WorkflowElement<?> we,
String weid,
Observer observer
)
{
// If the workflow element "we" exists then deregister the observer
// with "we" else deregister the observer with a static list of
// workflow element identifiers
if (we != null)
{
ObservableWorkflowElementState westate = we.getObservableState();
westate.deleteObserver(observer);
}
if (weid != null)
{
List<Observer> observers = newWorkflowElementNotificationList.get(weid);
if (observers != null)
{
observers.remove(observer);
}
}
}
/**
* Notifies all the observers who want to be warn when a workflow element
* is created.
*
* @param workflowElement the newly created workflow element
*/
public static void notifyStateUpdate(
WorkflowElement<?> workflowElement)
{
// Go through the list of observers and notify them all
String weID = workflowElement.getWorkflowElementID();
List<Observer> observers = newWorkflowElementNotificationList.get(weID);
if (observers != null)
{
for (Observer observer: observers)
{
// The update might fail because an observer could have been
// terminated. In this case, just ignore the failure and remove
// the observer from the list of objects to notify.
try
{
observer.update(workflowElement.getObservableState(), null);
}
catch(Exception e)
{
observers.remove(observer);
}
}
}
}
/**
* 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.
* @param workflowElementTypeInfo an information to indicate the type of
* the current workflow element. For example
* "Backend" if the current workflow element
* is a local backend workflow element.
*/
public void initialize(
String workflowElementID,
String workflowElementTypeInfo)
{
this.workflowElementID = workflowElementID;
this.workflowElementTypeInfo = workflowElementTypeInfo;
this.statistics = this.createStatistics();
if (this.statistics != null) {
DirectoryServer.registerMonitorProvider(this.statistics);
}
}
/**
* {@inheritDoc}
*/
public void update(Observable o, Object arg)
{
// By default, do nothing when notification hits the workflow element.
}
/**
* Get the type of the workflow element. The type is a string information
* indicating which type is the current workflow element. This information
* is intended to be used by tools for trace and debug purpose.
*
* @return the type of the workflow element.
*/
public String getWorkflowElementTypeInfo()
{
return this.workflowElementTypeInfo;
}
/**
* Indicates whether the provided configuration is acceptable for
* this workflow element.
*
* @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(
T 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()
{
// Deregister the monitor provider.
if (statistics != null) {
DirectoryServer.deregisterMonitorProvider(statistics);
}
}
/**
* Executes the workflow element for an operation.
*
* @param operation the operation to execute
*
* @throws CanceledOperationException if this operation should be
* canceled
*/
public abstract void execute(Operation operation)
throws CanceledOperationException;
/**
* Indicates whether the workflow element encapsulates a private
* local backend.
*
* @return <code>true</code> if the workflow element encapsulates a private
* local backend, <code>false</code> otherwise
*/
public boolean isPrivate()
{
return isPrivate;
}
/**
* Specifies whether the workflow element encapsulates a private local
* backend.
*
* @param isPrivate Indicates whether the workflow element encapsulates a
* private local backend.
*/
protected void setPrivate(boolean isPrivate)
{
this.isPrivate = isPrivate;
}
/**
* Provides the workflow element identifier.
*
* @return the workflow element identifier
*/
public String getWorkflowElementID()
{
return workflowElementID;
}
/**
* Modifies the saturation index of the workflow element.
*
* @param newValue
* The new value of the saturation index of the workflow element.
*/
public void setSaturationIndex(int newValue)
{
observableStatus.setSaturationIndex(newValue);
}
/**
* Gets the saturation index of the workflow element.
*
* @return the value of the saturation index of the workflow element.
*/
public int getSaturationIndex()
{
return observableStatus.getSaturationIndex();
}
/**
* Registers an observer with the saturation index of the workflow
* element. The observer will be notified when the saturation index
* is updated.
*
* @param observer
* The observer to notify when the saturation index is modified.
*/
public void registerForSaturationIndexUpdate(Observer observer)
{
observableStatus.addObserver(observer);
}
/**
* Deregisters an observer with the saturation index of the workflow
* element.
*
* @param observer
* The observer to deregister.
*/
public void deregisterForSaturationIndexUpdate(Observer observer)
{
observableStatus.deleteObserver(observer);
}
/**
* Retrieves the list of child workflow elements, ie the
* WorkflowElements below this one in the topology tree.
*
* @return child workflow elements
*/
public abstract List<WorkflowElement<?>> getChildWorkflowElements();
/**
* Checks whether the tree of workflow elements below this one
* contains the provided workflow element.
*
* @param element The WorkflowElement we are looking for in the topology
* below this object.
* @return boolean
*/
public boolean hasChildWorkflowElement(WorkflowElement<?> element) {
if (this.getChildWorkflowElements().size() == 0) {
return (this.equals(element));
}
for (WorkflowElement<?> subElement : this.getChildWorkflowElements()) {
if (subElement.equals(element) ||
subElement.hasChildWorkflowElement(element)) {
return true;
}
}
return false;
}
/**
* Creates the statistics exposed by the workflow element. By default,
* workflow elements do not expose anything but specific implementations
* can override this method and provide their own stats.
* @return the statistics exposed by the workflow element.
*/
public MonitorProvider<MonitorProviderCfg> createStatistics() {
// by default, no stats are created;
// This method should be overriden if necessary
return null;
}
}