Skip to content

The Red Future Module

Aviv C edited this page Jul 1, 2017 · 6 revisions

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 Module Features

The RedFuture module contains 3 basic models:

  1. RedFuture - A void future.
  2. RedFutureOf<T> - A future of type T.
  3. RedFutureHub - A container providing, collecting, manging and timing RedFutures.

RedFuture

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.

Consumer Level

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

Producer Level

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

RedFutureOf<T>

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.

Consumer Level

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

Producer Level

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

RedFutureHub

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

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());
    }
});
Reducing

RedFutureHub introduces 3 types of future union:

  1. Optimistic.
  2. Pessimistic.
  3. 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.

More Stuff

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.