package com.zillabyte.motherbrain.flow.operations;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import com.zillabyte.motherbrain.coordination.CoordinationException;
import com.zillabyte.motherbrain.flow.FlowStateException;
import com.zillabyte.motherbrain.flow.MapTuple;
import com.zillabyte.motherbrain.flow.StateMachineException;
import com.zillabyte.motherbrain.flow.StateMachineHelper;
import com.zillabyte.motherbrain.flow.collectors.OutputCollector;
import com.zillabyte.motherbrain.flow.config.OperationConfig;
import com.zillabyte.motherbrain.flow.error.strategies.FakeLocalException;
import com.zillabyte.motherbrain.utils.Log4jWrapper;
public abstract class Function extends Operation implements ProcessableOperation {
private static final long serialVersionUID = 2349633136720169473L;
protected long _lastProcess;
protected FunctionState _state = FunctionState.INITIAL;
private Log4jWrapper _log = Log4jWrapper.create(Function.class, this);
/***
*
* @param name
*/
public Function(String name) {
super(name);
}
public Function(String name, OperationConfig config) {
super(name,config);
}
/***
*
* @param t
* @param c
* @throws InterruptedException
* @throws ExecutionException
* @throws FakeLocalException
*/
protected abstract void process(MapTuple t, OutputCollector c) throws OperationException, InterruptedException;
/***
*
* @param t
* @param c
* @throws InterruptedException
* @throws FakeLocalException
* @throws OperationDeadException
*/
public void handleProcess(final MapTuple t, final OutputCollector c) throws InterruptedException, OperationException, FakeLocalException {
try {
final FunctionState currentState = _state;
switch(currentState) {
case STARTED:
case IDLE:
try {
transitionToState(FunctionState.ACTIVE.toString(), true);
} catch (StateMachineException | TimeoutException e) {
throw new OperationException(Function.this, e);
}
// trickle!
case PAUSING:
case SUSPECT:
// When we're in loop_error, we might as well keep consuming...restarting the instance will likely just produce more loop errors anyway
case ACTIVE:
try {
heartbeatErrorCheck_ThreadUnsafe();
} catch(Exception e) {
handleLoopError(e);
}
// Init
incLoop();
incConsumed();
markBeginActivity();
try {
// Logging
if (_ipcLogBackoff.tick()) {
_operationLogger.writeLog("[sampled #" + _ipcLogBackoff.counter() +"] receiving tuple: " + t, OperationLogger.LogPriority.IPC);
}
// Make sure we're alive..
if (isAlive() == false) {
throw new OperationException(Function.this, "The operation is not alive.");
}
// process
c.resetCounter();
process(t, c);
} catch(InterruptedException e) {
// Do nothing...
} catch(Exception e) {
handleLoopError(e);
} finally {
markEndActivity();
}
incEmit(c.getCounter());
return;
case STARTING:
case INITIAL:
case PAUSED:
// All these states, we're just waiting for somebody to change us...
heartbeatErrorCheck_ThreadUnsafe();
return;
case KILLING:
case KILLED:
case ERROR:
// New: Do nothing, this will trigger the flow to kill us.
return;
default:
// This should never be reached.
throw new FlowStateException(Function.this, "don't know how to handle state: " + currentState);
}
} catch(InterruptedException e) {
// Continue processing...
} catch (FakeLocalException e) {
((FakeLocalException) e).printAndWait();
} catch (Exception e) {
handleFatalError(e);
}
}
@Override
public void handleIdleDetected() throws InterruptedException, OperationException {
try {
if (_state == FunctionState.PAUSING) {
transitionToState(SinkState.PAUSED.toString(), true);
}
else if (_state == FunctionState.ACTIVE || _state == FunctionState.SUSPECT|| _state == FunctionState.STARTED) {
transitionToState(FunctionState.IDLE.toString(), true);
}
} catch (StateMachineException | TimeoutException | CoordinationException e) {
throw new OperationException(this, e);
}
}
@Override
public void prePrepare() throws InterruptedException, OperationException {
try {
transitionToState(FunctionState.STARTING.toString(), true);
} catch (StateMachineException | CoordinationException | TimeoutException e) {
throw new OperationException(this, e);
}
}
@Override
public void postPrepare() throws InterruptedException, OperationException {
try {
transitionToState(FunctionState.STARTED.toString(), true);
} catch (StateMachineException | CoordinationException | TimeoutException e) {
throw new OperationException(this, e);
}
}
/***
*
* @throws OperationException
*/
@Override
public void handlePause() throws OperationException {
// Function pause when they IDLE during the PAUSING state
try {
if(!getState().equalsIgnoreCase("ERROR")) transitionToState("PAUSING");
} catch (StateMachineException | CoordinationException | TimeoutException e) {
_log.warn("An error occured while trying to resume "+e.getMessage());
}
}
/**
* @throws OperationException
*/
@Override
public void handleResume() throws OperationException {
// Resume the operation
try {
if(!getState().equalsIgnoreCase("ERROR")) transitionToState("ACTIVE");
} catch (StateMachineException | CoordinationException | TimeoutException e) {
_log.warn("An error occured while trying to resume "+e.getMessage());
}
}
@Override
public String type() {
return "each";
}
/***
*
* @param newState
* @param transactional
* @throws CoordinationException
* @throws TimeoutException
* @throws StateMachineException
*/
public synchronized void transitionToState(FunctionState newState, boolean transactional) throws CoordinationException, TimeoutException, StateMachineException {
FunctionState oldState = _state;
_state = StateMachineHelper.transition(_state, newState);
if(_state != oldState) notifyOfNewState(newState.toString(), transactional);
}
@Override
public synchronized void transitionToState(String newState, boolean transactional) throws CoordinationException, TimeoutException, StateMachineException {
transitionToState(FunctionState.valueOf(newState), transactional);
}
@Override
public String getState() {
return _state.toString();
}
//
// /***
// *
// */
// @Override
// public void onThisBatchCompleted(final Object batchId) {
// try {
// transitionToState("IDLE"); // we 'force' a fast idle because all upstream operations should be done.
// } catch (StateMachineException | CoordinationException | TimeoutException e) {
// throw new OperationException(this, e);
// }
// }
}