All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.termer.twine.utils.CallbackChain Maven / Gradle / Ivy

Go to download

A fully featured application server built for boilerplate-less Vert.x web applications

There is a newer version: 2.1b
Show newest version
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);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy