https://github.com/soabase/soabase-stages
A tiny library that makes staged/pipelined CompletableFutures much easier to create and manage
https://github.com/soabase/soabase-stages
concurrency futures java java-8 java-9 threading
Last synced: 30 days ago
JSON representation
A tiny library that makes staged/pipelined CompletableFutures much easier to create and manage
- Host: GitHub
- URL: https://github.com/soabase/soabase-stages
- Owner: soabase
- License: apache-2.0
- Created: 2017-06-12T02:34:52.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-03-02T17:50:23.000Z (almost 8 years ago)
- Last Synced: 2025-07-05T08:16:52.661Z (7 months ago)
- Topics: concurrency, futures, java, java-8, java-9, threading
- Language: Java
- Homepage:
- Size: 64.5 KB
- Stars: 25
- Watchers: 4
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# soabase-stages
[](https://travis-ci.org/soabase/soabase-stages)
[](http://search.maven.org/#search%7Cga%7C1%7Csoabase-stages)
A tiny library that makes staged/pipelined CompletableFutures much easier to create and manage.
## Use Cases
- You have a sequence of tasks that pipeline or chain
- These tasks can be executed **synchronously** or **asynchronously**
- You might need to **abort/cancel** the chain in the middle
- You might need to provide a **timeout** for the tasks
Most of this can be done with Java 8's CompletableFuture/CompletionStage today. Timeouts
must be added manually (or wait for Java 9). Aborting tasks is not supported. Also, the
CompletableFuture/CompletionStage API is awkward and difficult to use.
## StagedFuture
`StagedFuture` simplifies CompletableFuture/CompletionStage so that you can write code like this:
```java
StagedFuture.async(executor)
.thenIf(() -> queryDatabaseFor("something"))
.withTimeout(Duration.ofSeconds(25))
.thenIf(record -> applyRecord(record)) // chain aborts if no record found
.thenIf(result -> returnNextRecord(result))
.whenSucceeded(nextResult -> handleResult(nextResult))
.whenAborted(() -> handleAbort())
.whenFailed(e -> handleFailure(e));
```
### Benefits Over Raw CompletableFuture/CompletionStage
- Can easily set timeouts and timeouts with default values for tasks (without waiting for Java 9)
- Allows setting the executor once instead of with each chained call
- Allows a task to signal that the remainder of the chain should be canceled
- Simplified API
Note: you can easily access the managed `CompletionStage` when needed by calling `StagedFuture#unwrap()`.
### Using Stages
Stages is available from [Maven Central](http://search.maven.org/#search%7Cga%7C1%7Csoabase-stages). Use your favorite build tool and specify:
| GroupId | ArtifactId |
|---------|------------|
| io.soabase.stages | soabase-stages |
[Change Log](https://github.com/soabase/soabase-stages/blob/master/CHANGELOG.md)
#### Starting a chain
Similarly to the builders in `CompletableFuture` you start a chain using the builders in `StagedFuture`. There are syncrhonous and asynchronous builders:
- `StagedFuture.sync()` - starts a StagedFuture chain that executes tasks synchronously
- `StagedFuture.async(executor)` - starts a StagedFuture chain that executes tasks asynchronously using the given executor
- `StagedFuture.asyncPool()` - starts a StagedFuture chain that executes tasks asynchronously using the ForkJoin pool
#### Adding tasks to the chain
Tasks are added to the chain using one of the "thenIf" methods. The first task added is specified via a supplier and subsequent tasks are specified via functions that take the result of the previous task:
_Initial Task_
- `thenIf(Supplier> proc)` - Execute the given task synchronously or asynchronously depending on how the StagedFuture was built. The given task returns an optional value that indicates whether or not the next stage can execute. If `Optional.empty()` is returned, the entire StagedFuture chain is considered to be aborted and no future tasks will execute. The `StagedFuture.whenAborted()` completer will get called.
_Subsequent Tasks_
- `thenIf(Function> proc)` - If the chain has not been aborted or errored, the result of the current task is passed to this new task synchronously or asynchronously depending on how the StagedFuture was built. The given task returns an optional value that indicates whether or not the next stage can execute. If `Optional.empty()` is returned, the entire StagedFuture chain is considered to be aborted and no future tasks will execute. The `StagedFuture.whenAborted()` completer will get called.
_Timeouts_
The "then" methods (see above) can optional be assigned a timeout or a timeout and default value:
- `thenIf(X).withTimeout(Duration timeout)` - Sets a timeout for this stage's task. If the given timeout elapses before the task completes this stage is completed exceptionally with a `TimeoutException`.
- `thenIf(X).withTimeout(Duration timeout, Supplier defaultValue)` - Sets a timeout for this stage's task. If the given timeout elapses before the task completes this stage is completed with the given default value.
_Completers_
At any point in the chain, you can add handlers for successful completions, failures or aborts:
- `whenSucceeded(Consumer handler)` - if the chain completes successfully the handler is called.
- `whenSucceededYield(Function handler)` - same as `whenSucceeded()` but allows mapping the return type.
- `whenAborted(Runnable handler)` - if the chain is aborted (i.e. one of the `thenIf()` tasks returns empty) the handler is called.
- `whenFailed(Consumer handler)` - if there is an exception or failure in the chain the handler is called.
- `whenFinal(Runnable handler)` - calls the handler when the chain completes in any way (success, abort, exception, etc.).
_Chaining Other Stages_
You can include external stages into the chain:
- `thenStageIf(Function>> stage)` - executes the given stage asynchronously as the next task in the chain. If the stage returns an empty Optional the chain is aborted.
_Access The Internal CompletionStage_
You can access the internally managed `CompletionStage` via:
- `unwrap()` - returns the `CompletionStage>`.
### Tracing
The tasks submitted to StagedFuture can optionally be traced via the `Tracing` interface. The library comes with an SLF4J tracer and a `System.out` tracer. You can also write your own. Pass an instace of the tracer to the StagedFuture builder. E.g.
```java
StagedFuture.async(executor, Tracing.debug(logger)).
then(...)
...
```
#### Cancelable Tracer
The special purpose tracer, `Cancelable`, can be used to enable canceling a running chain.
It keeps track of the threads in use by the StagedFuture it is associated with. At any time you can call cancelChain(boolean) to interrupt currently running tasks and prevent new tasks from running. E.g.
```java
Cancelable cancelable = new Cancelable();
StagedFuture.async(executor, cancelable)
.thenIf(() -> worker("1"))
.thenIf(s -> hangingWorker("2"))
.thenIf(s -> worker("3"))
.thenIf(s -> worker("4"));
cancelable.cancel(true); // hangingWorker() gets interrupted
```
### Manual Wrappers
The CompletionStage wrappers that StagedFuture uses internally can be used directly without having to use `StagedFuture`.
#### Timeout
The `Timeout` class has methods that wrap `CompletionStage` adding timeouts and timeouts with default values. It roughly emulates the forthcoming Java 9 timeout features for CompletableFuture.
#### Aborted
The `Aborted` class has methods that wrap `CompletionStage` and call given handlers when the stage completes with an empty `Optional`.