Processes - Hive2Hive/Hive2Hive GitHub Wiki
Internally, all Hive2Hive operations are executed by means of processes. For this reason, the Hive2Hive project features a separate open-source process framework project, the Hive2Hive - Process Framework.
This process framework is highly extendable and reusable such that future additions, improvements and extensions are possible.
- simple, straightforward API
- supports:
- rollback
- result computation
- pause/resume
- asynchronous execution/rollback
-
easily extendable due to the use of GoF Design Patterns:
- all components have the same API
- processes can be built by using a Composite:
- use default containers or define your own
- define your own process steps
- process trees can be built by nesting components (containers, steps)
- processes can be extended by adding behaviour/state by using a Decorator:
- use default decorators or define your own
The most essential elements are the following:
-
IProcessComponent<T>
: Basic interface for all process components. Defines all common functionality.T
is the type of the result computed by thisIProcessComponent
. -
ProcessComponent<T>
: Abstract base class for all process components. Keeps track of a process components' most essential properties and functionalities. -
ProcessStep<T>
: Abstract base class for all normalIProcessComponent
s (leaf). These normal components represent a specific operation and do not contain otherIProcessComponent
s. -
ProcessComposite<T>
: Abstract base class for all compositeIProcessComponent
s. These composites contain otherIProcessComponent
s. -
ProcessDecorator<T>
: Abstract base class for all decorators that decorateIProcessComponent
s. These decorators provide additional behavior or state to existing implementations ofIProcessComponent
s.
The graphic depicts the state transitions that are valid for all IProcessComponent
s. In case some component tries to enter an invalid state, an InvalidProcessStateException
will be thrown.
Process State | Description |
---|---|
READY |
Represents an IProcessComponent that is ready to be executed. |
EXECUTING |
Represents an IProcessComponent that is currently being executed. |
EXECUTION_SUCCEEDED |
Represents an IProcessComponent that has executed successfully. |
EXECUTION_FAILED |
Represents an IProcessComponent that has executed unsuccessfully due to cancellation or a failure. |
ROLLBACKING |
Represents an IProcessComponent that is currently being rolled back. |
ROLLBACK_SUCCEEDED |
Represents an IProcessComponent that has rolled back successfully. |
ROLLBACK_FAILED |
Represents an IProcessComponent that has rolled back unsuccessfully due to a failure. |
PAUSED |
Represents an IProcessComponent that is currently paused, whether it is executing or rolling back. |
The process framework uses the following vocabulary to define the lifespan of a process component:
- Execution - The normal process flow, from start to end/position of fail.
- Rollback - The reverse process flow, from end/position of fail to start.
In case of a failure or manual cancellation during a process component's execution, a ProcessExecutionException
is thrown.
In case of a failure during a process component's rollback, a ProcessRollbackException
is thrown.
Both exceptions grant access to the failing component, the 'source' of failure, in the composite via getSource()
.
Note: ProcessStep
s do not rollback by default!
As each process step executes something else, it is left to the developer to decide if a step needs to be rolled back an when. In case a step needs to be rolled back, the rollback flag must be set in doExecute()
with setRequiresRollback(true)
.
During rollback, the same rule applies in doRollback()
. The developer decides when the rollback has been finished with setRequiresRollback(false)
.
Each IProcessComponent
can be attached with event listeners that implement the IProcessComponentListener
interface. This interface defines the enlisted methods that are fired for the most fundamental process events. The parameter of type IProcessEventArgs
provides additional information about the fired event.
Event | Description |
---|---|
onExecuting | Fires when the observed IProcessComponent 's starts its execution. |
onRollbacking | Fires when the observed IProcessComponent 's starts its rollback. |
onPaused | Fires when the observed IProcessComponent 's gets paused. |
onExecutionSucceeded | Fires if the observed IProcessComponent 's execution succeeded. |
onExecutionFailed | Fires if the observed IProcessComponent 's execution failed. |
onRollbackSucceeded | Fires if the observed IProcessComponent 's rollback succeeded. |
onRollbackFailed | Fires if the observed IProcessComponent 's rollback failed. |
The following code shows the most fundamental things that can be done with the framework.
Creating a ProcessStep
Simply inherit from the abstract class ProcessStep<T>
, where T
is the type of the result, and implement doExecute()
(and doRollback()
). (Tip: T
can also be Void
.)
public class MyProcessStep extends ProcessStep<Integer> {
@Override
protected Integer doExecute() throws InvalidProcessStateException, ProcessExecutionException {
// do some stuff...
int result = 1 + 2 + 3;
// tag for rollback
setRequiresRollback(true);
return result;
}
@Override
protected Integer doRollback() throws InvalidProcessStateException, ProcessRollbackException {
// rollback stuff...
int result = 0;
// untag for rollback
setRequiresRollback(false);
return result;
}
}
Execution and Rollback
Then, the above created step can be executed and its result retrieved as follows. In case of an execution error, it could directly be rolled back.
ProcessStep<Integer> step1 = new MyProcessStep();
int result;
try {
result = step1.execute();
} catch (ProcessExecutionException ex) {
result = step1.rollback();
}
Asynchronous Components
Every IProcessComponent<T>
can be wrapped with the AsyncComponent
decorator. With this, the component's execution and rollback run on a separate thread and immediately return control to the caller.
ProcessStep<Integer> step2 = new MyProcessStep();
IProcessComponent<Future<Integer>> asyncStep2 = new AsyncComponent<Integer>(step2);
Future<Integer> futureResult = asyncStep2.execute();
Note: Dealing with asynchronous components might be a bit tricky. Make sure to take a look at our Wiki Example to be sure to use it correctly.
Process Composition
Classes inheriting from ProcessComposition<T>
allow to nest IProcessComponent
s and to build process trees. The below example shows the usage of the default SyncProcess
.
ProcessComposite<Void> composite1 = new SyncProcess();
ProcessComposite<Void> composite2 = new SyncProcess();
composite1.add(step1); // add a single ProcessStep
composite1.add(asyncStep2); // add an async component
composite1.add(composite2); // add another ProcessComposite
Now, we could make the whole composite run asynchronously.
IProcessComponent<Future<Void>> asyncComposite = new AsyncComponent<Void>(composite1);
try {
// execute composite sync
composite.execute();
// or async
asyncComposite.execute();
} catch (ProcessExecutionException ex) {
// rollback composite sync
composite.execute();
// or async
asyncComposite.rollback();
}
Pause and Resume
IProcessComponent<T>
s that are wrapped with the AsyncComponent
decorator can be paused/resumed because they run asynchronously.
asyncComposite.execute();
asyncComposite.pause();
// do other stuff meanwhile...
asyncComposite.resume();