Package org.apache.wiki.workflow

# Class Workflow

java.lang.Object
org.apache.wiki.workflow.Workflow

All Implemented Interfaces:
  `Serializable`

public class Workflow extends Object implements Serializable

Sequence of `Step` objects linked together. Workflows are always initialized with a message key that denotes the name of the Workflow, and a Principal that represents its owner.

## Workflow lifecycle

A Workflow's state (obtained by `getCurrentState()`) will be one of the following:

- `CREATED`: after the Workflow has been instantiated, but before it has been started using the `start(Context)` method.
- `RUNNING`: after the Workflow has been started using the `start(Context)` method, but before it has finished processing all Steps. Note that a Workflow can only be started once; attempting to start it again results in an IllegalStateException. Callers can place the Workflow into the WAITING state by calling `waitstate()`.
- `WAITING`: when the Workflow has temporarily paused, for example because of a pending Decision. Once the responsible actor decides what to do, the caller can change the Workflow back to the RUNNING state by calling the `restart(Context)` method (this is done automatically by the Decision class, for instance, when the `Decision.decide(Outcome, Context)` method is invoked)
- `COMPLETED`: after the Workflow has finished processing all Steps, without errors.
- `ABORTED`: if a Step has elected to abort the Workflow.

## Steps and processing algorithm

Workflow Step objects can be of type `Decision`, `Task` or other Step subclasses. Decisions require user input, while Tasks do not. See the `Step` class for more details.

After instantiating a new Workflow (but before telling it to `start(Context)`), calling classes should specify the first Step by executing the `setFirstStep(Step)` method. Additional Steps can be chained by invoking the first step's `Step.addSuccessor(Outcome, Step)` method.

When a Workflow's `start` method is invoked, the Workflow retrieves the first Step and processes it. This Step, and subsequent ones, are processed as follows:

- The Step's `Step.start()` method executes, which sets the start time.
- The Step's `Step.execute(Context)` method is called to begin processing, which will return an Outcome to indicate completion, continuation or errors:

- `Outcome.STEP_COMPLETE` indicates that the execution method ran without errors, and that the Step should be considered "completed."
- `Outcome.STEP_CONTINUE` indicates that the execution method ran without errors, but that the Step is not "complete" and should be put into the WAITING state.
- `Outcome.STEP_ABORT` indicates that the execution method encountered errors, and should abort the Step and the Workflow as a whole. When this happens, the Workflow will set the current Step's Outcome to `Outcome.STEP_ABORT` and invoke the Workflow's `abort(Context)` method. The Step's processing errors, if any, can be retrieved by `Step.getErrors()`.

- The Outcome of the `execute` method also affects what happens next. Depending on the result (and assuming the Step did not abort), the Workflow will either move on to the next Step or put the Workflow into the `WAITING` state:

- If the Outcome denoted "completion" (i.e., its `Step.isCompleted()` method returns `true`) then the Step is considered complete; the Workflow looks up the next Step by calling the current Step's `Step.getSuccessor(Outcome)` method. If `successor()` returns a non-`null` Step, the return value is marked as the current Step and added to the Workflow's Step history. If `successor()` returns `null`, then the Workflow has no more Steps and it enters the `COMPLETED` state.
- If the Outcome did not denote "completion" (i.e., its `Step.isCompleted()` method returns `false`), then the Step still has further work to do. The Workflow enters the `WAITING` state and stops further processing until a caller restarts it.

The currently executing Step can be obtained by `getCurrentStep()`. The actor for the current Step is returned by `getCurrentActor()`.

To provide flexibility for specific implementations, the Workflow class provides two additional features that enable Workflow participants (i.e., Workflow subclasses and Step/Task/Decision subclasses) to share context and state information. These two features are named attributes and message arguments:

- Named attributes are simple key-value pairs that Workflow participants can get or set. Keys are Strings; values can be any Object. Named attributes are set with `setAttribute(String, Serializable)` and retrieved with `getAttribute(String)`.
- Message arguments are used in combination with JSPWiki's `InternationalizationManager` to create language-independent user interface messages. The message argument array is retrieved via `getMessageArguments()`; the first two array elements will always be these: a String representing work flow owner's name, and a String representing the current actor's name. Workflow participants can add to this array by invoking `addMessageArgument(Serializable)`.

## Example

Workflow Steps can be very powerful when linked together. JSPWiki provides two abstract subclasses classes that you can use to build your own Workflows: Tasks and Decisions. As noted, Tasks are Steps that execute without user intervention, while Decisions require actors (aka Principals) to take action. Decisions and Tasks can be mixed freely to produce some highly elaborate branching structures.

Here is a simple case. For example, suppose you would like to create a Workflow that (a) executes a initialization Task, (b) pauses to obtain an approval Decision from a user in the Admin group, and if approved, (c) executes a "finish" Task. Here's sample code that illustrates how to do it:

        // Create workflow; owner is current user
     1  Workflow workflow = new Workflow( " workflow.myworkflow ", context.getCurrentUser() );

        // Create custom initialization task
     2  Step initTask = new InitTask( this );

        // Create finish task
     3  Step finishTask = new FinishTask( this );

        // Create an intermediate decision step
     4  Principal actor = new GroupPrincipal( "Admin" );
     5  Step decision = new SimpleDecision( this, "decision.AdminDecision", actor );

        // Hook the steps together
     6  initTask.addSuccessor( Outcome.STEP_COMPLETE, decision );
     7  decision.addSuccessor( Outcome.DECISION_APPROVE, finishTask );

        // Set workflow's first step
     8  workflow.setFirstStep( initTask );

Some comments on the source code:

- Line 1 instantiates the workflow with a sample message key and designated owner Principal, in this case the current wiki user
- Lines 2 and 3 instantiate the custom Task subclasses, which contain the business logic
- Line 4 creates the relevant GroupPrincipal for the `Admin` group, who will be the actor in the Decision step
- Line 5 creates the Decision step, passing the Workflow, sample message key, and actor in the constructor
- Line 6 specifies that if the InitTask's Outcome signifies "normal completion" (STEP_COMPLETE), the SimpleDecision step should be invoked next
- Line 7 specifies that if the actor (anyone possessing the `Admin` GroupPrincipal) selects DECISION_APPROVE, the FinishTask step should be invoked
- Line 8 adds the InitTask (and all of its successor Steps, nicely wired together) to the workflow

See Also:
  - Serialized Form

## Field Summary

Fields
Modifier and Type
Field
Description
`static final int`
`ABORTED`
State value: Workflow aborted before completion.
`static final int`
`COMPLETED`
State value: Workflow completed all Steps without errors.
`static final int`
`CREATED`
State value: Workflow instantiated, but not started.
`static final int`
`ID_NOT_SET`
ID value: the workflow ID has not been set.
`static final int`
`RUNNING`
State value: Workflow started, and is running.
`static final int`
`WAITING`
State value: Workflow paused, typically because a Step returned an Outcome that doesn't signify "completion."

## Constructor Summary

Constructors
Constructor
Description
`Workflow(String messageKey, Principal owner)`
Constructs a new Workflow object with a supplied message key, owner Principal, and undefined unique identifier `ID_NOT_SET`.

## Method Summary

Modifier and Type
Method
Description
`final void`
`abort(org.apache.wiki.api.core.Context context)`
Aborts the Workflow by setting the current Step's Outcome to `Outcome.STEP_ABORT`, and the Workflow's overall state to `ABORTED`.
`final void`
`addMessageArgument(Serializable obj)`
Appends a message argument object to the array returned by `getMessageArguments()`.
`protected void`
`cleanup()`
Clears the attribute map and sets the current step field to `null`.
`protected final void`
`complete()`
Protected helper method that changes the Workflow's state to `COMPLETED` and sets the current Step to `null`.
`final Object`
`getAttribute(String attr)`
Retrieves a named Object associated with this Workflow.
`final Map<String,Serializable>`
`getAttributes()`
Retrieves workflow's attributes.
`final Principal`
`getCurrentActor()`
Returns the actor Principal responsible for the current Step.
`final int`
`getCurrentState()`
Returns the workflow state: `CREATED`, `RUNNING`, `WAITING`, `COMPLETED` or `ABORTED`.
`final Step`
`getCurrentStep()`
Returns the current Step, or `null` if the workflow has not started or already completed.
`final Date`
`getEndTime()`
The end time for this Workflow, expressed as a system time number.
`final List<Step>`
`getHistory()`
Returns a Step history for this Workflow as a List, chronologically, from the first Step to the currently executing one.
`final int`
`getId()`
Returns the unique identifier for this Workflow.
`final Serializable[]`
`getMessageArguments()`
Returns an array of message arguments, used by `MessageFormat` to create localized messages.
`final String`
`getMessageKey()`
Returns an i18n message key for the name of this workflow; for example, `workflow.saveWikiPage`.
`final Principal`
`getOwner()`
The owner Principal on whose behalf this Workflow is being executed; that is, the user who created the workflow.
`final Step`
`getPreviousStep()`
Convenience method that returns the predecessor of the current Step.
`final Date`
`getStartTime()`
The start time for this Workflow, expressed as a system time number.
`final boolean`
`isAborted()`
Returns `true` if the workflow had been previously aborted.
`final boolean`
`isCompleted()`
Determines whether this Workflow is completed; that is, if it has no additional Steps to perform.
`final boolean`
`isStarted()`
Determines whether this Workflow has started; that is, its `start(Context)` method has been executed.
`protected final Step`
`previousStep(Step step)`
Protected method that returns the predecessor for a supplied Step.
`protected final void`
`processCurrentStep(org.apache.wiki.api.core.Context context)`
Protected method that processes the current Step by calling `Step.execute(Context)`.
`final void`
`restart(org.apache.wiki.api.core.Context context)`
Restarts the Workflow from the `WAITING` state and puts it into the `RUNNING` state again.
`final void`
`setAttribute(String attr, Serializable obj)`
Temporarily associates an object with this Workflow, as a named attribute, for the duration of workflow execution.
`final void`
`setFirstStep(Step step)`
Sets the first Step for this Workflow, which will be executed immediately after the `start(Context)` method executes.
`final void`
`setId(int id)`
Sets the unique identifier for this Workflow.
`final void`
`start(org.apache.wiki.api.core.Context context)`
Starts the Workflow and sets the state to `RUNNING`.
`final void`
`waitstate()`
Sets the Workflow in the `WAITING` state.

### Methods inherited from class java.lang.Object

`clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

## Field Details

### ID_NOT_SET

public static final int ID_NOT_SET
ID value: the workflow ID has not been set.

See Also:
  - Constant Field Values

### COMPLETED

public static final int COMPLETED
State value: Workflow completed all Steps without errors.

See Also:
  - Constant Field Values

### ABORTED

public static final int ABORTED
State value: Workflow aborted before completion.

See Also:
  - Constant Field Values

### WAITING

public static final int WAITING
State value: Workflow paused, typically because a Step returned an Outcome that doesn't signify "completion."

See Also:
  - Constant Field Values

### RUNNING

public static final int RUNNING
State value: Workflow started, and is running.

See Also:
  - Constant Field Values

### CREATED

public static final int CREATED
State value: Workflow instantiated, but not started.

See Also:
  - Constant Field Values

## Constructor Details

### Workflow

public Workflow(String messageKey, Principal owner)
Constructs a new Workflow object with a supplied message key, owner Principal, and undefined unique identifier `ID_NOT_SET`. Once instantiated the Workflow is considered to be in the `CREATED` state; a caller must explicitly invoke the `start(Context)` method to begin processing.

Parameters:
  `messageKey` - the message key used to construct a localized workflow name, such as `workflow.saveWikiPage`
  `owner` - the Principal who owns the Workflow. Typically, this is the user who created and submitted it

## Method Details

### abort

public final void abort(org.apache.wiki.api.core.Context context)
Aborts the Workflow by setting the current Step's Outcome to `Outcome.STEP_ABORT`, and the Workflow's overall state to `ABORTED`. It also appends the aborted Step into the workflow history, and sets the current step to `null`. If the Step is a Decision, it is removed from the DecisionQueue. This method can be called at any point in the lifecycle prior to completion, but it cannot be called twice. It finishes by calling the `cleanup()` method to flush retained objects. If the Workflow had been previously aborted, this method throws an IllegalStateException.

### addMessageArgument

public final void addMessageArgument(Serializable obj)
Appends a message argument object to the array returned by `getMessageArguments()`. The object must be an type used by the `MessageFormat`: String, Date, or Number (BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short). If the object is not of type String, Number or Date, this method throws an IllegalArgumentException.

Parameters:
  `obj` - the object to add

### getCurrentActor

public final Principal getCurrentActor()
Returns the actor Principal responsible for the current Step. If there is no current Step, this method returns `null`.

Returns:
  the current actor

### getCurrentState

public final int getCurrentState()
Returns the workflow state: `CREATED`, `RUNNING`, `WAITING`, `COMPLETED` or `ABORTED`.

Returns:
  the workflow state

### getCurrentStep

public final Step getCurrentStep()
Returns the current Step, or `null` if the workflow has not started or already completed.

Returns:
  the current step

### getAttribute

public final Object getAttribute(String attr)
Retrieves a named Object associated with this Workflow. If the Workflow has completed or aborted, this method always returns `null`.

Parameters:
  `attr` - the name of the attribute
Returns:
  the value

### getAttributes

public final Map<String,Serializable> getAttributes()
Retrieves workflow's attributes.

Returns:
  workflow's attributes.

### getEndTime

public final Date getEndTime()
The end time for this Workflow, expressed as a system time number. This value is equal to the end-time value returned by the final Step's `Step.getEndTime()` method, if the workflow has completed. Otherwise, this method returns `Step.TIME_NOT_SET`.

Returns:
  the end time

### getId

public final int getId()
Returns the unique identifier for this Workflow. If not set, this method returns ID_NOT_SET (0).

Returns:
  the unique identifier

### getMessageArguments

public final Serializable[] getMessageArguments()

Returns an array of message arguments, used by `MessageFormat` to create localized messages. The first two array elements will always be these:

- String representing the name of the workflow owner (i.e.,`getOwner()`)
- String representing the name of the current actor (i.e.,`getCurrentActor()`). If the current step is `null` because the workflow hasn't started or has already finished, the value of this argument will be a dash character (`-`)

Workflow and Step subclasses are free to append items to this collection with `addMessageArgument(Serializable)`.

Returns:
  the array of message arguments

### getMessageKey

public final String getMessageKey()
Returns an i18n message key for the name of this workflow; for example, `workflow.saveWikiPage`.

Returns:
  the name

### getOwner

public final Principal getOwner()
The owner Principal on whose behalf this Workflow is being executed; that is, the user who created the workflow.

Returns:
  the name of the Principal who owns this workflow

### getStartTime

public final Date getStartTime()
The start time for this Workflow, expressed as a system time number. This value is equal to the start-time value returned by the first Step's `Step.getStartTime()` method, if the workflow has started already. Otherwise, this method returns `Step.TIME_NOT_SET`.

Returns:
  the start time

### getHistory

public final List<Step> getHistory()
Returns a Step history for this Workflow as a List, chronologically, from the first Step to the currently executing one. The first step is the first item in the array. If the Workflow has not started, this method returns a zero-length array.

Returns:
  an array of Steps representing those that have executed, or are currently executing

### isAborted

public final boolean isAborted()
Returns `true` if the workflow had been previously aborted.

Returns:
  the result

### isCompleted

public final boolean isCompleted()
Determines whether this Workflow is completed; that is, if it has no additional Steps to perform. If the last Step in the workflow is finished, this method will return `true`.

Returns:
  `true` if the workflow has been started but has no more steps to perform; `false` if not.

### isStarted

public final boolean isStarted()
Determines whether this Workflow has started; that is, its `start(Context)` method has been executed.

Returns:
  `true` if the workflow has been started; `false` if not.

### getPreviousStep

public final Step getPreviousStep()
Convenience method that returns the predecessor of the current Step. This method simply examines the Workflow history and returns the second-to-last Step.

Returns:
  the predecessor, or `null` if the first Step is currently executing

### restart

public final void restart(org.apache.wiki.api.core.Context context) throws org.apache.wiki.api.exceptions.WikiException
Restarts the Workflow from the `WAITING` state and puts it into the `RUNNING` state again. If the Workflow had not previously been paused, this method throws an IllegalStateException. If any of the Steps in this Workflow throw a WikiException, the Workflow will abort and propagate the exception to callers.

Parameters:
  `context` - current wiki context
Throws:
  `org.apache.wiki.api.exceptions.WikiException` - if the current task's `AbstractStep.execute(Context)` method throws an exception

### setAttribute

public final void setAttribute(String attr, Serializable obj)
Temporarily associates an object with this Workflow, as a named attribute, for the duration of workflow execution. The passed object can be anything required by an executing Step, although it should be serializable. Note that when the workflow completes or aborts, all attributes will be cleared.

Parameters:
  `attr` - the attribute name
  `obj` - the value

### setFirstStep

public final void setFirstStep(Step step)
Sets the first Step for this Workflow, which will be executed immediately after the `start(Context)` method executes. Note than the Step is not marked as the "current" step or added to the Workflow history until the `start(Context)` method is called.

Parameters:
  `step` - the first step for the workflow

### setId

public final void setId(int id)
Sets the unique identifier for this Workflow.

Parameters:
  `id` - the unique identifier

### start

public final void start(org.apache.wiki.api.core.Context context) throws org.apache.wiki.api.exceptions.WikiException
Starts the Workflow and sets the state to `RUNNING`. If the Workflow has already been started (or previously aborted), this method returns an IllegalStateException. If any of the Steps in this Workflow throw a WikiException, the Workflow will abort and propagate the exception to callers.

Parameters:
  `context` - current wiki context.
Throws:
  `org.apache.wiki.api.exceptions.WikiException` - if the current Step's `Step.start()` method throws an exception of any kind

### waitstate

public final void waitstate()
Sets the Workflow in the `WAITING` state. If the Workflow is not running or has already been paused, this method throws an IllegalStateException. Once paused, the Workflow can be un-paused by executing the `restart(Context)` method.

### cleanup

protected void cleanup()
Clears the attribute map and sets the current step field to `null`.

### complete

protected final void complete()
Protected helper method that changes the Workflow's state to `COMPLETED` and sets the current Step to `null`. It calls the `cleanup()` method to flush retained objects. This method will no-op if it has previously been called.

### previousStep

protected final Step previousStep(Step step)
Protected method that returns the predecessor for a supplied Step.

Parameters:
  `step` - the Step for which the predecessor is requested
Returns:
  its predecessor, or `null` if the first Step was supplied.

### processCurrentStep

protected final void processCurrentStep(org.apache.wiki.api.core.Context context) throws org.apache.wiki.api.exceptions.WikiException
Protected method that processes the current Step by calling `Step.execute(Context)`. If the `execute` throws an exception, this method will propagate the exception immediately to callers without aborting.

Throws:
  `org.apache.wiki.api.exceptions.WikiException` - if the current Step's `Step.start()` method throws an exception of any kind