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