io.bigdime.core.handler.HandlerManager Maven / Gradle / Ivy
/**
* Copyright (C) 2015 Stubhub.
*/
package io.bigdime.core.handler;
import java.util.LinkedHashSet;
import java.util.Stack;
import io.bigdime.alert.Logger.ALERT_CAUSE;
import io.bigdime.alert.Logger.ALERT_SEVERITY;
import io.bigdime.alert.Logger.ALERT_TYPE;
import io.bigdime.alert.LoggerFactory;
import io.bigdime.core.ActionEvent.Status;
import io.bigdime.core.Handler;
import io.bigdime.core.HandlerException;
import io.bigdime.core.commons.AdaptorLogger;
/**
* Executes the chain of handlers by invoking process method on each handler of
* the chain.
*
* This class is NOT thread safe. If more than one threads invoke execute method
* on same HandlerManager instance, the outcome could be unexpected.
*
* @author Neeraj Jain
*
*/
public class HandlerManager {
private static final AdaptorLogger logger = new AdaptorLogger(LoggerFactory.getLogger(HandlerManager.class));
private LinkedHashSet handlers;
private HandlerNode handlerNodeHead;
/**
* Name of this handler chain.
*/
private String handlerChainName;
/**
* Count of errors for this handler chain.
*/
private int errorCount;
private int handlerChainExecutionCount;
public HandlerManager(LinkedHashSet handlers, String handlerChainName) {
this.handlers = handlers;
constructHandlerList();
this.handlerChainName = handlerChainName;
state = STATE.INITIALIZED;
}
private void constructHandlerList() {
HandlerNode temp = null;
if (handlers != null) {
for (Handler h : handlers) {
HandlerNode handlerNode = new HandlerNode(h);
if (handlerNodeHead == null) {
handlerNodeHead = handlerNode;
} else {
temp.setNext(handlerNode);
}
temp = handlerNode;
}
}
}
/**
* List of handlers that this handler will manage.
*/
/**
* State transition : INITIALIZED->RUNNING->STOPPING->STOPPED
*
* @author Neeraj Jain
*
*/
enum STATE {
INITIALIZED,
/**
*
*/
RUNNING,
/**
*
*/
STOPPING,
/**
*
*/
STOPPED
}
private STATE state;
/**
* Processes an ActionEvent through the chain of handlers. If the process
* method of any handler in the chain throws an exception, an alert is
* raised and the subsequent handlers are not processed.
*
* Execute the handlers, run them in a do-while loop until the whole batch
* is done.
*
* @param event
* @return
* @throws HandlerException
* if the process method of any handler throws an exception,
* it's propagated to the caller of the execute method. Also if
* the HandlerManager is already in RUNNING state, this method
* throws an IllegalHandlerStateException indicating that the
* handler chain could not be executed.
*/
public Status execute() throws HandlerException {
logger.debug("handler chain will execute",
"_message=\"updating state to RUNNING\" handler_chain=\"{}\" current_state=\"{}\"", handlerChainName,
state);
/*
* @formatter:off
* update state to running.
* execute handlers.
* @formatter:on
*/
updateState(STATE.RUNNING);
logger.debug("handler chain will execute", "_message=\"updated state to RUNNING\" handler_chain=\"{}\"",
handlerChainName);
Status status = execute0();
logger.debug("handler chain executed successfully",
"_message=\"updating state to STOPPED\" handler_chain=\"{}\"", handlerChainName);
updateState(STATE.STOPPED);
return status;
}
/*
* @formatter:off
* Run the handler chain in a loop, to let it process the whole input batch.
* Once all the data is processed, null event will be returned and this
* do-while loop will end.
* @formatter:on
*/
private Status execute0() throws HandlerException {
Status status = Status.READY;
int iteration = 0;
String currentHandlerName = null;
// HandlerContext.get().reset();
Stack callbackHandlerIndexStack = new Stack<>();
callbackHandlerIndexStack.push(handlerNodeHead);
while (!callbackHandlerIndexStack.isEmpty()) {
try {
handlerChainExecutionCount++;
logger.debug("handler chain executing",
"handlers=\"{}\" state=\"{}\" handlerChainExecutionCount=\"{}\"", handlers, state,
handlerChainExecutionCount);
if ((state == STATE.STOPPING) || Thread.interrupted()) {
logger.alert(ALERT_TYPE.INGESTION_STOPPED, ALERT_CAUSE.SHUTDOWN_COMMAND, ALERT_SEVERITY.NORMAL,
"command received to shutdown the handler chain execution");
break;
}
iteration++;
HandlerNode tempHandlerNode = callbackHandlerIndexStack.pop();
do {
Handler handler = tempHandlerNode.getHandler();
logger.debug("handler chain executing",
"_message=\"picked up next handler\" handler_chain=\"{}\" handler_id=\"{}\" handler_name=\"{}\" total_iterations=\"{}\"",
handlerChainName, handler.getId(), handler.getName(), iteration);
currentHandlerName = handler.getName();
status = handler.process();
switch (status) {
case BACKOFF:
logger.debug("handler chain executing",
"_message=\"handler will backoff\" handler_chain=\"{}\" handler_id=\"{}\" handler_name=\"{}\" total_iterations=\"{}\"",
handlerChainName, handler.getId(), handler.getName(), iteration);
return status;
case CALLBACK:
logger.debug("handler chain executing",
"_message=\"pushing handler node stack\" handler_chain=\"{}\" handler_id=\"{}\" handler_name=\"{}\" total_iterations=\"{}\"",
handlerChainName, handler.getId(), handler.getName(), iteration);
callbackHandlerIndexStack.push(tempHandlerNode);
break;
case READY:
logger.debug("handler chain executing",
"_message=\"handler ready\" handler_chain=\"{}\" handler_id=\"{}\" handler_name=\"{}\" total_iterations=\"{}\"",
handlerChainName, handler.getId(), handler.getName(), iteration);
break;
default:
// do nothing
}
tempHandlerNode = tempHandlerNode.getNext();
} while (tempHandlerNode != null);
}
catch (final HandlerException ex) {
errorCount++;
logger.alert(ALERT_TYPE.INGESTION_STOPPED, ALERT_CAUSE.APPLICATION_INTERNAL_ERROR,
ALERT_SEVERITY.BLOCKER,
"_message=\"one of the handlers({}) in the chain threw an exception, this loop of handlers will be stopped\" exception=\"{}\" error_count=\"{}\" handlerChainExecutionCount=\"{}\"",
currentHandlerName, ex.getMessage(), errorCount, handlerChainExecutionCount);
logger.info("handler chain handling exception", "invoking handleException");
executeHandleException();
stopOnError();
logger.info("handler chain handling exception", "invoked handleException");
throw ex;
} catch (Exception ex) {
errorCount++;
logger.alert(ALERT_TYPE.INGESTION_STOPPED, ALERT_CAUSE.APPLICATION_INTERNAL_ERROR,
ALERT_SEVERITY.BLOCKER,
"_message=\"one of the handlers({}) in the chain threw an unknown exception, this loop of handlers will be stopped\" exception=\"{}\" error_count=\"{}\" handlerChainExecutionCount=\"{}\"",
currentHandlerName, ex.getMessage(), errorCount, handlerChainExecutionCount);
logger.info("handler chain handling exception", "invoking handleException");
executeHandleException();
stopOnError();
logger.info("handler chain handling exception", "invoked handleException");
throw new HandlerException(ex.getMessage(), ex);
}
}
// while (true);
return status;
}
private void stopOnError() throws IllegalHandlerStateException {
logger.warn("handler chain in error",
"_message=\"stopping the handler chain due to an error\" handler_chain=\"{}\" state=\"{}\"",
handlerChainName, state);
updateState(STATE.STOPPING);
updateState(STATE.STOPPED);
}
private void executeHandleException() {
Stack callbackHandlerIndexStack = new Stack<>();
callbackHandlerIndexStack.push(handlerNodeHead);
if (handlerNodeHead == null)
return;
HandlerNode tempHandlerNode = handlerNodeHead;
do {
Handler handler = tempHandlerNode.getHandler();
logger.debug("handler chain handling exception",
"_message=\"picked up next handler\" handler_chain=\"{}\" handler_id=\"{}\" handler_name=\"{}\"",
handlerChainName, handler.getId(), handler.getName());
tempHandlerNode.getHandler().handleException();
tempHandlerNode = tempHandlerNode.getNext();
} while (tempHandlerNode != null);
}
/**
* @formatter:on
* Execute steps to shutdown the handler manager.
* @throws IllegalHandlerStateException
*/
public void shutdown() throws IllegalHandlerStateException {
logger.info("handler chain shutting down",
"_message=\"received a command to shutdown the handler chain\" handler_chain=\"{}\" state=\"{}\"",
handlerChainName, state);
updateState(STATE.STOPPING);
}
private boolean updateState(STATE state) throws IllegalHandlerStateException {
if (state == STATE.RUNNING) {
return updateStateToRunning();
} else if (state == STATE.STOPPING) {
return updateStateToStopping();
} else if (state == STATE.STOPPED) {
return updateStateToStopped();
}
return false;
}
private boolean updateStateToRunning() throws IllegalHandlerStateException {
if (((state == STATE.INITIALIZED)) || (state == STATE.STOPPED)) {
state = STATE.RUNNING;
logger.debug("handler chain will execute, updating state",
"handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"", handlerChainName, state,
STATE.RUNNING);
return true;
} else {
logger.alert(ALERT_TYPE.INGESTION_FAILED, ALERT_CAUSE.APPLICATION_INTERNAL_ERROR, ALERT_SEVERITY.BLOCKER,
"_message=\"handler chain start failed\" handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"",
handlerChainName, state, STATE.RUNNING);
throw new IllegalHandlerStateException("handler can't come to running state from " + state);
}
}
private boolean updateStateToStopping() {
if (state == STATE.RUNNING) {
logger.debug("handler chain stopping", "handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"",
handlerChainName, state, STATE.STOPPING);
state = STATE.STOPPING;
return true;
} else if (state == STATE.STOPPED) {
logger.warn("handler chain stopping, no action taken",
"handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"", handlerChainName, state,
STATE.STOPPING);
return false;
} else {
logger.warn("handler chain stopping, no action taken",
"handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"", handlerChainName, state,
STATE.STOPPING);
return false;
}
}
private boolean updateStateToStopped() {
logger.debug("handler chain executed", "handler_chain=\"{}\" current_state=\"{}\" new_state=\"{}\"",
handlerChainName, state, STATE.STOPPED);
if (state != STATE.STOPPED) {
state = STATE.STOPPED;
return true;
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy