/*
* The University of Wales, Cardiff Triana Project Software License (Based
* on the Apache Software License Version 1.1)
*
* Copyright (c) 2007 University of Wales, Cardiff. All rights reserved.
*
* Redistribution and use of the software in source and binary forms, with
* or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any,
* must include the following acknowledgment: "This product includes
* software developed by the University of Wales, Cardiff for the Triana
* Project (http://www.trianacode.org)." Alternately, this
* acknowledgment may appear in the software itself, if and wherever
* such third-party acknowledgments normally appear.
*
* 4. The names "Triana" and "University of Wales, Cardiff" must not be
* used to endorse or promote products derived from this software
* without prior written permission. For written permission, please
* contact triana@trianacode.org.
*
* 5. Products derived from this software may not be called "Triana," nor
* may Triana appear in their name, without prior written permission of
* the University of Wales, Cardiff.
*
* 6. This software may not be sold, used or incorporated into any product
* for sale to third parties.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL UNIVERSITY OF WALES, CARDIFF OR ITS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* ------------------------------------------------------------------------
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Triana Project. For more information on the
* Triana Project, please see. http://www.trianacode.org.
*
* This license is based on the BSD license as adopted by the Apache
* Foundation and is governed by the laws of England and Wales.
*
*/
package org.trianacode.enactment;
import org.trianacode.config.TrianaProperties;
import org.trianacode.taskgraph.*;
import org.trianacode.taskgraph.imp.CableImp;
import org.trianacode.taskgraph.imp.TaskFactoryImp;
import org.trianacode.taskgraph.imp.TaskImp;
import org.trianacode.taskgraph.imp.ToolImp;
import org.trianacode.taskgraph.interceptor.InterceptorChain;
import org.trianacode.taskgraph.service.*;
import org.trianacode.taskgraph.tool.Tool;
/**
* A class for running a taskgraph. Unlike TrianaExec, TrianaRun does not handle synchronuizing between multiple
* invocations.
*
* @author Ian Wang
* @version $Revision: 4048 $
*/
public class TrianaRun {
protected Task task;
protected TaskGraph taskgraph;
protected ExecServer server;
protected Scheduler scheduler;
protected ExecCable[] incables;
protected ExecCable[] outcables;
private Task dummy;
/**
* Constructs a TrianaExec to execute a clone of the specified taskgraph. Uses a default tool table
*/
public TrianaRun(TaskGraph taskgraph) throws TaskGraphException {
init(taskgraph);
}
/**
* Constructs a TrianaExec to execute a clone of the specified tool. Uses a default tool table.
*/
public TrianaRun(Tool tool) throws TaskGraphException {
init(tool);
}
public SchedulerInterface getScheduler() {
return scheduler;
}
/**
* Initializes the triana exec
*/
protected void init(Tool tool) throws TaskGraphException {
initDummyTask();
if (tool instanceof TaskGraph) {
TaskGraph group = (TaskGraph) tool;
this.task = TaskGraphUtils.cloneTaskGraph(group, TaskGraphManager.DEFAULT_FACTORY_TYPE);
this.taskgraph = (TaskGraph) task;
} else {
this.taskgraph = TaskGraphManager.createTaskGraph(TaskGraphManager.DEFAULT_FACTORY_TYPE);
this.task = taskgraph.createTask(tool);
initGroupNodes();
TaskLayoutUtils.translateToOrigin(taskgraph.getTasks(false));
taskgraph.setToolName(tool.getToolName());
taskgraph.setToolPackage(tool.getToolPackage());
}
this.scheduler = new Scheduler(taskgraph);
TaskGraphManager.setTrianaServer(taskgraph, new ExecServer());
initCables();
}
/**
* Initialises a dummy task with the specified tool name. This task acts as a place holder at the unconnected end of
* the exec cables.
*/
private void initDummyTask() throws TaskException, NodeException {
dummy = new TaskImp(new ToolImp((TrianaProperties) null), new TaskFactoryImp(), false);
dummy.setToolName("Dummy");
dummy.addDataInputNode();
dummy.addDataOutputNode();
}
/**
* If an individual task was created then set its nodes as the taskgraphs parent nodes.
*/
private void initGroupNodes() throws NodeException {
Node[] innodes = task.getInputNodes();
Node[] outnodes = task.getOutputNodes();
for (int count = 0; count < innodes.length; count++) {
taskgraph.addDataInputNode(innodes[count]);
}
for (int count = 0; count < outnodes.length; count++) {
taskgraph.addDataOutputNode(outnodes[count]);
}
}
/**
* Initializes the cables connected to the taskgraph
*/
private void initCables() throws CableException {
Node[] innodes = taskgraph.getInputNodes();
Node[] outnodes = taskgraph.getOutputNodes();
incables = new ExecCable[innodes.length];
outcables = new ExecCable[outnodes.length];
for (int count = 0; count < innodes.length; count++) {
incables[count] = new ExecCable();
incables[count].connectInput(innodes[count]);
}
for (int count = 0; count < outnodes.length; count++) {
outcables[count] = new ExecCable();
outcables[count].connectOutput(outnodes[count]);
}
}
/**
* @return the taskgraph that is being executed. If a single task was executed then this returns that task wrapped
* in a simple taskgraph.
*/
public TaskGraph getTaskGraph() {
return taskgraph;
}
/**
* @return the task that is being executed.
*/
public Task getTask() {
return task;
}
/**
* @return the dummy tool name
*/
public String getDummyToolName() {
return dummy.getToolName();
}
/**
* Sets the dummy tool name
*/
public void setDummyToolName(String dummyname) {
dummy.setToolName(dummyname);
}
/**
* Runs the unconnected tasks with the taskgraph
*/
public void runTaskGraph() throws SchedulerException {
scheduler.runTaskGraph();
}
/**
* @return true if the taskgraph has finished executing
*/
public boolean isFinished() {
return isFinished(taskgraph);
}
/**
* @return true if the taskgraph has finished executing
*/
private boolean isFinished(TaskGraph taskgraph) {
Task[] tasks = taskgraph.getTasks(true);
boolean finished = true;
for (int count = 0; (count < tasks.length) && (finished); count++) {
if (tasks[count] instanceof RunnableInstance) {
finished = finished && ((tasks[count]).getExecutionState()
== ExecutionState.COMPLETE);
}
if (tasks[count] instanceof TaskGraph) {
finished = finished && isFinished((TaskGraph) tasks[count]);
}
}
return finished;
}
/**
* @return the number of input nodes
*/
public int getInputNodeCount() {
return incables.length;
}
/**
* @return true if the input data on the specified node is successfully sent
*/
public boolean isDataSent(int count) {
return incables[count].isDataSent();
}
/**
* Sends output data using the specified input node
*/
public void sendInputData(int count, Object obj) {
incables[count].send(obj);
}
/**
* @return the number of output nodes
*/
public int getOutputNodeCount() {
return outcables.length;
}
/**
* @return true if data is ready at the specified output node
*/
public boolean isOutputReady(int count) {
return outcables[count].isDataReady();
}
/**
* @return the data at the specified output node, automatically unpacking it from any data message.
*/
public Object receiveOutputData(int count) {
return receiveOutputData(count, true);
}
/**
* @return the data at the specified output node, optionally unpacking it from any data message.
*/
public Object receiveOutputData(int count, boolean unpack) {
Object data = outcables[count].recv();
if (unpack) {
return unpackResult(data);
} else {
return data;
}
}
/**
* @return true if data is ready at every output node
*/
public boolean isOutputReady() {
boolean ready = true;
for (int count = 0; (count < outcables.length) && ready; count++) {
ready = ready && outcables[count].isDataReady();
}
return ready;
}
protected Object unpackResult(Object result) {
Object unpacked;
if (result instanceof DataMessage) {
unpacked = ((DataMessage) result).getData();
} else {
unpacked = result;
}
return unpacked;
}
/**
* Clean up after a run execution.
*/
public void dispose() {
scheduler.resetTaskGraph();
taskgraph.dispose();
}
private class ExecServer implements TrianaServer {
public ExecServer() {
}
public void notifyError(RunnableInstance runnable, String message) {
scheduler.notifyError(runnable, message);
}
public SchedulerInterface getSchedulerInterface() {
return scheduler;
}
/**
* Called by a control task to run the specified task within a running taskgraph
*/
public void runTask(Task task) throws SchedulerException {
scheduler.runTask(task);
}
}
private class ExecCable extends CableImp implements IOCable {
private Node node;
private Object data;
private boolean output = false;
private boolean suspended = false;
public void connectOutput(Node sendnode) throws CableException {
this.node = sendnode;
this.output = true;
sendnode.connect(this);
}
public void connectInput(Node recnode) throws CableException {
this.node = recnode;
this.output = false;
recnode.connect(this);
}
/**
* @return the node which receives data along this cable
*/
public Node getReceivingNode() {
if (!output) {
return node;
} else {
return dummy.getInputNode(0);
}
}
/**
* @return the node which receives data along this cable
*/
public Task getReceivingTask() {
if (!output) {
return node.getTask();
} else {
return dummy;
}
}
/**
* @return the node which sends data along this cable
*/
public Node getSendingNode() {
if (output) {
return node;
} else {
return dummy.getOutputNode(0);
}
}
/**
* @return the task which sends data (via a node) along this cable
*/
public Task getSendingTask() {
if (output) {
return node.getTask();
} else {
return dummy;
}
}
public String getType() {
return "Exec";
}
/**
* @return true if this cable is connected.
*/
public boolean isConnected() {
return (node != null);
}
public boolean isDataReady() {
return (data != null);
}
public Object recv() {
Object tempdata = data;
data = null;
return InterceptorChain.interceptSend(getSendingNode(), getReceivingNode(), tempdata);
}
public synchronized void suspend() {
suspended = true;
}
/**
* Unsuspends a pipe
*/
public void resume() {
suspended = false;
}
/**
* Flushes all data out of communication pipes.
*/
public void flush() {
data = null;
}
public synchronized void send(Object data) {
if (suspended) {
return;
}
this.data = InterceptorChain.interceptSend(getSendingNode(), getReceivingNode(), data);
if (!output) {
((RunnableInstance) node.getTopLevelTask()).wakeUp(node.getTopLevelNode());
}
}
public synchronized void sendNonBlocking(Object data) {
send(data);
}
public boolean isDataSent() {
return (data == null);
}
}
}