-
Notifications
You must be signed in to change notification settings - Fork 6
The Red Future Module
The RedFuture module provides an implementation of a Future
that simplify and extend the capabilities of both java Future and Guava ListenableFuture. On top of the RedFuture, the module introduces a new way of managing and timing various result related callbacks.
The RedFuture module contains 3 basic models:
-
RedFuture
- A void future. -
RedFutureOf<T>
- A future of typeT
. -
RedFutureHub
- A container providing, collecting, manging and timingRedFuture
s.
A RedFuture
is a future of void
result. For example, async DB write will not return any value, but only a way to determine when the operation has completed, and if it failed - what was the cause.
A RedFuture
instance has two permission levels. The consumer level, which allows adding different callbacks and querying, and the producer level, which in addition, allows marking the future as successfully completed, or failed.
The main interface of a RedFuture
allows registering on various event callbacks. RedFuture
introduces 3 event types:
- Success - triggered if the future is marked at the producer level as successfully completed.
- Failure - triggered if the future is marked at the producer level as failed.
- Finish - triggered after each of the above cases.
Each callback registration optionally allows specifying an Executor to handle the callback. If an executor is not specified, then the callback will be executed by the thread marking the future as completed (the producer thread).
For example:
RedFuture future = someTask();
future.addSuccessCallback(() -> print("success!"));
future.addFailureCallback(throwable -> print("failed due to " + throwable.getMessage()));
future.addFinallyCallback(() -> print("finished!"));
Additionally, the RedFuture
interface allows for blocking querying through waitForCompletion
method, and non-blocking querying through isResolved
method.
More info at the API reference: https://avivcarmis.github.io/java-red/apidocs/latest/io/github/avivcarmis/javared/future/RedFuture.html
A producer level RedFuture
is available under OpenRedFuture
and instantiated through RedFuture.future()
. A producer level instance introduces several additional method for marking the future as completed. This operation is called resolving.
Marking the future as successfully completed is done through the simple future.resolve()
. Marking it as failed is done through future.fail(throwable)
.
Example:
public static Future saveToDB() {
OpenRedFuture future = RedFuture.future();
try {
performSave().whenFinished(status -> {
if (status.success()) {
future.resolve();
} else {
future.fail(status.getCause());
}
});
} catch (Throwable t) {
future.fail(t);
}
return future;
}
In addition, a new instance of OpenRedFuture
can follow the status of another RedFuture
or ListenableFuture
by using one of the follow
methods. In this case, the Success, Failure and Finish events of the given future will be propagated to the current one, even if they already occurred.
More info at the API reference: https://avivcarmis.github.io/java-red/apidocs/latest/io/github/avivcarmis/javared/future/OpenRedFuture.html
The RedFutureOf<T>
is a RedFuture
that on successful completion, produces a value of T
. The RedFutureOf<T>
interface inherits the RedFuture
interface, this means that each RedFutureOf
instance is essentially a RedFuture
which i can wait for it's success, or get a cause on failure. Additionally, on success event, i can get it's produced value.
Like RedFuture
, a RedFutureOf
instance has two permission levels. The consumer level, which allows adding different callbacks and querying, and the producer level, which in addition, allows marking the future as successfully completed, or failed.
The main interface of a RedFutureOf
allows registering on various event callbacks. RedFuture
introduces 3 event types:
- Success - triggered if the future is marked at the producer level as successfully completed.
- Failure - triggered if the future is marked at the producer level as failed.
- Finish - triggered after each of the above cases.
Each callback registration optionally allows specifying an Executor to handle the callback. If an executor is not specified, then the callback will be executed by the thread marking the future as completed (the producer thread).
For example:
RedFutureOf<String> future = someTask();
future.addSuccessCallback(stringResult -> print("success, result = " + stringResult));
// inherited from RedFuture
future.addSuccessCallback(() -> print("success!"));
future.addFailureCallback(throwable -> print("failed due to " + throwable.getMessage()));
future.addFinallyCallback(() -> print("finished!"));
Additionally, the RedFutureOf
interface allows for blocking querying through waitAndGet
method, and non-blocking querying through tryGet
method.
More info at the API reference: https://avivcarmis.github.io/java-red/apidocs/latest/io/github/avivcarmis/javared/future/RedFutureOf.html
A producer level RedFutureOf
is available under OpenRedFutureOf
and instantiated through RedFuture.futureOf()
. A producer level instance introduces several additional method for marking the future as completed. This operation is called resolving.
Marking the future as successfully completed is done through the simple future.resolve(T value)
. Marking it as failed is done through inherited future.fail(throwable)
.
Example:
public static FutureOf<String> readFromDB() {
OpenRedFutureOf<String> future = RedFuture.futureOf();
try {
performRead().whenFinished(status -> {
if (status.success()) {
future.resolve(status.result());
} else {
future.fail(status.getCause());
}
});
} catch (Throwable t) {
future.fail(t);
}
return future;
}
More info at the API reference: https://avivcarmis.github.io/java-red/apidocs/latest/io/github/avivcarmis/javared/future/OpenRedFutureOf.html
A RedFutureHub
can be used to track and manage numerous future instances. A RedFutureHub
has two main capabilities:
- Adding future to be tacked.
- Reducing all tracked futures to a single united future.
A new RedFutureHub
is instantiated through RedFuture.hub()
.
Adding futures to a RedFutureHub
instance may be done through providing new futures, or adopting external futures.
Providing futures should be done when we need to track a new producer level RedFuture
or RedFutureOf<T>
, that it's our responsibility to mark as completed.
Adopting futures should be done when we need to track RedFuture
, RedFutureOf
or ListenableFuture
that we've received at the consumer level.
Example:
RedFutureHub hub = RedFuture.hub(); // new hub
RedFuture consumerFuture = performTask(); // acquire some consumer level future
hub.adoptFuture(someFuture); // adopt it
OpenRedFuture producerFuture = hub.provideFuture(); // provide producer level future
// do something with it:
readFromDB().whenFinished(status -> {
if (status.success()) {
producerFuture.resolve(status.result());
} else {
producerFuture.fail(status.getCause());
}
});
RedFutureHub
introduces 3 types of future union:
- Optimistic.
- Pessimistic.
- Cautious.
An optimistic union is acquired by calling hub.uniteOptimistically()
- this method returns a RedFuture
that will be triggered with success if and when all of the hub tracked futures are successfully completed, and will be triggered with failure if and when the first of the hub tracked futures is failed. It expects all of the tracked futures to successfully complete - hence it's name.
A pessimistic union is acquired by calling hub.unitePessimistically()
- this method returns a RedFuture
that will be triggered with success when all of the hub tracked futures are completed (either with success or failure). It expects all of the tracked futures to finish - hence it's name. This future will not fail.
A cautious union is acquired by calling hub.uniteCautiously()
- this method returns a RedFuture
that will be triggered with success if and when all of the hub tracked futures are successfully completed, and will be triggered with failure if and when all of the hub tracked futures are completed, and one or more of them has failed. As opposed to optimistic union, it will not fail on the first failure, but instead, it will first wait for all the futures to complete.
RedFuture
can also provide instances of RedFuture
and RedFutureOf
that are initially successfully completed or failed though:
resolved()
resolvedOf(T value)
failed(Throwable t)
failedOf(Throwable t)
Additionally, it provides a simple method for converting java Future<T>
and Guava ListenableFuture<T>
to RedFutureOf<T>
through the convert
methods.
More info at the API reference: https://avivcarmis.github.io/java-red/apidocs/latest/io/github/avivcarmis/javared/future/RedFuture.html
Note that everything under the future module is instantiated statically though RedFuture
class. This means that you don't have to look too much for what you need, just type RedFuture.
and see what your options are.