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.


Features

  • 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

Architecture

Class Diagram

H2H Process Framework Class Diagram

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 this IProcessComponent.
  • 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 normal IProcessComponents (leaf). These normal components represent a specific operation and do not contain other IProcessComponents.
  • ProcessComposite<T>: Abstract base class for all composite IProcessComponents. These composites contain other IProcessComponents.
  • ProcessDecorator<T>: Abstract base class for all decorators that decorate IProcessComponents. These decorators provide additional behavior or state to existing implementations of IProcessComponents.

Process States

The graphic depicts the state transitions that are valid for all IProcessComponents. In case some component tries to enter an invalid state, an InvalidProcessStateException will be thrown.

Process States

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.

Execution and Rollback

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: ProcessSteps 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).

Process Listeners

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.

API Demonstration

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 IProcessComponents 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();
⚠️ **GitHub.com Fallback** ⚠️