/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.execution.internal;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.component.execution.api.ComponentState;
import de.rcenvironment.core.component.execution.api.ConsoleRow;
import de.rcenvironment.core.component.execution.api.ExecutionControllerException;
import de.rcenvironment.core.component.execution.api.WorkflowExecutionControllerCallback;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
import de.rcenvironment.core.utils.incubator.DebugSettings;
/**
* {@link WorkflowExecutionControllerCallback} which delegates the method calls if the workflow execution controller is reachable. Prevent
* constantly recurring failures and stack traces.
*
* @author Doreen Seider
*/
public class WorkflowExecutionControllerBridgeDelegator implements WorkflowExecutionControllerCallback {
private static final Log LOG = LogFactory.getLog(WorkflowExecutionControllerBridgeDelegator.class);
private static final boolean VERBOSE_LOGGING = DebugSettings.getVerboseLoggingEnabled(WorkflowExecutionControllerBridgeDelegator.class);
private static final int MAX_CALLBACK_FAILURES = 5;
private final ComponentExecutionRelatedInstances compExeRelatedInstances;
private final String wfExeId;
private AtomicInteger wfExeCtrlCallbackFailureCount = new AtomicInteger(0);
protected WorkflowExecutionControllerBridgeDelegator(ComponentExecutionRelatedInstances compExeRelatedInstances) {
this.compExeRelatedInstances = compExeRelatedInstances;
this.wfExeId = compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier();
}
protected boolean isWorkflowControllerReachable() {
return wfExeCtrlCallbackFailureCount.get() < MAX_CALLBACK_FAILURES;
}
@Override
public synchronized void processConsoleRows(final ConsoleRow[] consoleRows) {
new WorkflowExecutionControllerCallbackCheckingReachability() {
@Override
protected void call() throws ExecutionControllerException, RemoteOperationException {
compExeRelatedInstances.wfExeCtrlBridge.onConsoleRowsProcessed(wfExeId, consoleRows);
}
}.callback();
}
@Override
public synchronized void onComponentStateChanged(final String compExeId, final ComponentState newState, final Integer count,
final String countOnResets) {
onComponentStateChanged(compExeId, newState, count, countOnResets, null);
}
@Override
public synchronized void onComponentStateChanged(final String compExeId, final ComponentState newState, final Integer count,
final String countOnResets, final String errId) {
onComponentStateChanged(compExeId, newState, count, countOnResets, errId, null);
}
@Override
public synchronized void onComponentStateChanged(final String compExeId, final ComponentState newState, final Integer count,
final String countOnResets, final String errId, final String errMessage) {
new WorkflowExecutionControllerCallbackCheckingReachability() {
@Override
protected void call() throws ExecutionControllerException, RemoteOperationException {
if (errId == null) {
compExeRelatedInstances.wfExeCtrlBridge.onComponentStateChanged(wfExeId, compExeId, newState, count, countOnResets);
} else if (errMessage == null) {
compExeRelatedInstances.wfExeCtrlBridge.onComponentStateChanged(wfExeId, compExeId, newState, count, countOnResets,
errId);
} else {
compExeRelatedInstances.wfExeCtrlBridge.onComponentStateChanged(wfExeId, compExeId, newState, count, countOnResets,
errId, errMessage);
}
}
}.callback();
}
@Override
public synchronized void onInputProcessed(final String serializedEndpointDatum) {
if (isWorkflowControllerReachable()) {
try {
compExeRelatedInstances.wfExeCtrlBridge.onInputProcessed(wfExeId, serializedEndpointDatum);
handleWorkflowControllerCallbackSuccess();
} catch (ExecutionControllerException | RemoteOperationException e) {
handleWorkflowControllerCallbackFailure(e);
}
}
}
@Override
public synchronized void onComponentHeartbeatReceived(String compExeId) {
if (isWorkflowControllerReachable()) {
try {
if (VERBOSE_LOGGING) {
LOG.debug(StringUtils.format("Component '%s' (%s) is sending heartbeat to workflow controller '%s' (%s)",
compExeRelatedInstances.compExeCtx.getInstanceName(),
compExeRelatedInstances.compExeCtx.getExecutionIdentifier(),
compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(),
compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier()));
}
compExeRelatedInstances.wfExeCtrlBridge.onComponentHeartbeatReceived(wfExeId, compExeId);
handleWorkflowControllerCallbackSuccess();
} catch (ExecutionControllerException | RemoteOperationException e) {
handleWorkflowControllerCallbackFailure(e);
}
}
}
private void handleWorkflowControllerCallbackSuccess() {
if (wfExeCtrlCallbackFailureCount.get() > 0) {
LOG.debug(StringUtils.format("Callback from local component '%s' (%s) to workflow controller '%s' (%s)"
+ " succeeded again", compExeRelatedInstances.compExeCtx.getInstanceName(),
compExeRelatedInstances.compExeCtx.getExecutionIdentifier(),
compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(),
compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier()));
}
wfExeCtrlCallbackFailureCount.set(0);
}
private void handleWorkflowControllerCallbackFailure(Throwable e) {
int failureCount = wfExeCtrlCallbackFailureCount.incrementAndGet();
String message =
StringUtils.format("Callback from local component '%s' (%s) to workflow controller '%s' (%s) failed",
compExeRelatedInstances.compExeCtx.getInstanceName(),
compExeRelatedInstances.compExeCtx.getExecutionIdentifier(),
compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(),
compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier());
registerCallbackFailureEvent(message, failureCount, e);
}
private void registerCallbackFailureEvent(String message, int failureCount, Throwable cause) {
if (failureCount >= MAX_CALLBACK_FAILURES) {
LOG.error(message + "; maximum number of workflow controller callback failures (" + MAX_CALLBACK_FAILURES
+ ") exceeded, considering the workflow controller unreachable; last cause: " + cause.toString());
compExeRelatedInstances.compStateMachine.postEvent(new ComponentStateMachineEvent(
ComponentStateMachineEventType.WF_CRTL_CALLBACK_FAILED, cause));
} else {
LOG.warn(message + "; failure count is " + failureCount + " (threshold: " + MAX_CALLBACK_FAILURES + "); cause: "
+ cause.toString());
}
}
/**
* Executes callbacks to the workflow execution controller if it is known as reachable.
*
* @author Doreen Seider
*/
private abstract class WorkflowExecutionControllerCallbackCheckingReachability {
protected void callback() {
if (isWorkflowControllerReachable()) {
try {
call();
} catch (ExecutionControllerException | RemoteOperationException e) {
wfExeCtrlCallbackFailureCount.set(MAX_CALLBACK_FAILURES);
compExeRelatedInstances.compStateMachine
.postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.WF_CRTL_CALLBACK_FAILED, e));
}
}
}
protected abstract void call() throws ExecutionControllerException, RemoteOperationException;
}
}