/*
* This file is part of MiToBo, the Microscope Image Analysis Toolbox.
*
* Copyright (C) 2010 - @YEAR@
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not,
<http://www.gnu.org/licenses/>.
*
* Fore more information on MiToBo, visit
*
* http://www.informatik.uni-halle.de/mitobo/
*
*/
package de.unihalle.informatik.Alida.workflows;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import javax.swing.event.EventListenerList;
import de.unihalle.informatik.Alida.annotations.Parameter.Direction;
import de.unihalle.informatik.Alida.dataconverter.ALDDataConverterManager;
import de.unihalle.informatik.Alida.dataio.ALDDataIOManagerXmlbeans;
import de.unihalle.informatik.Alida.exceptions.ALDDataConverterManagerException;
import de.unihalle.informatik.Alida.exceptions.ALDDataConverterException;
import de.unihalle.informatik.Alida.exceptions.ALDException;
import de.unihalle.informatik.Alida.exceptions.ALDOperatorException;
import de.unihalle.informatik.Alida.exceptions.ALDOperatorException.OperatorExceptionType;
import de.unihalle.informatik.Alida.exceptions.ALDProcessingDAGException;
import de.unihalle.informatik.Alida.exceptions.ALDWorkflowException;
import de.unihalle.informatik.Alida.exceptions.ALDWorkflowException.WorkflowExceptionType;
import de.unihalle.informatik.Alida.helpers.ALDFilePathManipulator;
import de.unihalle.informatik.Alida.operator.ALDOpParameterDescriptor;
import de.unihalle.informatik.Alida.operator.ALDOperator;
import de.unihalle.informatik.Alida.operator.ALDOperatorControllable;
import de.unihalle.informatik.Alida.operator.ALDOperatorLocation;
import de.unihalle.informatik.Alida.operator.events.ALDOperatorExecutionProgressEvent;
import de.unihalle.informatik.Alida.workflows.ALDWorkflowNode.ALDWorkflowNodeState;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEvent;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEvent.ALDWorkflowClassEventType;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowClassEventListener;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEvent.ALDWorkflowEventType;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEventListener;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowEventReporter;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowRunFailureInfo;
import de.unihalle.informatik.Alida.workflows.events.ALDWorkflowStorageInfo;
/** Class to model a workflow in Alida.
* A workflow consists of nodes each holding one {@link ALDOperator} and
* edges connecting output parameters of the source node with input parameters of the target node.
*
* @author posch
*
*/
public class ALDWorkflow extends ALDOperatorControllable
implements ALDWorkflowEventReporter {
/**
* workflow context types.
*/
public static enum ALDWorkflowContextType {
/**
* workflow runs in a oprunner context.
* Only one node is allowed an ALDControlEventType of type STEP_EVENT
* are passed to the operator in the enclosed node
*/
OP_RUNNER,
/**
* workflow runs with in grappa.
* ALDControlEventType of type STEP_EVENT are not passed to the operators in the
* enclosed nodes.
*/
GRAPPA,
/**
*
*/
OTHER
}
/**
* Standard extension for a xml file holding the external representation
* of a workflow.
*
*/
public final static String workflowXMLFileExtension = "awf";
/**
* For debugging information
*/
static int debug = 0;
/**
* Name of a workflow with out a proper name
*/
public final static String untitledWorkflowName = "Untitled";
/**
* Determines the context of this workflow
*/
private final ALDWorkflowContextType workflowContext;
/**
* If true, an input parameter is reset (currently set to null)
* if is is disconnected (by removing or redirecting an edge) from its source.
*/
boolean resetDisconnectedInput = true;
/**
* All nodes contained in this workflow
*/
private LinkedList<ALDWorkflowNode> nodes;
/**
* This node is used as an interior shadow (or substitute) for the
* node holding this workflow.
* This is necessary (or at least convenient) as the workflow does
* not know about the node it holds, and this node may not exist at all.
*/
private ALDWorkflowNode interiorShadowNode = null;
/**
* All edges of this workflow.
* An edge may connect {@linkplain wfNode} and nodes from {@linkplain #nodes}.
*/
private LinkedList<ALDWorkflowEdge> edges;
/**
* If true the user has requested an interrupt of the current execution of
* (part of) the workflow.
*/
@Deprecated
private boolean executionInterrupted;
/**
* Hash map of operator nodes of this workflow.
* The id is used to identify and access a node from outside of the workflow.
*/
private static HashMap<ALDWorkflowNodeID,ALDWorkflowNode> nodeIdToNode =
new HashMap<ALDWorkflowNodeID,ALDWorkflowNode>();
/**
*
*/
private static HashMap<ALDWorkflowNode, ALDWorkflowNodeID> nodeToNodeId =
new HashMap<ALDWorkflowNode, ALDWorkflowNodeID>();
/**
* Hash map of edges of this workflow.
* The id is used to identify and access an edge from outside of the workflow.
*/
private static HashMap<ALDWorkflowEdgeID,ALDWorkflowEdge> edgeIdToEdge =
new HashMap<ALDWorkflowEdgeID,ALDWorkflowEdge>();
/**
*
*/
private static HashMap<ALDWorkflowEdge,ALDWorkflowEdgeID> edgeToEdgeId =
new HashMap<ALDWorkflowEdge,ALDWorkflowEdgeID>();
/**
* Gives the nodeIds of last loading of this workflow from an external representation
* in the order as store externally.
* This order is the some as returned by {@link #getMappingIntegerToNodeId()}
*/
ALDWorkflowNodeID[] loadIndexToNodeId;
/**
* Gives the edgeIds of last loading of this workflow from an external representation
* in the order as store externally.
* This order is the some as returned by {@link #getMappingIntegerToEdgeId()}
*/
ALDWorkflowEdgeID[] loadIndexToEdgeId;
/**
* converter manager singleton instance
*/
private static ALDDataConverterManager converterManager = ALDDataConverterManager.getInstance();
/**
* List of control event listeners attached to this workflow.
*/
//protected transient volatile EventListenerList workflowEventlistenerList =
// new EventListenerList();
private transient HashMap<ALDWorkflowEventListener,ALDWorkflowEventManager> workflowEventMangerList =
new HashMap<ALDWorkflowEventListener, ALDWorkflowEventManager>();
/*
private transient HashMap<ALDWorkflowEventListener,Thread> workflowEventMangerThreadList =
new HashMap<ALDWorkflowEventListener, Thread>();
*/
/**
* List of control event listeners attached to this class used for loading workflows.
*/
protected static transient volatile EventListenerList workflowLoadEventlistenerList =
new EventListenerList();
// ==================================================
// Constructor section
/** Constructor for a workflow in a grappa context.
*
* @param name of the workflow
* @throws ALDOperatorException
*/
public ALDWorkflow( String name) throws ALDOperatorException {
this( name, ALDWorkflowContextType.GRAPPA);
}
/** Constructor
*
* @param name of the workflow
* @param context context this workflow is running in
* @throws ALDOperatorException
*/
public ALDWorkflow( String name, ALDWorkflowContextType context) throws ALDOperatorException {
super();
setName( name);
this.workflowContext = context;
if ( this.workflowContext == ALDWorkflowContextType.OP_RUNNER) {
this.notifyListenersRecursively = true;
} else {
this.notifyListenersRecursively = false;
}
this.nodes = new LinkedList<ALDWorkflowNode>();
this.edges = new LinkedList<ALDWorkflowEdge>();
this.executionInterrupted = false;
this.interiorShadowNode = new ALDWorkflowNode( null, this, true);
addNode( interiorShadowNode);
this.notifyListenersRecursively = true;
}
/** Constructor for an untitled workflow in a grappa context.
*
* @throws ALDOperatorException
*/
public ALDWorkflow() throws ALDOperatorException {
this( untitledWorkflowName, ALDWorkflowContextType.GRAPPA);
}
/** Constructor for an untitled workflow
*
* @param name of the workflow
* @param context context this workflow is running in
* @throws ALDOperatorException
*/
public ALDWorkflow( ALDWorkflowContextType context) throws ALDOperatorException {
this( untitledWorkflowName, context);
}
// ==================================================
// Methods
@Override
public boolean supportsStepWiseExecution() {
return true;
}
@Override
protected void operate() throws ALDOperatorException,
ALDProcessingDAGException {
try {
runWorkflow( true, false);
} catch (ALDWorkflowException e) {
throw new ALDOperatorException(OperatorExceptionType.OPERATE_FAILED,
"ALDWorkflow::operator failed\n" + e.getMessage());
}
}
@Override
public void setName( String name) {
super.setName( name);
// fire an event
String info = new String( name);
fireALDWorkflowEvent(new ALDWorkflowEvent(this, ALDWorkflowEventType.RENAME, info));
}
// ===================================================================
// Getter methods
// TODO some part of the getter methods may be needs to be synchronized
/**
* @return the resetDisconnectedInput
*/
public boolean isResetDisconnectedInput() {
return resetDisconnectedInput;
}
/**
* @param resetDisconnectedInput the resetDisconnectedInput to set
*/
void setResetDisconnectedInput(boolean resetDisconnectedInput) {
this.resetDisconnectedInput = resetDisconnectedInput;
}
/** Get the operator associated with <code>nodeId</code>.
*
* @param nodeId id of operator to return
* @return operator object or <code>null</code> if not existing
* @throws ALDWorkflowException if the node with <code>nodeId</code> does not exist.
*/
public ALDOperator getOperator( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
return getNode(nodeId).getOperator();
}
/** Set the operator associated with <code>nodeId</code>.
* The new operator instance needs to be of the same class as the current operator instance.
*
* @param nodeId id of operator to return
* @throws ALDWorkflowException if the new operator instance is of wrong type.
*/
public void setOperator( ALDWorkflowNodeID nodeId, ALDOperator newOp) throws ALDWorkflowException {
ALDOperator oldOp = getNode( nodeId).getOperator();
oldOp.removeOperatorExecutionProgressEventListener(this);
getNode(nodeId).setOperator( newOp);
newOp.addOperatorExecutionProgressEventListener(this);
if ( this.workflowContext == ALDWorkflowContextType.OP_RUNNER &&
oldOp instanceof ALDOperatorControllable) {
ALDOperatorControllable controlableOp = (ALDOperatorControllable)oldOp;
if ( debug >= 1 ) {
System.out.println("ALDWorkflow::setOperator remove old operator and register new operator as listener to the workflow");
}
this.removeALDConfigurationEventListener( controlableOp);
this.removeALDControlEventListener(controlableOp);
controlableOp = (ALDOperatorControllable)newOp;
this.addALDConfigurationEventListener( controlableOp);
this.addALDControlEventListener(controlableOp);
}
this.nodeParameterChanged(nodeId);
}
/** Get the state of node with <code>nodeId</code>.
*
* @param nodeId node id
* @return state of the node
* @throws ALDWorkflowException if the node with <code>nodeId</code> does not exist.
*/
public ALDWorkflowNodeState getState( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
return getNode(nodeId).getState();
}
/**
* @return the workflowContext
*/
public ALDWorkflowContextType getWorkflowContext() {
return workflowContext;
}
/**
* Returns the names of all required input parameters of the operator object associated with the node
* which are not linked and have a value of null.
*
* @param nodeId
* @return
* @throws ALDWorkflowException
*/
public Collection<String> getMissingRequiredInputs( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
return getNode(nodeId).getMissingRequiredInputs();
}
/** Get the workflow node associated with <code>nodeId</code>.
*
* @param nodeId id of workflow node to return
* @return node
* @throws ALDWorkflowException if the node with <code>nodeId</code> does not exist.
* @throws NullPointerException if <code>nodeId</code> is null.
*/
public ALDWorkflowNode getNode( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
if ( nodeId == null )
throw( new NullPointerException( "ALDWorkflow::getNode nodeId is null"));
if ( ALDWorkflow.mapNodeIdToNode(nodeId) == null) {
throw( new ALDWorkflowException( WorkflowExceptionType.NODE_DOESNOT_EXIST,
"NodeId <" + nodeId.toString() + "> " + nodeId));
}
return nodeIdToNode.get(nodeId);
}
/** Return a collection of all nodes of this workflow
*
* @return all nodes of this workflow
*/
public Collection<ALDWorkflowNode> getNodes() {
LinkedList<ALDWorkflowNode> nodeList = new LinkedList<ALDWorkflowNode>();
for ( ALDWorkflowNode node : this.nodes)
nodeList.add(node);
return nodeList;
}
/** Get the workflow edge associated with <code>edgeId</code>.
*
* @param edgeId
* @return
* @throws ALDWorkflowException if the edge with <code>edgeId</code> does not exist
*/
public ALDWorkflowEdge getEdge( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
if ( edgeId == null )
throw( new NullPointerException( "ALDWorkflow:getEdge edgeId is null"));
ALDWorkflowEdge edge = ALDWorkflow.mapeEdgeIdToEdge(edgeId);
if ( edge == null ) {
throw( new ALDWorkflowException( WorkflowExceptionType.EDGE_DOESNOT_EXIST,
"EdgeId <" + edgeId.toString() + "> " + edgeId));
}
return edge;
}
/** Get the workflow edge connecting given edges and parameters.
*
* @param sourceNodeId
* @param sourceParameterName
* @param targetNodeId
* @param targetParameterName
* @return the edge or null if the edge does not exists
*/
public ALDWorkflowEdge getEdge( ALDWorkflowNode sourceNode, String sourceParameterName,
ALDWorkflowNode targetNode, String targetParameterName) {
for ( ALDWorkflowEdge edge : edges ) {
if ( edge.getSourceNode() == sourceNode &&
edge.getTargetNode() == targetNode &&
edge.getSourceParameterName().equals( sourceParameterName) &&
edge.getTargetParameterName().equals(targetParameterName)) {
return edge;
}
}
return null;
}
/** Return a collection of all edges of this workflow
*
* @return all edges of this workflow
*/
public Collection<ALDWorkflowEdge> getEdges() {
LinkedList<ALDWorkflowEdge> edgeList = new LinkedList<ALDWorkflowEdge>();
for ( ALDWorkflowEdge edge : this.edges)
edgeList.add(edge);
return edgeList;
}
/**
* Return the NodeId of the source node of this edge
*
* @param edgeId
* @return
* @throws ALDWorkflowException
*/
public ALDWorkflowNodeID getSourceNodeId( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
ALDWorkflowEdge edge = this.getEdge( edgeId);
return ALDWorkflow.mapNodeToNodeId( edge.getSourceNode());
}
/**
* Return the source parameter name of this edge
*
* @param edgeId
* @return
* @throws ALDWorkflowException
*/
public String getSourceParameterName( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
ALDWorkflowEdge edge = this.getEdge( edgeId);
return edge.getSourceParameterName();
}
/**
* Return the NodeId of the target node of this edge
*
* @param edgeId
* @return
* @throws ALDWorkflowException
*/
public ALDWorkflowNodeID getTargetNodeId( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
ALDWorkflowEdge edge = this.getEdge( edgeId);
return ALDWorkflow.mapNodeToNodeId( edge.getTargetNode());
}
/**
* Return the target parameter name of this edge
*
* @param edgeId
* @return
* @throws ALDWorkflowException
*/
public String getTargetParameterName( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
ALDWorkflowEdge edge = this.getEdge( edgeId);
return edge.getTargetParameterName();
}
/**
* Return the current mapping of NodeIds to integer ids.
* Note: this mapping may change during manipulation of the workflow,
* i.e. the same NodeId may have different mappings if the workflow has changed
* is graph structure.
* The set of ids is a contiguous interval of non negative integers starting a zero.
*
* @return current mapping of NodeIds to integer ids
*/
public HashMap<ALDWorkflowNodeID, Integer> getMappingNodeIdToInteger() {
HashMap<ALDWorkflowNodeID, Integer> res = new HashMap<ALDWorkflowNodeID, Integer>( this.nodes.size());
Integer i = 0;
for ( ALDWorkflowNode node : this.nodes) {
res.put( ALDWorkflow.mapNodeToNodeId( node), i);
i++;
}
return res;
}
/**
* Return the current mapping of integer ids to NodeIds.
* Note: this mapping may change during manipulation of the workflow,
* i.e. the same NodeId may have different mappings if the workflow has changed
* is graph structure.
* The set of ids is a contiguous interval of non negative integers starting a zero.
*
* @return current mapping of integer ids to NodeIds
*/
public HashMap<Integer,ALDWorkflowNodeID> getMappingIntegerToNodeId() {
HashMap<Integer, ALDWorkflowNodeID> res = new HashMap< Integer, ALDWorkflowNodeID>( this.nodes.size());
Integer i = 0;
for ( ALDWorkflowNode node : this.nodes) {
res.put( i, ALDWorkflow.mapNodeToNodeId( node));
i++;
}
return res;
}
/**
* Return the current mapping of EdgeIds to integer ids.
* Note: this mapping may change during manipulation of the workflow,
* i.e. the same EdgeId may have different mappings if the workflow has changed
* is graph structure.
* The set of ids is a contiguous interval of non negative integers starting at zero.
*
* @return current mapping of EdgeIds to integer ids
*/
public HashMap<ALDWorkflowEdgeID, Integer> getMappingEdgeIdToInteger() {
HashMap<ALDWorkflowEdgeID, Integer> res = new HashMap<ALDWorkflowEdgeID, Integer>( this.edges.size());
Integer i = 0;
for ( ALDWorkflowEdge edge : this.edges) {
res.put( ALDWorkflow.mapEgdeToEdgeId( edge), i);
i++;
}
return res;
}
/**
* Return the current mapping of integer ids to EdgeIds.
* Note: this mapping may change during manipulation of the workflow,
* i.e. the same EdgeId may have different mappings if the workflow has changed
* is graph structure.
* The set of ids is a contiguous interval of non negative integers starting a zero.
*
* @return current mapping of integer ids to EdgeIds
*/
public HashMap<Integer,ALDWorkflowEdgeID> getMappingIntegerToEdgeId() {
HashMap<Integer, ALDWorkflowEdgeID> res = new HashMap< Integer, ALDWorkflowEdgeID>( this.edges.size());
Integer i = 0;
for ( ALDWorkflowEdge edge : this.edges) {
res.put( i, ALDWorkflow.mapEgdeToEdgeId( edge));
i++;
}
return res;
}
/** Return the nodeId of the idx-th node according to the order of the nodes
* in the external representation resulting from (last) loading this workflow.
* The order is identical as return from {@link #getNodeIds()} at the time of saving
* the workflow represented externally.
*
* @param idx
* @return
*/
public ALDWorkflowNodeID getNodeIdDuringLoading( Integer idx) {
if ( loadIndexToNodeId == null || (idx < 0 || idx >= loadIndexToNodeId.length ) )
return null;
else
return loadIndexToNodeId[idx];
}
/** Return the edgeId of the idx-th edge according to the order of the edges
* in the external representation resulting from (last) loading this workflow.
* The order is identical as return from {@link #getEdgeIds()} at the time of saving
* the workflow represented externally.
*
* @param idx
* @return
*/
public ALDWorkflowEdgeID getEdgeIdDuringLoading( Integer idx) {
if ( idx < 0 || idx >= loadIndexToEdgeId.length )
return null;
else
return loadIndexToEdgeId[idx];
}
// ===================================================================
// Methods which manipulate the workflow, e.g. add or remove nodes and edges, state of nodes
/** Add a new operator node to this workflow.
*
* @param opName Name of operator to add in the new node
* @throws ALDWorkflowException if the operator cannot be instantiated
*/
public synchronized ALDWorkflowNodeID createNode( String opName) throws ALDWorkflowException {
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::createNode for " + opName);
}
try {
return this.createNode((ALDOperator) (Class.forName(opName).newInstance()));
} catch (Exception e) {
throw( new ALDWorkflowException(WorkflowExceptionType.INSTANTIATION_ERROR,
"ALDWorkflow::createNode cannot instantiate <" + opName + ">"));
}
}
/** Add a new operator node to this workflow.
*
* @param opName Name of operator to add in the new node
* @throws ALDWorkflowException if the operator cannot be instantiated
*/
public synchronized ALDWorkflowNodeID createNode( ALDOperatorLocation location) throws ALDWorkflowException {
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::createNode for " + location.getName());
}
try {
return this.createNode(location.createOperator());
} catch (Exception e) {
throw( new ALDWorkflowException(WorkflowExceptionType.INSTANTIATION_ERROR,
"ALDWorkflow::createNode cannot instantiate <" + location.getName() + ">"));
}
}
/** Add a new operator to this workflow.
*
* @param op Operator object to add in the new node
* @throws ALDWorkflowException if on OP_RUNNER context an we get more then one node
*/
public synchronized ALDWorkflowNodeID createNode( ALDOperator op) throws ALDWorkflowException {
if ( op == null )
throw( new NullPointerException( "ALDWorkflow::createNode operator is null"));
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::createNode for " + op.getName() + " (" + op + ")");
}
// in the OP_RUNNER context we allow only one node
if ( this.workflowContext == ALDWorkflowContextType.OP_RUNNER &&
nodes.size() == 1 ) {
throw( new ALDWorkflowException( WorkflowExceptionType.ILLEGAL_GRAPH_STRUCTURE,
"In OP_RUNNER context only one node is allowed"));
}
ALDWorkflowNode node = new ALDWorkflowNode( this, op);
this.addNode( node);
op.addOperatorExecutionProgressEventListener( this);
if ( node.isConfigured()) {
// since the new node has no edges it is runnable iff configured
node.setState(ALDWorkflowNodeState.RUNNABLE);
}
if ( this.workflowContext == ALDWorkflowContextType.OP_RUNNER &&
op instanceof ALDOperatorControllable) {
ALDOperatorControllable controlableOp = (ALDOperatorControllable)op;
if ( debug >= 1 ) {
System.out.println("ALDWorkflow::createNode register operator as listener to the workflow");
}
this.addALDConfigurationEventListener( controlableOp);
this.addALDControlEventListener(controlableOp);
}
fireALDWorkflowEvent(
new ALDWorkflowEvent(this, ALDWorkflowEventType.ADD_NODE,ALDWorkflow.mapNodeToNodeId(node) ));
return ALDWorkflow.mapNodeToNodeId(node);
}
/**
* Copy this node.
*
* @param nodeId
* @param retainInEdges copy also all incoming edges
* @param retainParameterValues copy the values of all input parameters
* @return nodeId of the new copy
* @throws ALDWorkflowException
*/
public synchronized ALDWorkflowNodeID copyNode( ALDWorkflowNodeID nodeId,
boolean retainInEdges, boolean retainParameterValues) throws ALDWorkflowException {
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::copyNode copy node <" + nodeId + ">");
}
ALDWorkflowNode node = ALDWorkflow.mapNodeIdToNode(nodeId);
ALDOperator oldOp = node.getOperator();
if ( oldOp instanceof ALDWorkflow ) {
throw( new ALDWorkflowException( WorkflowExceptionType.INSTANTIATION_ERROR,
"Cannot copy workflows yet"));
}
ALDOperator newOp;
try {
newOp = oldOp.getClass().newInstance();
} catch (Exception e1) {
throw( new ALDWorkflowException( WorkflowExceptionType.INSTANTIATION_ERROR,
"Cannot instantiate new operator in copyNode"));
}
ALDWorkflowNodeID newNodeId = this.createNode(newOp);
if ( retainInEdges) {
for ( ALDWorkflowEdge edge : node.getInEdges() ) {
this.createEdge(ALDWorkflow.mapNodeToNodeId(edge.getSourceNode()),
edge.getSourceParameterName(), newNodeId, edge.getTargetParameterName());
}
}
if ( retainParameterValues ) {
for ( String name : newOp.getInInoutNames(null) ) {
try {
newOp.setParameter(name, oldOp.getParameter(name));
} catch (ALDOperatorException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"ALDWorkflow::copyNode cannot copy parameter values"));
}
}
}
fireALDWorkflowEvent(
new ALDWorkflowEvent(this, ALDWorkflowEventType.COPY_NODE,ALDWorkflow.mapNodeToNodeId(node) ));
return newNodeId;
}
/** Remove a node with all incoming and outgoing edges from the workflow.
*
* @param nodeId id of the node to be removed
* @throws ALDWorkflowException if the node with <code>nodeId</code> does not exist
*/
public synchronized void removeNode( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
if ( nodeId == null )
throw( new NullPointerException( "ALDWorkflow::removeNode nodeId is null"));
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::removeNode " + nodeId.id);
}
ALDWorkflowNode node = this.getNode(nodeId);
// first remember descendants
Collection<ALDWorkflowNode> oldDescendants;
oldDescendants = node.getDescendants();
// remove incoming edges
for ( ALDWorkflowEdge edge : node.getInEdges()) {
// note: we cannot use removeEdge
// remove this edge from the outEdges of the source
edge.getSourceNode().getOutEdges().remove(edge);
// remove the edge from the workflow
ALDWorkflowEdgeID edgeId = ALDWorkflow.mapEgdeToEdgeId( edge);
this.edges.remove( edge);
ALDWorkflow.edgeIdToEdge.remove( edgeId);
ALDWorkflow.edgeIdToEdge.remove( edge);
// fire an event
fireALDWorkflowEvent( new ALDWorkflowEvent( this, ALDWorkflowEventType.DELETE_EDGE, edgeId));
}
// remove outgoing edges
for ( ALDWorkflowEdge edge : node.getOutEdges()) {
// note: we cannot use removeEdge;
// remove this edge from the inEdges of the target
edge.getTargetNode().getInEdges().remove(edge);
if ( this.resetDisconnectedInput &&
// note: we might remove and edge because source
edge.getTargetNode().getOperator().hasParameter(edge.getTargetParameterName()) ) {
edge.getTargetNode().resetParameter(edge.getTargetParameterName());
}
// remove the edge from the workflow
ALDWorkflowEdgeID edgeId = ALDWorkflow.mapEgdeToEdgeId( edge);
this.edges.remove( edge);
ALDWorkflow.edgeIdToEdge.remove( edgeId);
ALDWorkflow.edgeIdToEdge.remove( edge);
// fire an event
fireALDWorkflowEvent( new ALDWorkflowEvent( this, ALDWorkflowEventType.DELETE_EDGE, edgeId));
}
// finally remove the node itself
this.nodes.remove( node);
ALDWorkflow.nodeIdToNode.remove(nodeId);
ALDWorkflow.nodeToNodeId.remove( node);
fireALDWorkflowEvent( new ALDWorkflowEvent(this, ALDWorkflowEventType.DELETE_NODE, nodeId));
// check states changes
updateStates( oldDescendants, true);
}
/** Notify the workflow that parameters of the operator object associated with node
* <code>nodeId</code> have changed.
*
* @param nodeId
* @throws ALDWorkflowException if the node with <code>nodeId</code> does not exist.
*/
public void nodeParameterChanged( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
if ( nodeId == null )
throw( new NullPointerException( "ALDWorkflow::nodeParameterChanged nodeId is null"));
ALDWorkflowNode node = this.getNode(nodeId);
ALDWorkflowNodeState oldState = node.getState();
if ( debug >= 1 ) {
System.out.println( "ALDWorkflow::nodeParameterChanged of node with id <" + nodeId.id +
"> old state " + oldState + " configured is now " + node.isConfigured());
}
// first remove edges involving parameters which have been removed
// in edges
ALDOperator op = node.getOperator();
LinkedList<ALDWorkflowEdge> edgesToRemove = new LinkedList<ALDWorkflowEdge>();
for ( ALDWorkflowEdge edge : node.getInEdges()) {
String pName = edge.getTargetParameterName();
if ( ! op.hasParameter(pName) ) {
edgesToRemove.add( edge);
}
}
for ( ALDWorkflowEdge edge : edgesToRemove )
this.removeEdge(ALDWorkflow.mapEgdeToEdgeId( edge));
// ... and now the out edges
edgesToRemove.clear();
for ( ALDWorkflowEdge edge : node.getOutEdges()) {
String pName = edge.getSourceParameterName();
if ( ! op.hasParameter(pName) ) {
edgesToRemove.add( edge);
}
}
for ( ALDWorkflowEdge edge : edgesToRemove )
this.removeEdge( ALDWorkflow.mapEgdeToEdgeId( edge));
// next update the states of downstream nodes
updateState( node);
// and fire event
LinkedList<ALDWorkflowNodeID> list = new LinkedList<ALDWorkflowNodeID>();
list.add( nodeId);
fireALDWorkflowEvent(new ALDWorkflowEvent(this, ALDWorkflowEventType.NODE_PARAMETER_CHANGE, list));
}
/** This method calls {@link createEdge} with the argument <code>allowDataConversion = false</code>.
*
*
* @param sourceNodeId
* @param sourceParameterName
* @param targetNodeId
* @param targetParameterName
* @throws ALDWorkflowException if edge is not allowed
*/
public synchronized ALDWorkflowEdgeID createEdge( ALDWorkflowNodeID sourceNodeId, String sourceParameterName,
ALDWorkflowNodeID targetNodeId, String targetParameterName) throws ALDWorkflowException {
return createEdge(sourceNodeId, sourceParameterName, targetNodeId, targetParameterName, false);
}
/** Add an edge representing the data flow between the parameter
* <code>sourceParameterName</code> in the operator represented by the node
* with <code>sourceNodeId</code>
* to the parameter <code>targetParameterName</code> in the operator represented by the node with
* <code>targetNodeId</code>.
* If a nodeId is null then this nodeId refers to this workflow,
* i.e. connects a parameter of the workflow.
* Otherwise source and target node need to be a node of this workflow or represent the workflow itself.
*
* The following restrictions apply for the edge to be created:
* <ol>
* <li>Direction of the parameters to connect
* <ol>
* <li> Source parameter
* <br>
* If the source node represents the workflow, the source parameter
* needs to be of direction <code>IN</code> or
* <code>INOUT</code>.
* Otherwise
* it needs to be of direction <code>OUT</code> or
* <code>INOUT</code>.</li>
* <li> Target parameter
* <br>
* If the target node represents the workflow, the target parameter
* needs to be of direction <code>OUT</code> or
* <code>INOUT</code>.
* Otherwise
* it needs to be of direction <code>IN</code> or
* <code>INOUT</code>.</li>
* </ol>
* </li>
* <li>
* The parameter in the target operator must not already be connected by an edge.</li>
* <li>
* The java types associated with both parameters must be compatible.
* I.e. the target parameter must be assignable by the source parameter.
* if <code>allowDataConversion</code> the parameters are also compatible if a data converter is available</li>
* <li>
* No cycles may be introduced into the workflow</li>
* </ol>
*
* @param sourceNodeId
* @param sourceParameterName
* @param targetNodeId
* @param targetParameterName
* @param allowDataConversion if true the edge is allowed in case of non assignable parameters if
* a data converter is available
* @return
* @throws ALDWorkflowException
*/
public synchronized ALDWorkflowEdgeID createEdge( ALDWorkflowNodeID sourceNodeId, String sourceParameterName,
ALDWorkflowNodeID targetNodeId, String targetParameterName,
Boolean allowDataConversion) throws ALDWorkflowException {
if ( sourceParameterName == null )
throw( new NullPointerException( "ALDWorkflow::createEdge sourceParameterName is null"));
if ( targetParameterName == null)
throw( new NullPointerException( "ALDWorkflow::createEdge targetParameterName is null"));
// get source and target node and check consistency of hierarchy
ALDWorkflowNode sourceNode;
if ( sourceNodeId == null) {
sourceNodeId = ALDWorkflow.mapNodeToNodeId(this.interiorShadowNode);
}
sourceNode = this.getNode(sourceNodeId);
if ( sourceNode.getOperator() == this) {
sourceNode = this.interiorShadowNode;
}
if ( sourceNode != this.interiorShadowNode &&
! this.nodes.contains(sourceNode)) {
throw( new ALDWorkflowException( WorkflowExceptionType.EDGE_CREATE_FAILED,
"Source node not contained in this workflow nor the workflow itself"));
}
ALDWorkflowNode targetNode;
if ( targetNodeId == null) {
targetNodeId = ALDWorkflow.mapNodeToNodeId(this.interiorShadowNode);
}
targetNode= this.getNode(targetNodeId);
if ( targetNode.getOperator() == this) {
targetNode = this.interiorShadowNode;
}
if ( targetNode != this.interiorShadowNode &&
! this.nodes.contains(targetNode)) {
throw( new ALDWorkflowException( WorkflowExceptionType.EDGE_CREATE_FAILED,
"Target node not contained in this workflow nor the workflow itself"));
}
if ( ! sourceNode.getOperator().getParameterNames().contains(sourceParameterName) ) {
throw( new ALDWorkflowException( WorkflowExceptionType.EDGE_CREATE_FAILED,
"Source node does not contained a parameter <" + sourceParameterName + ">"));
}
if ( ! targetNode.getOperator().getParameterNames().contains(targetParameterName) ) {
throw( new ALDWorkflowException( WorkflowExceptionType.EDGE_CREATE_FAILED,
"Target node does not contained a parameter <" + targetParameterName + ">"));
}
if ( debug >=1 ) {
System.out.println( "ALDWorkflow::createEdge <" +
sourceNode.getId() + "> (" + sourceParameterName + ") --> <" +
targetNode.getId() + "> (" + targetParameterName + ") ");
}
// now tentatively create and add the edge
ALDWorkflowEdge edge = new ALDWorkflowEdge( sourceNode, sourceParameterName,
targetNode, targetParameterName);
this.addEdge( edge);
try {
// checks restrictions
edgeAllowed( edge, allowDataConversion);
} catch (ALDWorkflowException ex) {
// not allowed
sourceNode.outEdges.remove(edge);
targetNode.inEdges.remove(edge);
this.edges.remove( edge);
ALDWorkflow.edgeIdToEdge.remove(ALDWorkflow.mapEgdeToEdgeId(edge));
ALDWorkflow.edgeToEdgeId.remove(edge);
throw ex;
}
fireALDWorkflowEvent(
new ALDWorkflowEvent( this, ALDWorkflowEventType.ADD_EDGE, ALDWorkflow.mapEgdeToEdgeId(edge)));
// check if states have changed
updateState( targetNode);
if ( debug >= 3) {
this.print();
}
return ALDWorkflow.mapEgdeToEdgeId(edge);
}
/** Remove an edge from the workflow.
*
* @param edgeId id of the edge to remove
* @throws ALDWorkflowException if edge is not allowed or the edge with <code>edgeId</code> does not exist.
*/
public synchronized void removeEdge( ALDWorkflowEdgeID edgeId) throws ALDWorkflowException {
if ( edgeId == null )
throw( new NullPointerException( "ALDWorkflow::removeEdge edgeId is null"));
if ( debug >= 1) {
System.out.println( "ALDWorkflow::removeEdge remove " + edgeId.id);
}
ALDWorkflowEdge edge = this.getEdge( edgeId);
ALDWorkflowNode sourceNode = edge.getSourceNode();
ALDWorkflowNode targetNode = edge.getTargetNode();
// remove this edge from the outEdges of the source and inEdges of the target
sourceNode.outEdges.remove(edge);
targetNode.inEdges.remove(edge);
if ( this.resetDisconnectedInput &&
// mind, we migth remove this edge as a parameter has been removed
targetNode.getOperator().hasParameter(edge.getTargetParameterName())) {
targetNode.resetParameter(edge.getTargetParameterName());
}
// remove the edge from the workflow
this.edges.remove( edge);
ALDWorkflow.edgeIdToEdge.remove(edgeId);
ALDWorkflow.edgeToEdgeId.remove(edge);
// fire an event
fireALDWorkflowEvent( new ALDWorkflowEvent(this, ALDWorkflowEventType.DELETE_EDGE, edgeId));
// check state changes
// the target node may now be unconfigured
if ( debug >= 2 ) {
System.out.println( " ALDWorkflow::removeEdge targetNode old state " +
targetNode.getState() + ", configured is now " + targetNode.isConfigured());
}
// check if states have changed
updateState( targetNode);
}
/** Call <code>redirectSource</code> with with the argument <code>allowDataConversion = false</code>.
* The same restrictions as for {@link createEdge} apply.
*
* @param edgeId
* @param newSourceNodeId
* @param newSourceParameterName
* @param allowDataConversion if true the edge is allowed in case of non assignable parameters if
* a data converter is available
* @throws ALDWorkflowException if edge is not allowed or the edge with edgeId does not exist
*/
public synchronized void redirectSource( ALDWorkflowEdgeID edgeId, ALDWorkflowNodeID newSourceNodeId,
String newSourceParameterName) throws ALDWorkflowException {
redirectSource( edgeId, newSourceNodeId, newSourceParameterName, false);
}
/** Redirect the source of an edge.
* The same restrictions as for {@link createEdge} apply.
*
* @param edgeId
* @param newSourceNodeId
* @param newSourceParameterName
* @param allowDataConversion if true the edge is allowed in case of non assignable parameters if
* a data converter is available
* @throws ALDWorkflowException if edge is not allowed or the edge with edgeId does not exist
*/
public synchronized void redirectSource( ALDWorkflowEdgeID edgeId, ALDWorkflowNodeID newSourceNodeId,
String newSourceParameterName, Boolean allowDataConversion) throws ALDWorkflowException {
if ( edgeId == null )
throw( new NullPointerException( "ALDWorkflow::redirectSource edgeId is null"));
if ( newSourceParameterName == null )
throw( new NullPointerException( "ALDWorkflow::redirectSource sourceParameterName is null"));
if ( debug >= 1) {
System.out.println( "ALDWorkflow::redirectSource edge " + edgeId.id +
" new source <" + newSourceNodeId.id + "> for parameter " + newSourceParameterName);
}
ALDWorkflowNode newSourceNode;
if ( newSourceNodeId == null) {
newSourceNodeId = ALDWorkflow.mapNodeToNodeId(this.interiorShadowNode);
}
newSourceNode = this.getNode(newSourceNodeId);
if ( newSourceNode.getOperator() == this) {
newSourceNode = this.interiorShadowNode;
}
if ( newSourceNode != this.interiorShadowNode &&
! this.nodes.contains(newSourceNode)) {
throw( new ALDWorkflowException( WorkflowExceptionType.SAVE_FAILED,
"Source node not contained in this workflow nor the workflow itself"));
}
// tentatively redirect the edge
ALDWorkflowEdge edge = this.getEdge( edgeId);
ALDWorkflowNode oldSourceNode = edge.getSourceNode();
String oldSourceParameterName = edge.getSourceParameterName();
edge.redirectSource( newSourceNode, newSourceParameterName);
try {
// checks restrictions
edgeAllowed( edge, allowDataConversion);
} catch (ALDWorkflowException ex) {
// not allowed
edge.redirectSource( oldSourceNode, oldSourceParameterName);
throw ex;
}
fireALDWorkflowEvent( new ALDWorkflowEvent(this, ALDWorkflowEventType.REDIRECT_EDGE_SOURCE, edgeId));
// check state changes
updateState( edge.getTargetNode());
}
/** Calls {@link redirectTarget} with the argument <code>allowDataConversion = false</code>
* The same restrictions as for {@link createEdge} apply.
*
* @param edgeId
* @param newTargetNodeId
* @param newTargetParameterName
* @throws ALDWorkflowException if edge is not allowed or the edge with edgeId does not exist
*/
public synchronized void redirectTarget( ALDWorkflowEdgeID edgeId, ALDWorkflowNodeID newTargetNodeId,
String newTargetParameterName) throws ALDWorkflowException {
redirectTarget(edgeId, newTargetNodeId, newTargetParameterName, false);
}
/** Redirect the target of an edge.
* The same restrictions as for {@link createEdge} apply.
*
* @param edgeId
* @param newTargetNodeId
* @param newTargetParameterName
* @param allowDataConversion if true the edge is allowed in case of non assignable parameters if
* a data converter is available
* @throws ALDWorkflowException if edge is not allowed or the edge with edgeId does not exist
*/
public synchronized void redirectTarget( ALDWorkflowEdgeID edgeId, ALDWorkflowNodeID newTargetNodeId,
String newTargetParameterName, Boolean allowDataConversion) throws ALDWorkflowException {
if ( edgeId == null )
throw( new NullPointerException( "ALDWorkflow::redirectTarget edgeId is null"));
if ( newTargetParameterName == null )
throw( new NullPointerException( "ALDWorkflow::redirectTarget targetParameterName is null"));
if ( debug >= 1) {
System.out.println( "ALDWorkflow::redirectTarget edge <" + edgeId.id +
"> new source >" + newTargetNodeId.id + "> for parameter " + newTargetParameterName);
}
ALDWorkflowNode newTargetNode;
if ( newTargetNodeId == null) {
newTargetNodeId = ALDWorkflow.mapNodeToNodeId(this.interiorShadowNode);
}
newTargetNode= this.getNode(newTargetNodeId);
if ( newTargetNode.getOperator() == this) {
newTargetNode = this.interiorShadowNode;
}
if ( newTargetNode != this.interiorShadowNode &&
! this.nodes.contains(newTargetNode)) {
throw( new ALDWorkflowException( WorkflowExceptionType.SAVE_FAILED,
"Target node not contained in this workflow nor the workflow itself"));
}
// tentatively redirect the edge
ALDWorkflowEdge edge = this.getEdge( edgeId);
ALDWorkflowNode oldTargetNode = edge.getTargetNode();
String oldTargetParameterName = edge.getTargetParameterName();
edge.redirectTarget( newTargetNode, newTargetParameterName);
try {
// checks restrictions
edgeAllowed( edge, allowDataConversion);
} catch (ALDWorkflowException ex) {
// not allowed
edge.redirectTarget( oldTargetNode, oldTargetParameterName);
throw ex;
}
fireALDWorkflowEvent( new ALDWorkflowEvent(this, ALDWorkflowEventType.REDIRECT_EDGE_TARGET, edgeId));
if ( this.resetDisconnectedInput ) {
oldTargetNode.resetParameter(oldTargetParameterName);
}
// check state changes
LinkedList<ALDWorkflowNode> nodeList = new LinkedList<ALDWorkflowNode>();
nodeList.add( oldTargetNode);
nodeList.add( newTargetNode);
updateStates(nodeList, true);
}
// ===========================================================================================================
// load and save support
/**
* Save this workflow to file.
* Does not fire an LOAD event.
*
* @param filename
* @throws ALDWorkflowException if file cannot be opened for writing or serialization fails
*/
public void save( String filename) throws ALDWorkflowException {
save(filename, false);
}
/**
* Save this workflow to file.
*
* @param filename
* @param doFireEvent if true an appropriate event is fired
* @throws ALDWorkflowException if file cannot be opened for writing or serialization fails
*/
public void save( String filename, boolean doFireEvent) throws ALDWorkflowException {
if ( debug >= 2) {
System.out.println( "ALDWorkflow.save to file " + filename);
}
this.save( new File( filename), doFireEvent);
}
/**
* Save this workflow to file.
* Does not fire an LOAD event.
*
* @param filename
* @throws ALDWorkflowException if file cannot be opened for writing or serialization fails
*/
public void save( File file) throws ALDWorkflowException {
save(file, false);
}
/**
* Save this workflow to file.
* @param doFireEvent if true an appropriate event is fired
* @param filename
*
* @throws ALDWorkflowException if file cannot be opened for writing or serialization fails
*/
public void save( File file, boolean doFireEvent) throws ALDWorkflowException {
if ( debug >= 2) {
System.out.println( "ALDWorkflow.save to file " + file);
}
// rename workflow
String filename = file.getAbsolutePath();
filename = ALDFilePathManipulator.removeExtension(filename);
String[] newFullname = filename.split("/");
String newName = newFullname[newFullname.length-1];
if ( this.getName().equals(untitledWorkflowName) || ! this.getName().equals( newName)) {
// currently untitled
this.setName( newName);
}
try {
ALDDataIOManagerXmlbeans.writeXml(file.getAbsolutePath(), this);
System.out.println("saved with xmlbeans");
} catch (Exception e1) {
throw( new ALDWorkflowException( WorkflowExceptionType.SAVE_FAILED,
"Serialization failed\n" +
e1.getMessage()));
}
// fire an event
if ( doFireEvent) {
ALDWorkflowStorageInfo info = new ALDWorkflowStorageInfo(file.getAbsolutePath(), this);
fireALDWorkflowEvent(new ALDWorkflowEvent(this, ALDWorkflowEventType.SAVE_WORKFLOW, info));
}
}
/**
* Load a workflow from file with <code>filename</code> and create a new workflow object.
* Does not fire an LOAD event.
*
* @param filename
* @return the workflow read from file
* @throws ALDWorkflowException if file cannot be opened for reading or deserialization fails
*/
public static ALDWorkflow load( String filename) throws ALDWorkflowException {
return load(filename, false);
}
/**
* Load a workflow from file with <code>filename</code> and create a new workflow object.
*
* @param filename
* @param doFireEvent if true an appropriate event is fired
* @return the workflow read from file
* @throws ALDWorkflowException if file cannot be opened for reading or deserialization fails
*/
public static ALDWorkflow load( String filename, boolean doFireEvent) throws ALDWorkflowException {
return load( new File( filename), doFireEvent);
}
/**
* Load a workflow from <code>File</code> and create a new workflow object.
* Does not fire an LOAD event.
* @param file
* @return the workflow read from file
* @throws ALDWorkflowException if file cannot be opened for reading or deserialization fails
*/
public static ALDWorkflow load( File file) throws ALDWorkflowException {
return load(file, false);
}
/**
* Load a workflow from <code>File</code> and create a new workflow object.
* @param file
* @param doFireEvent if true an appropriate event is fired
* @return
* @throws ALDWorkflowException if file cannot be opened for reading or deserialization fails
*/
public static ALDWorkflow load( File file, boolean doFireEvent) throws ALDWorkflowException {
ALDWorkflow newWorkflow = null;
try {
newWorkflow =
(ALDWorkflow) ALDDataIOManagerXmlbeans.readXml(file.getAbsolutePath(),
ALDWorkflow.class);
// System.out.println("loaded with xmlbeans");
// } catch (Exception e) {
// try {
//
// in = new FileInputStream( file.getAbsolutePath());
// try {
// System.out.println("trying xstream");
// XStream xstream = new XStream(new DomDriver());
// newWorkflow = (ALDWorkflow)(xstream.fromXML(in));
// System.out.println("loaded with xstreams");
// } catch (Exception e1) {
// throw( new ALDWorkflowException( WorkflowExceptionType.LOAD_FAILED,
// "Deserialization failed\n" +
// e1.getMessage()));
// }
//
} catch (Exception e1) {
// System.out.println( "exception in load");
throw( new ALDWorkflowException( WorkflowExceptionType.LOAD_FAILED,
"Can not open file <" + file.getAbsolutePath() +
"> for reading\n" +
e1.getMessage()));
}
//fire an event
if ( doFireEvent) {
ALDWorkflowStorageInfo info = new ALDWorkflowStorageInfo(file.getAbsolutePath(), newWorkflow);
ALDWorkflow.fireALDWorkflowClassEvent(new ALDWorkflowClassEvent(newWorkflow, ALDWorkflowClassEventType.LOAD_WORKFLOW, info));
}
return newWorkflow;
}
/**
* Init function for deserialized objects.
* <p>
* This function is called on an instance of this class being deserialized
* from file, prior to handing the instance over to the user. It takes care
* of a proper initialization of transient member variables as they are not
* initialized to the default values during deserialization.
* @return
*/
@Override
protected Object readResolve() {
super.readResolve();
for ( ALDWorkflowNode node : this.nodes) {
ALDWorkflowNodeID nodeId = null;
nodeId = new ALDWorkflowNodeID();
ALDWorkflow.nodeIdToNode.put( nodeId, node);
ALDWorkflow.nodeToNodeId.put( node, nodeId);
node.setState(ALDWorkflowNodeState.UNCONFIGURED);
}
ALDWorkflow.edgeIdToEdge = new HashMap<ALDWorkflowEdgeID, ALDWorkflowEdge>( this.edges.size());
ALDWorkflow.edgeToEdgeId = new HashMap<ALDWorkflowEdge, ALDWorkflowEdgeID>( this.edges.size());
for ( ALDWorkflowEdge edge : edges) {
ALDWorkflowEdgeID edgeId = null;
edgeId = new ALDWorkflowEdgeID();
ALDWorkflow.edgeIdToEdge.put( edgeId, edge);
ALDWorkflow.edgeToEdgeId.put( edge, edgeId);
String sourceParameterName = edge.getSourceParameterName();
String targetParameterName = edge.getTargetParameterName();
ALDOpParameterDescriptor sourceDescriptor = null;
ALDOpParameterDescriptor targetDescriptor = null;
try {
sourceDescriptor = edge.getSourceNode().getOperator().getParameterDescriptor(sourceParameterName);
} catch (ALDOperatorException e) {
System.err.println("ALDWorkflow::classesAllowed fatal error, cannot get source descriptor for <" +
sourceParameterName + ">");
}
try {
targetDescriptor = edge.getTargetNode().getOperator().getParameterDescriptor(targetParameterName);
} catch (ALDOperatorException e) {
System.err.println("ALDWorkflow::classesAllowed fatal error, cannot get target descriptor for <" +
targetParameterName + ">");
}
// reload data converter if necessary
if ( edge.isNeedConverter() ) {
try {
if ( debug >= 2) {
System.out.println("ALDWorkflow::classesAllowed trying to find a convert for <" +
sourceDescriptor.getMyclass() + "> --> < " + targetDescriptor.getMyclass());
}
// if we find a converter the register it in the edge
edge.setConverter( converterManager.
getProvider(sourceDescriptor.getMyclass(), sourceDescriptor.getField(),
targetDescriptor.getMyclass(), targetDescriptor.getField()));
} catch (Exception e) {
// TODO: need to mark edge as non valid
System.err.println("ALDWorkflow::readResolve fatal error, cannot get required data converter for <" +
targetParameterName + ">");
}
}
}
if ( debug >=2 ) {
System.out.println( "ALDWorkflow::readResolve workflow = " + this);
this.print();
}
this.workflowEventMangerList = new HashMap<ALDWorkflowEventListener, ALDWorkflowEventManager>();
//workflowEventMangerThreadList = new HashMap<ALDWorkflowEventListener, Thread>();
this.operatorStatus = OperatorControlStatus.OP_INIT;
controlEventlistenerList = new EventListenerList();
configurationEventlistenerList = new EventListenerList();
// update states of all nodes (without firing events)
try {
this.updateStates(this.nodes, false);
} catch (ALDWorkflowException e) {
return null;
}
return this;
}
// ===========================================================================================================
// Helper methods to check if an edge is allowed
/** Check if this edge is allowed. It is assumed that the edge to check was already
* added to the graph. An edge is not allowed, if
* <ol>
* <li>it does not connect an OUT or INOUT parameter with an IN or INOUT parameter,</li>
* <li>if the target IN or INOUT parameter now has more the one incident links, </li>
* <li>if the classes of connected parameters are not compatible, or </li>
* <li>if no cycle was introduced
* </ol>
* Classes are compatible either is they are assignable or a data converter exists and data conversion is allowed
*
* @param edge
* @param allowDataConversion is data conversion allowed for this edge
* @throws ALDWorkflowException if parameters are incompatible
*/
private void edgeAllowed(ALDWorkflowEdge edge, Boolean allowDataConversion) throws ALDWorkflowException {
ALDOpParameterDescriptor sourceDescriptor = null;
ALDOpParameterDescriptor targetDescriptor = null;
ALDWorkflowNode sourceNode = edge.getSourceNode();
String sourceParameterName = edge.getSourceParameterName();
ALDWorkflowNode targetNode = edge.getTargetNode();
String targetParameterName = edge.getTargetParameterName();
try {
sourceDescriptor = sourceNode.getOperator().getParameterDescriptor(sourceParameterName);
} catch (ALDOperatorException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.PARAMETER_ERROR,
"Source parameter with name <" + sourceParameterName + "> does not exist"));
}
try {
targetDescriptor = targetNode.getOperator().getParameterDescriptor(targetParameterName);
} catch (ALDOperatorException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.PARAMETER_ERROR,
"Target parameter with name <" + targetParameterName + "> does not exist"));
}
// directions
directionAllowd( sourceNode, sourceDescriptor, targetNode, targetDescriptor);
// now we have more then one link incident to the target, i.e. input parameter
if ( targetNode.getInEdgesForParameter( targetParameterName).size() > 1) {
throw( new ALDWorkflowException( WorkflowExceptionType.MULTIPLE_INCIDENT_LINKS,
"Target parameter with name <" + targetParameterName + ">"));
}
// classes
COMPATIBILITY comp = classesAllowed(sourceDescriptor, targetDescriptor, edge);
if ( comp == COMPATIBILITY.INCOMPATIBLE ) {
throw( new ALDWorkflowException( WorkflowExceptionType.INCOMPATIBLE_TYPES,
"Source Parameter <" + sourceParameterName.toString() + "> (" +
sourceDescriptor.getMyclass().getName() + ")\n" +
"Target Parameter <" + targetParameterName.toString() + "> (" +
targetDescriptor.getMyclass().getName() + ")\n"));
} else if ( comp == COMPATIBILITY.CONVERTIBLE && ! allowDataConversion ) {
throw( new ALDWorkflowException( WorkflowExceptionType.INCOMPATIBLE_TYPES_BUT_CONVERTIBLE,
"Source Parameter <" + sourceParameterName.toString() + "> (" +
sourceDescriptor.getMyclass().getName() + ")\n" +
"Target Parameter <" + targetParameterName.toString() + "> (" +
targetDescriptor.getMyclass().getName() + ")\n"));
}
// cycles
topSort();
}
/** Checks if the direction are allowed
* <ol>
* <li> Source parameter
* <br>
* If the source node represents the workflow, the source parameter
* needs to be of direction <code>IN</code> or
* <code>INOUT</code>.
* Otherwise
* it needs to be of direction <code>OUT</code> or
* <code>INOUT</code>.</li>
* <li> Target parameter
* <br>
* If the target node represents the workflow, the target parameter
* needs to be of direction <code>OUT</code> or
* <code>INOUT</code>.
* Otherwise
* it needs to be of direction <code>IN</code> or
* <code>INOUT</code>.</li>
* </ol>
* @param sourceNode source node
* @param sourceDescriptor Descriptor of the source parameter
* @param targetNode target node
* @param targetDescriptor Descriptor of the target parameter
*
* @return
* @throws ALDWorkflowException if direction is wrong
*/
private void directionAllowd( ALDWorkflowNode sourceNode,
ALDOpParameterDescriptor sourceDescriptor, ALDWorkflowNode targetNode, ALDOpParameterDescriptor targetDescriptor) throws ALDWorkflowException {
if ( sourceNode != this.interiorShadowNode) {
if ( sourceDescriptor.getDirection() != Direction.INOUT &&
sourceDescriptor.getDirection() != Direction.OUT ) {
throw( new ALDWorkflowException( WorkflowExceptionType.WRONG_SOURCE_PARAMETER_DIRECTION,
"Source parameter with name <" + sourceDescriptor.getName() + ">"));
}
} else {
if ( sourceDescriptor.getDirection() != Direction.INOUT &&
sourceDescriptor.getDirection() != Direction.IN ) {
throw( new ALDWorkflowException( WorkflowExceptionType.WRONG_SOURCE_PARAMETER_DIRECTION,
"Source parameter with name <" + sourceDescriptor.getName() + ">"));
}
}
if ( targetNode != this.interiorShadowNode) {
if ( targetDescriptor.getDirection() != Direction.INOUT &&
targetDescriptor.getDirection() != Direction.IN ) {
throw( new ALDWorkflowException( WorkflowExceptionType.WRONG_SOURCE_PARAMETER_DIRECTION,
"Target parameter with name <" + targetDescriptor.getName() + ">"));
}
} else {
if ( targetDescriptor.getDirection() != Direction.INOUT &&
targetDescriptor.getDirection() != Direction.OUT ) {
throw( new ALDWorkflowException( WorkflowExceptionType.WRONG_SOURCE_PARAMETER_DIRECTION,
"Target parameter with name <" + targetDescriptor.getName() + ">"));
}
}
}
private enum COMPATIBILITY {
ASSIGNABLE,
CONVERTIBLE,
INCOMPATIBLE
}
/** Check if class of source parameter is assignable to target parameter
* or may be converted by a converter provider or is incompatible
*
* @param sourceDescriptor
* @param targetDescriptor
* @param edge
* @return
*/
private COMPATIBILITY classesAllowed( ALDOpParameterDescriptor sourceDescriptor,
ALDOpParameterDescriptor targetDescriptor, ALDWorkflowEdge edge) {
if ( targetDescriptor.getMyclass().isAssignableFrom(sourceDescriptor.getMyclass()) ) {
return COMPATIBILITY.ASSIGNABLE;
} else {
try {
if ( debug >= 2) {
System.out.println("ALDWorkflow::classesAllowed trying to find a convert for <" +
sourceDescriptor.getMyclass() + "> --> < " + targetDescriptor.getMyclass());
}
// if we find a converter then register it in the edge
edge.setConverter(
converterManager.getProvider(sourceDescriptor.getMyclass(),
sourceDescriptor.getField(),
targetDescriptor.getMyclass(), targetDescriptor.getField()));
} catch (Exception e) {
// other wise classes are not compatible
return COMPATIBILITY.INCOMPATIBLE;
}
return COMPATIBILITY.CONVERTIBLE;
}
}
// ===========================================================================================================
// methods to run operators/nodes
/**
* Run the complete workflow. A node is only executed if it is not ready.
* Immediately returns after starting the execution.
*
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails
*/
public void runWorkflow() throws ALDWorkflowException {
runWorkflow( false, false);
}
/**
* Run the complete workflow. A node is only executed if it is not ready.
*
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails
*/
public void runWorkflow( boolean waitForCompletion) throws ALDWorkflowException {
runWorkflow(waitForCompletion, false);
}
/**
* Run the complete workflow. A node is only executed if it is not ready.
*
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @param stepRecursively if true OP_STEP events are passed to the child operators
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails
*/
public void runWorkflow( boolean waitForCompletion, boolean stepRecursively) throws ALDWorkflowException {
LinkedList<ALDWorkflowNode> nodes = this.topSort();
if ( debug >= 1) {
System.out.println( "ALDWorkflow::runFromNode " + ALDWorkflow.nodeIdsToString(nodes));
}
this.executeNonReadyNodes(nodes, waitForCompletion);
}
/**
* Run the given node and all its ancestors. A node is only executed if it is not ready.
* Immediately returns after starting the execution.
*
* @param nodeId
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runNode( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
runNode( nodeId, false, false);
}
/**
* Run the given node and all its ancestors. A node is only executed if it is not ready.
*
* @param nodeId
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runNode( ALDWorkflowNodeID nodeId, boolean waitForCompletion) throws ALDWorkflowException {
runNode(nodeId, waitForCompletion, false);
}
/**
* Run the given node and all its ancestors. A node is only executed if it is not ready.
*
* @param nodeId
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @param stepRecursively if true OP_STEP events are passed to the child operators
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runNode( ALDWorkflowNodeID nodeId, boolean waitForCompletion, boolean stepRecursively) throws ALDWorkflowException {
if ( nodeId == null )
throw( new NullPointerException( "ALDWorkflow::nodeParameterChanged nodeId is null"));
ALDWorkflowNode node = this.getNode(nodeId);
Set<ALDWorkflowNode> allNodesToRun = new HashSet<ALDWorkflowNode>();
allNodesToRun.add( node);
allNodesToRun.addAll( node.getAncestors());
if ( debug >= 1) {
System.out.println( "ALDWorkflow::runNode" + allNodesToRun);
}
this.executeNonReadyNodes( this.topSort(allNodesToRun), waitForCompletion);
}
/**
* Run the given node and all descendants of the given node.
* Additionally also all ancestors of these nodes are invoked.
* A node is only executed if it is not ready.
* Immediately returns after starting the execution.
*
* @param nodeId
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runFromNode( ALDWorkflowNodeID nodeId) throws ALDWorkflowException {
runFromNode( nodeId, false, false);
}
/**
* Run the given node and all descendants of the given node.
* Additionally also all ancestors of these nodes are invoked.
* A node is only executed if it is not ready.
*
* @param nodeId
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runFromNode( ALDWorkflowNodeID nodeId, boolean waitForCompletion) throws ALDWorkflowException {
runFromNode(nodeId, waitForCompletion, false);
}
/**
* Run the given node and all descendants of the given node.
* Additionally also all ancestors of these nodes are invoked.
* A node is only executed if it is not ready.
*
* @param nodeId
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @param stepRecursively if true OP_STEP events are passed to the child operators
* @throws ALDWorkflowException if not all nodes are at least runnable or the execution itself fails,
* or the node with nodeId does not exist
*/
public void runFromNode( ALDWorkflowNodeID nodeId, boolean waitForCompletion, boolean stepRecursively) throws ALDWorkflowException {
if ( nodeId == null )
throw( new NullPointerException( "ALDWorkflow::nodeParameterChanged nodeId is null"));
ALDWorkflowNode node = this.getNode(nodeId);
Set<ALDWorkflowNode> allNodesToRun = new HashSet<ALDWorkflowNode>();
allNodesToRun.add( node);
allNodesToRun.addAll(node.getDescendants());
Set<ALDWorkflowNode> ancestors = new HashSet<ALDWorkflowNode>();
for ( ALDWorkflowNode nextnode : allNodesToRun) {
ancestors.addAll( nextnode.getAncestors());
}
allNodesToRun.addAll(ancestors);
if ( debug >= 1) {
System.out.println( "ALDWorkflow::runFromNode " + ALDWorkflow.nodeIdsToString(allNodesToRun));
}
this.executeNonReadyNodes( this.topSort( allNodesToRun), waitForCompletion);
}
/**
* Ask execution of nodes to be terminated.
*/
@Deprecated
public void interruptExecution (){
executionInterrupted = true;
}
// ===========================================================================================================
// Helper methods to run an operator
/**
* Run all not ready nodes of the list in the given order.
*
* @param nodeIds
* @param waitForCompletion if true wait for completion, otherwise return immediately
* @throws ALDWorkflowException if one of the nodes is not at least runnable
* or the execution itself fails
*/
private void executeNonReadyNodes( final List<ALDWorkflowNode> nodes, boolean waitForCompletion)
throws ALDWorkflowException {
//TODO guarantee that this workflow object is executed not multiple times in parallel
boolean noContollableOperators = true;
for ( ALDWorkflowNode node : nodes ) {
if ( ! node.stateGreaterEqual(ALDWorkflowNodeState.RUNNABLE) ) {
throw( new ALDWorkflowException( WorkflowExceptionType.RUN_FAILED,
"The operator <" + node.getOperator().getName() +
"> is not at least runnable."));
}
if ( node.getClass().isAssignableFrom(ALDOperatorControllable.class)){
noContollableOperators = false;
}
}
// AWT
for ( ALDWorkflowEventManager manager : this.workflowEventMangerList.values()) {
Thread managerThread = new Thread(manager);
//this.workflowEventMangerThreadList.put( listener, managerThread);
managerThread.start();
}
ExecuteThread executeThread = new ExecuteThread( this, nodes);
if ( waitForCompletion && noContollableOperators) {
if ( debug >= 2) {
System.out.println( "ALDWorkflow::executeNonReadyNodes call executeThread.run()");
}
executeThread.run();
} else {
if ( debug >= 2) {
System.out.println( "ALDWorkflow::executeNonReadyNodes call executeThread.start()");
}
executeThread.start();
}
/*
ExecuteThread executeThread = new ExecuteThread( this, nodes);
executeThread.start();
if ( waitForCompletion) {
try {
executeThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
*/
}
/**
* This thread executes all non-ready nodes in the given order.
* Execution may be asked to be terminated by calling the workflows <code>interruptExecution</code>
* method.
* Currently termination will take effect before the next node is executed.
*
* @author posch
*
*/
class ExecuteThread extends Thread {
/**
* workflow to execute nodes for.
*/
private final ALDWorkflow workflow;
/**
* The nodes of the workflow to execute
*/
private final List<ALDWorkflowNode> nodes;
public ExecuteThread(ALDWorkflow workflow, List<ALDWorkflowNode> nodes) {
this.workflow = workflow;
this.nodes = nodes;
}
@Override
public void run() {
ALDWorkflowNode runningNode = null;
ALDWorkflowNodeState oldState = null;
try {
workflow.executionInterrupted = false;
for ( ALDWorkflowNode node : nodes ) {
// user request to interrupt execution?
if ( workflow.executionInterrupted == true) {
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.USER_INTERRUPT,
ALDWorkflow.mapNodeToNodeId( runningNode)));
return;
}
// check status from controllable
// TODO complete for other states
switch (workflow.operatorStatus) {
case OP_STOP:
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.USER_INTERRUPT,
ALDWorkflow.mapNodeToNodeId( runningNode)));
return;
default:
break;
}
if ( node.getState() != ALDWorkflowNodeState.READY ) {
runningNode = node;
// should be runnable, but to be sure
oldState = node.getState();
Set<ALDWorkflowNode> children = node.getChildren();
children.retainAll(nodes);
ALDWorkflow.executeNode( workflow, node, children.isEmpty());
}
}
} catch (ALDException e) {
ALDWorkflowRunFailureInfo info =
new ALDWorkflowRunFailureInfo(e,
ALDWorkflow.mapNodeToNodeId( runningNode) );
workflow.doStateChange( runningNode, oldState);
StringBuffer msg = new StringBuffer();
// put some general information to the header
msg.append("\nOperator name: " + runningNode.getOperator().getName() + "\n");
msg.append("\nEvent message: " + e.getMessage() + "\n");
msg.append("Exception class type: \n");
msg.append(info.getException().getClass() + "\n");
msg.append("\nException stack trace:\n");
StackTraceElement[] trace = info.getException().getStackTrace();
for (StackTraceElement elem : trace) {
msg.append(elem.toString());
msg.append("\n");
}
// workflow.fireALDWorkflowEvent(
// new ALDWorkflowEvent(workflow, ALDWorkflowEventType.RUN_FAILURE,
// "Running <" + runningNode.getOperator().getName() +
// "> failed\n" + e.getMessage(), info));
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.RUN_FAILURE,
new String( msg), info));
return;
} catch (Exception e) {
ALDWorkflowRunFailureInfo info =
new ALDWorkflowRunFailureInfo(e,
ALDWorkflow.mapNodeToNodeId( runningNode) );
workflow.doStateChange( runningNode, oldState);
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.RUN_FAILURE,
"Running <" + runningNode.getOperator().getName() +
"> failed\n" + e.getMessage(), info));
return;
}
//TODO are this all possible states we have?
if (workflow.operatorStatus == OperatorControlStatus.OP_STOP) {
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.USER_INTERRUPT,
ALDWorkflow.mapNodeToNodeId( runningNode)));
} else {
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(this, ALDWorkflowEventType.EXECUTION_FINISHED,
"execution finished"));
}
}
}
/**
* This actually runOps the operator in its own thread
* and takes care of firing events for state changes.
* The runOp method of the operator is invoked independently of the
* node's state. if <code>showResults</code> is true an corresponding event is fired.
*
* @param workflow
* @param node
* @param showResults
* @throws ALDWorkflowException
* @throws ALDProcessingDAGException
* @throws ALDOperatorException
* @throws ALDDataConverterManagerException
* @throws ALDDataConverterManagerException
*/
private static void executeNode( ALDWorkflow workflow, ALDWorkflowNode node, boolean showResults )
throws ALDWorkflowException, ALDOperatorException, ALDProcessingDAGException, ALDDataConverterManagerException, ALDDataConverterManagerException {
if ( debug >= 2 ) {
System.out.println(" ALDWorkflow::executeNonReadyNodes execute node <" + node.getId() + ">");
}
final ALDOperator op = node.getOperator();
//retrieve input parameters from links
try {
if ( debug >= 3 ) {
System.out.println( " retrieve input parameters ");
}
for ( ALDWorkflowEdge edge : node.getInEdges()) {
Object value = edge.getSourceNode().getOperator().getParameter(
edge.getSourceParameterName());
if ( edge.isNeedConverter() ) {
// we may need to convert
Class<?> targetClass =
op.getParameterDescriptor(edge.getTargetParameterName()).getMyclass();
ALDOperator sourceOp = edge.getSourceNode().getOperator();
// check if objects are assignable (as the value may extend the source parameter's class
if ( ! targetClass.isAssignableFrom(value.getClass())) {
try {
if ( debug >= 2 ) {
System.out.println( "ALDWorkflow::executeNode try to convert");
}
Field sourceField = sourceOp.getParameterDescriptor(
edge.getSourceParameterName()).getField();
Field targetField = op.getParameterDescriptor(
edge.getTargetParameterName()).getField();
value = converterManager.convert(edge.getConverter(),
value, sourceField, targetClass, targetField);
} catch (ALDDataConverterException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"Convert failed in <" + op.getName() + "> for parameter <" +
edge.getTargetParameterName()
));
}
} else {
if ( debug >= 2 ) {
System.out.println( "ALDWorkflow::executeNode converter non-null but no need to convert from <" +
value.getClass().getName() + "> to <" +
targetClass.getName() + ">");
}
}
} else {
if ( debug >= 2 ) {
System.out.println( "ALDWorkflow::executeNode converter null");
}
}
op.setParameter(edge.getTargetParameterName(), value);
if ( debug >= 3 ) {
System.out.println( " set parameter " +
edge.getTargetParameterName() + " from node <" + edge.getSourceNode().getId() +
"> " + edge.getSourceParameterName() + " value = " + value);
}
}
} catch (ALDOperatorException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"Cannot retrieve all input parameters for <" + op.getName() + ">"));
}
if ( workflow.workflowContext != ALDWorkflowContextType.OP_RUNNER &&
op instanceof ALDOperatorControllable) {
ALDOperatorControllable controlableOp = (ALDOperatorControllable)op;
workflow.addALDControlEventListener( controlableOp);
}
workflow.doStateChange( node, ALDWorkflowNodeState.RUNNING);
op.runOp();
if ( op.hasInOutParameters()) {
workflow.doStateChange( node, ALDWorkflowNodeState.RUNNABLE);
} else {
workflow.doStateChange( node, ALDWorkflowNodeState.READY);
}
if ( workflow.workflowContext != ALDWorkflowContextType.OP_RUNNER &&
op instanceof ALDOperatorControllable ) {
ALDOperatorControllable controlableOp = (ALDOperatorControllable)op;
workflow.removeALDControlEventListener( controlableOp);
}
// propagate outputs to the innderNode
if ( debug >= 3 ) {
System.out.println( " propagate output parameters ");
}
for ( ALDWorkflowEdge edge : node.getOutEdges()) {
if ( edge.getTargetNode().isInteriorShadowNode) {
Object value = edge.getSourceNode().getOperator().getParameter(
edge.getSourceParameterName());
edge.getTargetNode().getOperator().setParameter(edge.getTargetParameterName(), value);
if ( debug >= 3 ) {
System.out.println( " set parameter " +
edge.getTargetParameterName() + " from node <" + edge.getSourceNode().getId() +
"> " + edge.getSourceParameterName() + " value = " + value);
}
}
}
if ( showResults) {
workflow.fireALDWorkflowEvent(
new ALDWorkflowEvent(workflow, ALDWorkflowEventType.SHOW_RESULTS,
ALDWorkflow.mapNodeToNodeId( node)));
}
}
// ===================================================================
// Helper methods to handle states of nodes
/**
* Updates the states of <code>nodesToCheck</code> and their descendants.
* The nodes <code>nodesToCheck</code> may have changed their state according
* to change of the configuration of their operator or adding/removing of
* in coming edges.
* These nodes and all their descendants may have also changed their state
* due to the data flow dependencies.
* This methods changes the states of the nodes within the workflow and also
* fires an event notifying the listeners of the state changes.
*
* @param nodesToCheck
* @param doFireEvent if true an appropriate event is fired
* @throws ALDWorkflowException
*/
private void updateStates( Collection<ALDWorkflowNode> nodesToCheck, boolean doFireEvent) throws ALDWorkflowException {
if ( debug >= 1) {
System.out.println( "ALDWorkflow::updateStates for nodes " +
ALDWorkflow.nodeIdsToString(nodesToCheck));
}
// HashSet<ALDWorkflowNodeID> changeSet = new HashSet<ALDWorkflowNodeID>();
HashMap<ALDWorkflowNodeID, ALDWorkflowNode.ALDWorkflowNodeState>
changeSet = new HashMap<>();
// first check impact of local changes to state
try {
for ( ALDWorkflowNode node : this.topSort(nodesToCheck)) {
if ( this.checkLocalStateChange(node)) {
changeSet.put(
ALDWorkflow.mapNodeToNodeId(node), node.getState());
}
}
} catch (ALDWorkflowException e1) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"Graph is cyclic in updateStates"));
}
// now collect all nodes to consider, which are nodesToCheck and their descendants
HashSet<ALDWorkflowNode> nodesToConsider = new HashSet<ALDWorkflowNode>( nodesToCheck);
for ( ALDWorkflowNode node : nodesToCheck) {
nodesToConsider.addAll( node.getDescendants());
}
try {
for ( ALDWorkflowNode node : this.topSort(nodesToConsider) ) {
if ( this.checkDataflowStateChange( node) ) {
changeSet.put(
ALDWorkflow.mapNodeToNodeId(node), node.getState());
}
}
} catch (ALDWorkflowException e) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"Graph is cyclic in updateStates"));
}
if ( doFireEvent && ! changeSet.isEmpty() ) {
fireALDWorkflowEvent(new ALDWorkflowEvent(this, ALDWorkflowEventType.NODE_STATE_CHANGE, changeSet));
}
}
/**
* Convenience method which just invokes {@link #updateStates(Collection, boolean)} with the single
* <code>node</code>.
*
* @param node
* @throws ALDWorkflowException
*/
private void updateState(ALDWorkflowNode node) throws ALDWorkflowException {
LinkedList <ALDWorkflowNode> nodeList = new LinkedList<ALDWorkflowNode>();
nodeList.add( node);
updateStates( nodeList, true);
}
/**
* Updates the state of this node according to its local configuration.
* Does not take state changes depending on the states of its parents into account
* <p>
* Note: this method does not fire an event to notify listeners.
*
* @param node
* @return true is state of the node has been changed
* @throws ALDWorkflowException
*/
private boolean checkLocalStateChange( ALDWorkflowNode node) throws ALDWorkflowException {
if ( debug >= 2 ) {
System.out.println( " ALDWorkflow::checkLocalStateChange of node with id <" + node.getId() +
"> old state " + node.getState() + " configured is now " + node.isConfigured());
}
boolean stateChanged= false;
ALDWorkflowNodeState oldState = node.getState();
switch (node.getState()) {
case UNCONFIGURED:
if ( node.isConfigured()) {
node.setState(ALDWorkflowNodeState.CONFIGURED);
stateChanged = true;
}
break;
case CONFIGURED:
case RUNNABLE:
if ( ! node.isConfigured()) {
node.setState(ALDWorkflowNodeState.UNCONFIGURED);
stateChanged = true;
}
break;
case READY:
if ( node.isConfigured()) {
node.setState(ALDWorkflowNodeState.RUNNABLE);
} else {
node.setState(ALDWorkflowNodeState.UNCONFIGURED);
}
stateChanged = true;
break;
default:
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"ALDWorkflow::checkLocalStateChange fatal error: state shoud not be running for " +
node.getId()));
}
if ( stateChanged) {
if ( debug >= 2) {
System.out.println( " ALDWorkflow::checkLocalStateChange changed state for node <" + node.getId() +
"> from " + oldState + " to " + node.getState());
}
}
return stateChanged;
}
/**
* Updates the state of this node according to the states of its parents.
* Updates considered are only due to data flow,
* not due to changes in operator configuration or adding/removing of in coming edges.
* <p>
* Note: this method does not fire an event to notify listeners.
*
* @param node
* @return true if state was changed
* @throws ALDWorkflowException
*/
private boolean checkDataflowStateChange(ALDWorkflowNode node) throws ALDWorkflowException {
boolean stateChanged = false;
ALDWorkflowNodeState oldState = node.getState();
if ( debug >= 2 ) {
System.out.println(" ALDWorkflow::checkDataflowStateChange for node with " + node.getId() +
" with current state " + node.getState());
}
switch ( node.getState()) {
case UNCONFIGURED:
// remains un-configured
break;
case CONFIGURED:
if ( checkParentStates( node, ALDWorkflowNodeState.RUNNABLE) ) {
node.setState( ALDWorkflowNodeState.RUNNABLE);
stateChanged = true;
}
break;
case RUNNABLE:
if ( checkParentStates( node, ALDWorkflowNodeState.RUNNABLE) ) {
//??node.setState( ALDWorkflowNodeState.RUNNABLE);
} else {
node.setState( ALDWorkflowNodeState.CONFIGURED);
stateChanged = true;
}
break;
case RUNNING:
System.err.println( "ALDWorkflow::updateState fatal error: state shoud not be running for " +
node);
break;
case READY:
if ( checkParentStates( node, ALDWorkflowNodeState.READY) ) {
// nothing to change
} else if ( checkParentStates( node, ALDWorkflowNodeState.RUNNABLE) ){
node.setState( ALDWorkflowNodeState.RUNNABLE);
stateChanged = true;
} else {
node.setState( ALDWorkflowNodeState.CONFIGURED);
stateChanged = true;
}
}
if ( stateChanged) {
if ( debug >= 2) {
System.out.println( " ALDWorkflow::checkDataflowStateChange changed state for node <" + node.getId() +
"> from " + oldState + " to " + node.getState());
}
}
return stateChanged;
}
/**
* Check if all parents of the given node have a state at least <code>requiredState</code>.
*
* @param node
* @param requiredState
* @return true if all parents have a state at least <code>requiredState</code>
* @throws ALDWorkflowException
*/
private boolean checkParentStates(ALDWorkflowNode node,
ALDWorkflowNodeState requiredState) {
for ( ALDWorkflowNode parent : node.getParents()) {
if ( parent.getState().compareTo( requiredState) < 0)
return false;
}
return true;
}
/**
* Set the new state in the node and fire corresponding event.
*
* @param node
* @param newState
*/
private void doStateChange( ALDWorkflowNode node, ALDWorkflowNode.ALDWorkflowNodeState newState) {
if ( debug >= 2) {
System.out.println( " ALDWorkflow::doStateChange for node <" + node.getId() +
"> from " + node.getState() + " to " + newState);
}
node.setState(newState);
// HashSet<ALDWorkflowNodeID> changeSet = new HashSet<ALDWorkflowNodeID>();
HashMap<ALDWorkflowNodeID, ALDWorkflowNodeState> changeSet =
new HashMap<>();
changeSet.put( ALDWorkflow.mapNodeToNodeId(node), newState);
fireALDWorkflowEvent(new ALDWorkflowEvent(this, ALDWorkflowEventType.NODE_STATE_CHANGE, changeSet));
}
// ===================================================================
// topological sorting
/** Topologically sorts the nodes given in <code>nodes</code>.
* For sorting the subgraph of the complete workflow induced by <code>nodes</code> is
* considered.
* If the subgraph is cyclic an exception is raised.
*
* @param nodes of the subgraph to be sorted
* @return topologically sorted list of <code>nodes</code>
* @throws ALDWorkflowException if the subgraph is cyclicor <code>nodes</code> is null.
*/
LinkedList<ALDWorkflowNode> topSort( Collection<ALDWorkflowNode> nodes) throws ALDWorkflowException {
if ( nodes == null)
throw( new NullPointerException( "ALDWorkflow::topSort nodes is null"));
if ( debug >= 2) {
System.out.println( " ALDWorkflow::topSort sort the nodes: " + ALDWorkflow.nodeIdsToString(nodes));
}
HashMap<ALDWorkflowNode, Set<ALDWorkflowNode>> sourceNodeMap = new HashMap<ALDWorkflowNode, Set<ALDWorkflowNode>>();
for ( ALDWorkflowNode node : nodes) {
Set<ALDWorkflowNode> sourceNodes = node.getParents();
sourceNodes.retainAll(nodes);
sourceNodeMap.put( node, sourceNodes);
}
if ( debug >= 3 ) {
for ( ALDWorkflowNode node : sourceNodeMap.keySet()) {
System.out.println(" " + node.getId() + ": ");
for ( ALDWorkflowNode source : sourceNodeMap.get(node) ) {
System.out.println( " " + source.getId());
}
}
}
LinkedList<ALDWorkflowNode> sortedNodes = new LinkedList<ALDWorkflowNode>();
while ( ! sourceNodeMap.isEmpty()) {
ALDWorkflowNode nextNode = getNodeWithoutSources(sourceNodeMap);
if ( nextNode == null ) {
throw( new ALDWorkflowException( WorkflowExceptionType.CYCLIC,
"workflow is cyclic"));
}
sortedNodes.add(nextNode);
sourceNodeMap.remove( nextNode);
for ( Set<ALDWorkflowNode> sourceNodes : sourceNodeMap.values()) {
sourceNodes.remove(nextNode);
}
if ( debug >= 3 ) {
for ( ALDWorkflowNode node : sourceNodeMap.keySet()) {
System.out.println(" " + node.getId() + ": ");
for ( ALDWorkflowNode source : sourceNodeMap.get(node) ) {
System.out.println( " " + source.getId());
}
}
}
}
return sortedNodes;
}
/** Topologically sorts all nodes of the workflow.
*
* @return topologically sorted list of all nodes in the workflow
* @throws ALDWorkflowException the workflow is cyclic
*/
LinkedList<ALDWorkflowNode> topSort() throws ALDWorkflowException {
if ( debug >= 2) {
System.out.println( " ALDWorkflow::topSort complete workflow ");
}
return topSort( this.nodes);
}
/**
* Returns one node from the <code>sourceNodeMap</code> with zero
* source nodes, if any. If no such node exists, return null.
*
* @param sourceNodeMap
* @return
*/
private ALDWorkflowNode getNodeWithoutSources(
HashMap<ALDWorkflowNode, Set<ALDWorkflowNode>> sourceNodeMap) {
for ( ALDWorkflowNode node : sourceNodeMap.keySet()) {
if ( sourceNodeMap.get(node).size() == 0) {
return node;
}
}
return null;
}
// ===================================================================
// mapping between IDs and nodes/edges
/**
* Map a nodeId to its node.
*
* @param nodeId the node or null if a node with the given nodeId does not exist
* @return
*/
protected static ALDWorkflowNode mapNodeIdToNode(ALDWorkflowNodeID nodeId) {
return ALDWorkflow.nodeIdToNode.get(nodeId);
}
/**
* Map a node to its NodeId
*
* @param node the NodeId or null (which should not happen)
* @return
*/
protected static ALDWorkflowNodeID mapNodeToNodeId( ALDWorkflowNode node) {
return ALDWorkflow.nodeToNodeId.get( node);
}
/**
* Add this node to the workflow, create a NodeId and update mapping.
* @param node
*/
private void addNode( ALDWorkflowNode node) {
if ( node != this.interiorShadowNode ) {
this.nodes.add( node);
}
ALDWorkflowNodeID nodeId= new ALDWorkflowNodeID();
ALDWorkflow.nodeIdToNode.put( nodeId, node);
ALDWorkflow.nodeToNodeId.put( node, nodeId);
}
private void addEdge( ALDWorkflowEdge edge) {
this.edges.add(edge);
ALDWorkflowEdgeID edgeId = new ALDWorkflowEdgeID();
ALDWorkflow.edgeIdToEdge.put( edgeId, edge);
ALDWorkflow.edgeToEdgeId.put( edge, edgeId);
}
/**
* Map an edgeId to its edge.
*
* @param edgeId the edge or null if no edge with the given edgeId exists
* @return
*/
protected static ALDWorkflowEdge mapeEdgeIdToEdge( ALDWorkflowEdgeID edgeId) {
return ALDWorkflow.edgeIdToEdge.get( edgeId);
}
protected static ALDWorkflowEdgeID mapEgdeToEdgeId( ALDWorkflowEdge edge) {
return ALDWorkflow.edgeToEdgeId.get( edge);
}
public ALDWorkflowNodeID getShadowNodeId() {
return ALDWorkflow.mapNodeToNodeId(this.interiorShadowNode);
}
// ===================================================================
// Listener section
@Override
public void addALDWorkflowEventListener(ALDWorkflowEventListener listener) {
if ( listener == null)
throw( new NullPointerException( "ALDWorkflow::addALDWorkflowEventListener listener is null"));
ALDWorkflowEventManager manager = new ALDWorkflowEventManager(listener);
this.workflowEventMangerList.put( listener, manager);
// do not create and start event manager thread currently, AWT problem in GUI
/*
Thread managerThread = new Thread(manager);
this.workflowEventMangerThreadList.put( listener, managerThread);
managerThread.start();
*/
}
@Override
public void removeALDWorkflowEventListener(ALDWorkflowEventListener listener) {
ALDWorkflowEventManager manager = this.workflowEventMangerList.get( listener);
if ( manager != null) {
manager.setTermiate(true);
this.workflowEventMangerList.remove(listener);
//this.workflowEventMangerThreadList.remove(listener);
}
}
/**
* Returns the event queue associated with the <code>listener</code> or null
* it the listener is not registered.
*
* @param listener
* @return
*/
public BlockingDeque<ALDWorkflowEvent> getEventQueue( ALDWorkflowEventListener listener) {
ALDWorkflowEventManager manager = this.workflowEventMangerList.get( listener);
if ( manager != null) {
return manager.eventQueue;
} else {
return null;
}
}
@Override
public void fireALDWorkflowEvent(final ALDWorkflowEvent event) {
if ( event == null)
throw( new NullPointerException( "ALDWorkflow::fireALDWorkflowEvent event is null"));
if ( debug >= 1) {
System.out.println( "ALDWorkflow::fireALDWorkflowEvent fire " + event.getEventType() +
" " + event.getEventMessage() + " info: " + event.getId());
}
for ( ALDWorkflowEventManager manager : this.workflowEventMangerList.values()) {
if ( debug >= 2) {
System.out.println( " ALDWorkflow::fireALDWorkflowEvent fire event for manager " +
manager);
}
try {
manager.eventQueue.put(event.createCopy());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* Add a listener for events thrown by the class ALDWorkflow (not an instance).
*
* @param listener
*/
public static void addALDWorkflowClassEventListener(ALDWorkflowClassEventListener listener) {
if ( listener == null)
throw( new NullPointerException( "ALDWorkflow::addALDWorkflowClassEventListener listener is null"));
workflowLoadEventlistenerList.add(ALDWorkflowClassEventListener.class,listener);
}
/**
* Remove a listener for events thrown by the class ALDWorkflow (not an instance).
* @param listener
*/
public static void removeALDWorkflowClassEventListener(ALDWorkflowEventListener listener) {
workflowLoadEventlistenerList.remove(ALDWorkflowEventListener.class,
listener);
}
/** Fire an event by the class ALDWorkflow.
*
* @param aldWorkflowClassEvent
*/
public static void fireALDWorkflowClassEvent(final ALDWorkflowClassEvent aldWorkflowClassEvent) {
if ( aldWorkflowClassEvent == null)
throw( new NullPointerException( "ALDWorkflow::fireALDWorkflowClassEvent event is null"));
// Guaranteed to return a non-null array
final Object[] listeners = workflowLoadEventlistenerList.getListenerList();
Thread eventThread;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ALDWorkflowClassEventListener.class) {
// Lazily create the event for each listener and invoke the listener in a new thread
final ALDWorkflowClassEventListener listener = (ALDWorkflowClassEventListener) listeners[i+1];
eventThread = new Thread(){
@Override
public void run() {
listener.handleALDWorkflowClassEvent(
new ALDWorkflowClassEvent (this,
aldWorkflowClassEvent.getEventType(), aldWorkflowClassEvent.getEventMessage(), aldWorkflowClassEvent.getId()));
}};
eventThread.start();
}
}
}
// ========================================================
// hierarchical workflows
// /**
// * Add a new parameter to this workflow.
// * <br>
// * Note: adding a new parameter will shadow an already existing parameter.
// *
// * @param descriptor
// */
// public void addParameter( ALDOpParameterDescriptor descriptor) {
// if ( debug >= 0 ) {
// System.out.println( "ALDWorkflow::addParameter with name <" + descriptor.getName() + ">");
// }
// ALDOpParameterDescriptor wfDescriptor = new ALDOpParameterDescriptor(descriptor);
//
// super.addParameter(wfDescriptor);
//// Field field;
//// try {
//// field = ALDOperator.class.getDeclaredField( "parameterDescriptorsAll");
//// field.setAccessible(true);
//// @SuppressWarnings("unchecked")
//// Hashtable<String,ALDOpParameterDescriptor> parameterDescriptorsAll =
//// (Hashtable<String, ALDOpParameterDescriptor>) field.get( this);
//// parameterDescriptorsAll.put( descriptor.getName(), wfDescriptor);
//// } catch (Exception e) {
//// throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
//// "ALDWorkflow::addParameter cannot access parameterDescriptorsAll"));
//// }
// }
// /**
// * Remove a parameter from this workflow.
// *
// * @param parameterName
// * @throws ALDWorkflowException if this parameter does not exist or in case of fatal error
// */
// protected void removeParameter( String parameterName) throws ALDWorkflowException{
// ALDOpParameterDescriptor descriptor = null;
// try {
// descriptor = getParameterDescriptor(parameterName);
// } catch (ALDOperatorException e1) {
// throw( new ALDWorkflowException( WorkflowExceptionType.PARAMETER_ERROR,
// "PCannot remove parameter <" +
// parameterName + ">, as it does not exist"));
// }
//
// // parameter which was defined as a field (and annotated) may not be removed
// if ( descriptor.getClass() == ALDOpParameterDescriptor.class) {
// throw( new ALDWorkflowException( WorkflowExceptionType.PARAMETER_ERROR,
// "ALDWorkflow::removeParameter parameter <" +
// parameterName + "> is a member variable and cannot be removed"));
// }
//
// Field field;
// try {
// field = ALDOperator.class.getDeclaredField( "parameterDescriptorsAll");
// field.setAccessible(true);
// @SuppressWarnings("unchecked")
// Hashtable<String,ALDOpParameterDescriptor> parameterDescriptorsAll =
// (Hashtable<String, ALDOpParameterDescriptor>) field.get( this);
// parameterDescriptorsAll.remove( parameterName);
//
// } catch (Exception e) {
// throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
// "ALDWorkflow::removeParameter cannot access parameterDescriptorsAll"));
// }
// }
// @Override
// public Object getParameter( String name) throws ALDOperatorException {
// if (this.getParameterDescriptor(name).getClass() == ALDOpParameterDescriptor.class) {
// return super.getParameter(name);
// } else {
// ALDOpParameterDescriptor descriptor = this.getParameterDescriptor(name);
// return descriptor.getValue();
// }
// }
// @Override
// public void setParameter( String name, Object value) throws ALDOperatorException {
// if (this.getParameterDescriptor(name).getClass() == ALDOpParameterDescriptor.class) {
// super.setParameter(name, value);
// } else {
// ALDOpParameterDescriptor descriptor = (ALDOpParameterDescriptor)this.getParameterDescriptor(name);
// descriptor.setValue( value);
// }
// }
/**
* Add a new workflow as a child to this workflow.
* @return
*/
protected ALDWorkflowNodeID createChildWorkflow() {
return createChildWorkflow(untitledWorkflowName);
}
/**
* Add a new workflow as a child to this workflow.
* @param name
* @return
*/
protected ALDWorkflowNodeID createChildWorkflow( String name) {
//TODO implement
return null;
}
/**
* Relocate the node to this workflow.
*
* @param nodeId
*/
protected void relocateNode( ALDWorkflowNodeID nodeId, boolean disconnectEdges) {
LinkedList<ALDWorkflowNodeID> nodeIds = new LinkedList<ALDWorkflowNodeID>();
nodeIds.add( nodeId);
relocateNodes(nodeIds, disconnectEdges);
}
/**
* Relocate the nodes <code>nodeIds</code> and all edges of the sub graph induced by these
* nodes to this workflow.
* If <code>disconnectEdges</code> is true, all edges connecting <code>nodeIds</code>
* to nodes outside of this sub graph are removed.
* Otherwise an exception is thrown if such edges exist.
*
*
* @param nodeIds
* @param disconnectEdges
*/
protected void relocateNodes( Collection<ALDWorkflowNodeID> nodeIds, boolean disconnectEdges) {
//TODO implement
// first check, if all nodes belong to the same workflow
// .. and if this workflow is not identical to this workflow
// check is no ode has edges incident to other nodes or
// collect them for removal
// remove edges if applicable
// move nodes form old workflow to this workflow (only move form nodes field
// the same for edges
}
//=======================================
// general helpers
@Override
public void print() {
System.out.println(" === Print workflow ===========================================");
super.print();
System.out.println( "Graph section");
System.out.println( "workflow <" + this.name + ">");
System.out.println();
this.interiorShadowNode.print();
System.out.println();
System.out.println("Nodes:");
System.out.println();
for ( ALDWorkflowNode node : this.nodes ) {
node.print();
}
System.out.println();
System.out.println("Edges:");
System.out.println();
for ( ALDWorkflowEdge edge : this.edges ) {
edge.print();
}
System.out.println(" === END: Print workflow =======================================");
}
/**
* Formats the ids of the given nodes into a string for debugging purposes.
*
* @param nodes
* @return
*/
static String nodeIdsToString(Collection<ALDWorkflowNode> nodes) {
StringBuffer buf = new StringBuffer();
buf.append('[');
for ( ALDWorkflowNode node : nodes) {
buf.append(node.getId() + ",");
}
if ( buf.length() != 1) {
buf.setCharAt(buf.length()-1, ']');
} else {
buf.append(']');
}
return new String( buf);
}
/**
* Formats the ids of the given edges into a string for debugging purposes.
*
* @param edges
* @return
*/
static String edgeIdsToString(Collection<ALDWorkflowEdge> edges) {
StringBuffer buf = new StringBuffer();
buf.append('[');
for ( ALDWorkflowEdge edge : edges) {
buf.append(edge.getId() + ",");
}
if ( buf.length() != 1) {
buf.setCharAt(buf.length()-1, ']');
} else {
buf.append(']');
}
return new String( buf);
}
@Override
public void handleOperatorExecutionProgressEvent(
ALDOperatorExecutionProgressEvent e) throws ALDWorkflowException {
// figure out which node is associated with the operator
HashSet<ALDWorkflowNodeID> changeSet = new HashSet<ALDWorkflowNodeID>();
ALDOperator op = null;
try {
op = e.getOriginatingOperator();
} catch (Exception e2) {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"ALDWorkflow::handleOperatorExecutionProgressEvent not " +
"an ALDOperator associated with event"));
}
for ( ALDWorkflowNode node : this.nodes) {
if ( node.getOperator() == op) {
if ( debug >= 1) {
System.out.println( "ALDWorkflow::handleOperatorExecutionProgressEvent " +
"Operator " + op.getName() + " changed progress to <" +
e.getExecutionProgressDescr() + ">");
}
node.setOperatorExecutionProgressDescr(e.getExecutionProgressDescr());
changeSet.add( ALDWorkflow.mapNodeToNodeId(node));
}
}
if ( ! changeSet.isEmpty()) {
fireALDWorkflowEvent(new ALDWorkflowEvent(this,
ALDWorkflowEventType.NODE_EXECUTION_PROGRESS, changeSet));
} else {
throw( new ALDWorkflowException( WorkflowExceptionType.FATAL_INTERNAL_ERROR,
"ALDWorkflow::handleOperatorExecutionProgressEvent no workflow " +
"node associated with operator of event found"));
}
}
}