/*
* 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 java.util.ArrayList;
import org.trianacode.taskgraph.Task;
import org.trianacode.taskgraph.TaskGraph;
import org.trianacode.taskgraph.TaskGraphException;
import org.trianacode.taskgraph.service.SchedulerException;
import org.trianacode.taskgraph.tool.Tool;
/**
* A class for executing a Triana taskgraph. TrianaExec assumes that each taskgraph invocation will correspond with a
* single set of output data, and thereby synchronizes multiple invocations.
*
* @author Ian Wang
* @version $Revision: 4048 $
*/
public class TrianaExec {
/**
* the time in milliseconds that the manager threads sleep when waiting for the previous data to be sent/the data to
* arrive.
*/
public static long DATA_SLEEP_TIME = 500;
private TrianaRun run;
private ExecInputManager inmanager;
private ExecOutputManager outmanager;
/**
* Constructs a TrianaExec to execute a clone of the specified taskgraph. Uses a default tool table
*/
public TrianaExec(TaskGraph taskgraph) throws TaskGraphException {
run = new TrianaRun(taskgraph);
initExecManagers();
}
/**
* Constructs a TrianaExec to execute a clone of the specified tool. Uses a default tool table.
*/
public TrianaExec(Tool tool) throws TaskGraphException {
run = new TrianaRun(tool);
initExecManagers();
}
/**
* Start the exec manager threads
*/
private void initExecManagers() {
outmanager = new ExecOutputManager();
inmanager = new ExecInputManager(outmanager);
outmanager.start();
inmanager.start();
}
/**
* @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 run.getTaskGraph();
}
/**
* @return the task that is being executed.
*/
public Task getTask() {
return run.getTask();
}
/**
* @return the dummy tool name
*/
public String getDummyToolName() {
return run.getDummyToolName();
}
/**
* Sets the dummy tool name
*/
public void setDummyToolName(String dummyname) {
run.setDummyToolName(dummyname);
}
/**
* @return true if the taskgraph has finished executing
*/
public boolean isFinished() {
return run.isFinished();
}
/**
* Executes the taskgraph using the specified input data array. Automatically unpacks any result data contained in
* data messages.
*/
public Object[] run(Object[] data) throws TaskGraphException, SchedulerException {
return run(data, true);
}
/**
* Executes the taskgraph using the specified input data array.
*
* @param unpack sets whether data within data messages is automatically unpacked in the results array
*/
public Object[] run(Object[] data, boolean unpack) throws TaskGraphException, SchedulerException {
ExecRun exec = new ExecRun(data, Thread.currentThread(), unpack);
inmanager.addExecRun(exec);
while (!exec.isOutputData()) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException except) {
}
}
return exec.getOutputData();
}
/**
* Dispose of the TrianaExec and clean up the threads
*/
public void dispose() {
inmanager.stopThread();
outmanager.stopThread();
run.dispose();
}
protected class ExecRun {
private Object[] indata;
private Object[] outdata = null;
private Thread thread;
private boolean unpack;
public ExecRun(Object[] indata, Thread thread, boolean unpack) {
this.indata = indata;
this.thread = thread;
this.unpack = unpack;
}
/**
* @return true if the data should be automatically unpacked
*/
public boolean unpackData() {
return unpack;
}
/**
* @return the input data
*/
public Object[] getInputData() {
return indata;
}
/**
* @return true if the output data is available
*/
public boolean isOutputData() {
return outdata != null;
}
/**
* Sets the output data and interupts the waiting thread
*/
public void setOutputData(Object[] outdata) {
this.outdata = outdata;
thread.interrupt();
}
/**
* @return the output data
*/
public Object[] getOutputData() {
return outdata;
}
}
private class ExecInputManager extends Thread {
private ArrayList inexec = new ArrayList();
private ExecOutputManager outmanager;
private boolean stopped = false;
public ExecInputManager(ExecOutputManager outmanager) {
setName("TrianaExecInputManager");
this.outmanager = outmanager;
}
public void addExecRun(ExecRun run) {
synchronized (this) {
inexec.add(run);
notifyAll();
}
}
public boolean isStopped() {
return stopped;
}
public void stopThread() {
synchronized (this) {
this.stopped = true;
this.notifyAll();
}
}
public void run() {
while (!stopped) {
try {
if (!inexec.isEmpty()) {
waitReady();
handleRun();
} else {
synchronized (this) {
if (!stopped) {
try {
this.wait();
} catch (InterruptedException except) {
}
}
}
}
} catch (SchedulerException except) {
except.printStackTrace();
}
}
inexec.clear();
outmanager = null;
}
private void waitReady() {
boolean ready = false;
do {
ready = true;
for (int count = 0; (count < run.getInputNodeCount()) && (ready); count++) {
ready = ready && run.isDataSent(count);
}
if (!ready) {
sleepThread(DATA_SLEEP_TIME);
}
} while ((!ready) && (!stopped));
}
private void handleRun() throws SchedulerException {
if (!stopped) {
ExecRun exec = (ExecRun) inexec.remove(0);
Object[] indata = exec.getInputData();
outmanager.addExecRun(exec);
run.runTaskGraph();
for (int count = 0; (count < run.getOutputNodeCount()) && (count < indata.length); count++) {
run.sendInputData(count, indata[count]);
}
}
}
private void sleepThread(long length) {
synchronized (this) {
try {
if (!stopped) {
this.wait(length);
}
} catch (InterruptedException e) {
}
}
}
}
private class ExecOutputManager extends Thread {
private ArrayList outexec = new ArrayList();
private boolean stopped = false;
public ExecOutputManager() {
setName("TrianaExecOutputManager");
}
public void addExecRun(ExecRun run) {
synchronized (this) {
outexec.add(run);
notifyAll();
}
}
public boolean isStopped() {
return stopped;
}
public synchronized void stopThread() {
synchronized (this) {
this.stopped = true;
this.notifyAll();
}
}
public void run() {
while (!stopped) {
if (!outexec.isEmpty()) {
waitReady();
handleOutput();
} else {
synchronized (this) {
if (!stopped) {
try {
this.wait();
} catch (InterruptedException except) {
}
}
}
}
}
outexec.clear();
}
private void waitReady() {
boolean ready = false;
do {
ready = true;
for (int count = 0; (count < run.getOutputNodeCount()) && (ready); count++) {
ready = ready && run.isOutputReady(count);
}
if (!ready) {
sleepThread(DATA_SLEEP_TIME);
}
} while ((!ready) && (!stopped));
}
private void handleOutput() {
if (!stopped) {
ExecRun exec = (ExecRun) outexec.remove(0);
Object[] outdata = new Object[run.getOutputNodeCount()];
for (int count = 0; count < outdata.length; count++) {
outdata[count] = run.receiveOutputData(count, exec.unpackData());
}
exec.setOutputData(outdata);
}
}
private void sleepThread(long length) {
synchronized (this) {
try {
if (!stopped) {
this.wait(length);
}
} catch (InterruptedException e) {
}
}
}
}
}