blob: 59a27f9362b746365da93936d9c908135bc817ef [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.invocation;
/**
* Abstract superclass of all 'event' types within a workflow invocation. These
* are the Job and Completion events which are used internally within a
* Processor, in particular by the dispatch stack and iteration system, and the
* WorkflowDataToken which is the only event class that can exist outside of a
* Processor boundary (and is therefore the most significant one for users of
* the API)
*
* @author Tom Oinn
*
*/
public abstract class Event<EventType extends Event<?>> {
protected String owner;
protected InvocationContext context;
protected int[] index;
protected Event(String owner, int[] index, InvocationContext context) {
this.owner = owner;
this.index = index;
this.context = context;
if (index == null) {
throw new RuntimeException("Job index cannot be null");
}
if (owner == null) {
throw new RuntimeException("Owning process cannot be null");
}
if (context == null) {
throw new RuntimeException("Invocation context cannot be null");
}
}
/**
* An event is final if its index array is zero length
*
* @return true if indexarray.length==0
*/
public final boolean isFinal() {
return (index.length == 0);
}
/**
* The event has an owner, this is represented as a String object but the
* ownership is hierarchical in nature. The String is a colon separated list
* of alphanumeric process identifiers, with identifiers being pushed onto
* this list on entry to a process and popped off on exit.
*
* @return String of colon separated process identifiers owning this Job
*/
public final String getOwningProcess() {
return this.owner;
}
public final InvocationContext getContext() {
return this.context;
}
/**
* Return a copy of the event subclass with the last owning process removed
* from the owning process list. For example, if the event had owner
* 'foo:bar' this would return a duplicate event with owner 'foo'. If the
* owning process is the empty string this is invalid and will throw a
* ProcessIdentifierException
*
* @return a copy of the event with the parent process identifier
*/
public abstract EventType popOwningProcess()
throws ProcessIdentifierException;
/**
* Return a copy of the event subclass with the specified local process name
* appended to the owning process identifier field. If the original owner
* was 'foo' and this was called with 'bar' you'd end up with a copy of the
* subclass with owner 'foo:bar'
*
* @param localProcessName
* name to add
* @return the modified event
* @throws ProcessIdentifierException
* if the local process name contains the ':' character
*/
public abstract EventType pushOwningProcess(String localProcessName)
throws ProcessIdentifierException;
/**
* Events have an index placing them in a conceptual tree structure. This
* index is carried along with the event and used at various points to drive
* iteration and ensure that separate jobs are kept that way
*/
public final int[] getIndex() {
return this.index;
}
/**
* Helper method for implementations of popOwningProcess, this constructs
* the appropriate process identifier after the leaf has been removed and
* returns it. If there is no leaf to remove, i.e. the current process
* identifier is the empty string, then ProcessIdentifierException is thrown
*
* @return
* @throws ProcessIdentifierException
*/
protected final String popOwner() throws ProcessIdentifierException {
// Empty string already, can't pop from here, throw exception
if (owner.equals("")) {
throw new ProcessIdentifierException(
"Attempt to pop a null owning process (empty string)");
}
// A single ID with no colon in, return the empty string
if (owner.lastIndexOf(':') < 0) {
return "";
}
return owner.substring(0, owner.lastIndexOf(':'));
}
/**
* Helper method for implementations of pushOwningProcess, appends the
* specified local name to the current owning process identifier and returns
* the new id. This doesn't change the current process identifier. If there
* is a colon ':' in the specified name this is invalid and will throw
* ProcessIdentifierException at you.
*
* @param newLocalProcess
* @return
* @throws ProcessIdentifierException
*/
protected final String pushOwner(String newLocalProcess)
throws ProcessIdentifierException {
if (newLocalProcess.contains(":")) {
throw new ProcessIdentifierException("Can't push '"
+ newLocalProcess + "' as it contains a ':' character");
}
if (owner.equals("")) {
// If the owner was the empty string we don't need to append the
// colon
return newLocalProcess;
} else {
return owner + ":" + newLocalProcess;
}
}
}