net.termer.twine.utils.CallbackChain Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of twine Show documentation
Show all versions of twine Show documentation
A fully featured application server built for boilerplate-less Vert.x web applications
package net.termer.twine.utils;
import java.util.ArrayList;
import java.util.HashMap;
import static net.termer.twine.Twine.*;
/**
* Utility class for chaining callbacks rather than nesting them.
* Note that most of this class's functionality is found native in Vert.x's Futures.
* The major factor that sets this apart from using Future composition/coordination is that this allows callbacks to skip to the end with .end().
* Additionally, callbacks can share data with eachother easier.
* @author termer
* @since 2.0
*/
public class CallbackChain {
private Throwable _cause = null;
private T _result = null;
private final HashMap _data;
private final ArrayList> _callbacks = new ArrayList<>();
private CallbackAction _endHandler;
private int _execIndex = 0;
/**
* Creates a new Callback object
* @since 2.0
*/
public CallbackChain() {
_data = new HashMap<>();
}
/**
* Creates a new Callback object
* @param data The initial data to be shared with
* @since 2.0
*/
public CallbackChain(HashMap data) {
_data = data;
}
/**
* Adds a callback
* @param action The callback
* @return This, to be used fluently
* @since 2.0
*/
public CallbackChain then(CallbackAction action) {
_callbacks.add(action);
return this;
}
/**
* Returns data passed by other callbacks
* @return Data passed by other callbacks
* @since 2.0
*/
public HashMap data() {
return _data;
}
/**
* Returns the value stored in data() with the specified key, or null if it does not exist
* @param key The key corresponding to the value to retrieve
* @return The value stored in data() with the specified key, or null if it does not exist
* @since 2.0
*/
public Object get(String key) {
return _data.get(key);
}
/**
* Sets the value for the specified key in data()
* @param key The key
* @param value The value
* @return This, to be used fluently
*/
public CallbackChain set(String key, Object value) {
_data.put(key, value);
return this;
}
/**
* Passes a Throwable error and executes this callback's error handler.
* Calling fail().next() is permitted, so if you do not want the next callback to be executed, be sure to call end() in the failure callback.
* @param error The error that was caught
* @since 2.0
*/
public void fail(Throwable error) {
_cause = error;
if(_endHandler != null) {
_endHandler.run(this);
}
}
/**
* Returns the value passed by an end(...) in a callback. Null if none was passed
* @return The value passed by an end(...) in a callback. Null if none was passed
* @since 2.0
*/
public T result() {
return _result;
}
/**
* Returns the Throwable passed by a fail() call in a callback. Null if none was passed
* @return The Throwable generated by a fail()
* @since 2.0
*/
public Throwable cause() {
return _cause;
}
/**
* Returns whether this callback chain was not terminated by an error
* @return Whether this callback chain was not terminated by an error
*/
public boolean succeeded() {
return _cause == null;
}
/**
* Returns whether this callback chain was terminated by an error
* @return whether this callback chain was not terminated by an error
*/
public boolean failed() {
return _cause != null;
}
/**
* Executes the next callback, if any
* @since 2.0
*/
public void next() {
_execIndex++;
if(_execIndex < _callbacks.size()) {
CallbackAction cb = _callbacks.get(_execIndex);
try {
cb.run(this);
} catch(Exception e) {
fail(e);
}
} else if(_endHandler != null) {
try {
_endHandler.run(this);
} catch(Exception e) {
logger().error("Uncaught exception when running end handler:");
e.printStackTrace();
}
}
}
/**
* Clears all callbacks and ends all further execution, then passes the provided value to the final handler
* Should be used on the last callback in chain, as it signifies that all callbacks are finished.
* @param result The result value to pass to the end handler
* @since 2.0
*/
public void end(T result) {
_cause = null;
_result = result;
_callbacks.clear();
_execIndex = 0;
if(_endHandler != null) {
try {
_endHandler.run(this);
} catch(Exception e) {
logger().error("Uncaught exception when running end handler:");
e.printStackTrace();
}
}
}
/**
* Clears all callbacks and ends all further execution without passing a value
* Should be used on the last callback in chain, as it signifies that all callbacks are finished.
* @since 2.0
*/
public void end() {
end(null);
}
/**
* Sets the end handler for this callback chain
* @param callback The handler to be run when the chain is complete
* @return This, to be used fluently
* @since 2.0
*/
public CallbackChain onEnd(CallbackAction callback) {
_endHandler = callback;
return this;
}
/**
* Executes all callbacks
* @since 2.0
*/
public void execute() {
if(_callbacks.size() > 0) {
try {
_callbacks.get(0).run(this);
} catch(Exception e) {
fail(e);
}
}
}
/**
* Simple interface for callbacks
* @author termer
* @since 2.0
*/
public interface CallbackAction {
/**
* Performs an action
* @param cb The Callback object that's running this callback
* @since 2.0
*/
void run(CallbackChain cb);
}
}