/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.execution.internal; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.api.ComponentException; import de.rcenvironment.core.component.api.ComponentUtils; import de.rcenvironment.core.component.api.LoopComponentConstants; import de.rcenvironment.core.component.execution.api.Component; import de.rcenvironment.core.component.execution.api.ComponentExecutionException; import de.rcenvironment.core.component.execution.api.ComponentExecutionService; import de.rcenvironment.core.component.execution.api.ComponentState; import de.rcenvironment.core.component.execution.api.ConsoleRow.WorkflowLifecyleEventType; import de.rcenvironment.core.component.execution.api.ExecutionControllerException; import de.rcenvironment.core.component.execution.api.WorkflowGraphHop; import de.rcenvironment.core.component.execution.impl.ComponentContextImpl; import de.rcenvironment.core.component.execution.impl.ComponentExecutionContextImpl; import de.rcenvironment.core.component.execution.internal.ComponentExecutor.ComponentExecutionType; import de.rcenvironment.core.component.model.api.ComponentInterface; import de.rcenvironment.core.component.model.endpoint.api.EndpointDatum; import de.rcenvironment.core.component.model.endpoint.api.EndpointDescription; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.datamodel.api.FinalComponentRunState; import de.rcenvironment.core.datamodel.api.FinalComponentState; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.LogUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.core.utils.common.TempFileServiceAccess; import de.rcenvironment.core.utils.common.rpc.RemoteOperationException; import de.rcenvironment.core.utils.incubator.AbstractFixedTransitionsStateMachine; import de.rcenvironment.core.utils.incubator.AbstractStateMachine; import de.rcenvironment.core.utils.incubator.DebugSettings; import de.rcenvironment.core.utils.incubator.StateChangeException; import de.rcenvironment.toolkit.modules.concurrency.api.AsyncTaskService; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; import de.rcenvironment.toolkit.utils.common.IdGenerator; /** * Component-specific implementation of {@link AbstractStateMachine}. * * @author Doreen Seider * @author Robert Mischke (tweaked error handling) */ public class ComponentStateMachine extends AbstractFixedTransitionsStateMachine<ComponentState, ComponentStateMachineEvent> { private static final Log LOG = LogFactory.getLog(ComponentStateMachine.class); private static final boolean VERBOSE_LOGGING = DebugSettings.getVerboseLoggingEnabled(ComponentStateMachine.class); private static final int HEARTBEAT_SEND_INTERVAL_MSEC = 30 * 1000; private static final ComponentState[][] VALID_COMPONENT_STATE_TRANSITIONS = new ComponentState[][] { // normal life cycle { ComponentState.INIT, ComponentState.PREPARING }, { ComponentState.PREPARING, ComponentState.PREPARED }, { ComponentState.PREPARED, ComponentState.WAITING_FOR_RESOURCES }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.STARTING }, { ComponentState.STARTING, ComponentState.IDLING }, { ComponentState.STARTING, ComponentState.WAITING_FOR_APPROVAL }, { ComponentState.IDLING, ComponentState.WAITING_FOR_RESOURCES }, { ComponentState.IDLING_AFTER_RESET, ComponentState.WAITING_FOR_RESOURCES }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.PROCESSING_INPUTS }, { ComponentState.PROCESSING_INPUTS, ComponentState.IDLING }, { ComponentState.PROCESSING_INPUTS, ComponentState.WAITING_FOR_APPROVAL }, { ComponentState.WAITING_FOR_APPROVAL, ComponentState.IDLING }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.RESETTING }, { ComponentState.IDLING, ComponentState.IDLING }, { ComponentState.RESETTING, ComponentState.IDLING_AFTER_RESET }, { ComponentState.IDLING, ComponentState.TEARING_DOWN }, { ComponentState.IDLING_AFTER_RESET, ComponentState.IDLING_AFTER_RESET }, { ComponentState.IDLING_AFTER_RESET, ComponentState.TEARING_DOWN }, { ComponentState.TEARING_DOWN, ComponentState.FINISHED }, { ComponentState.TEARING_DOWN, ComponentState.FINISHED_WITHOUT_EXECUTION }, { ComponentState.FINISHED_WITHOUT_EXECUTION, ComponentState.DISPOSING }, { ComponentState.FINISHED, ComponentState.DISPOSING }, { ComponentState.DISPOSING, ComponentState.DISPOSED }, // canceling { ComponentState.PREPARING, ComponentState.CANCELLING }, { ComponentState.PREPARED, ComponentState.CANCELLING }, { ComponentState.STARTING, ComponentState.CANCELLING }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.CANCELLING }, { ComponentState.PROCESSING_INPUTS, ComponentState.CANCELLING }, { ComponentState.WAITING_FOR_APPROVAL, ComponentState.CANCELLING }, { ComponentState.IDLING, ComponentState.CANCELLING }, { ComponentState.IDLING_AFTER_RESET, ComponentState.CANCELLING }, { ComponentState.RESETTING, ComponentState.CANCELLING }, { ComponentState.STARTING, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.PROCESSING_INPUTS, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.IDLING, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.IDLING_AFTER_RESET, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.RESETTING, ComponentState.CANCELLING_AFTER_FAILURE }, { ComponentState.CANCELLING, ComponentState.TEARING_DOWN }, { ComponentState.CANCELLING_AFTER_FAILURE, ComponentState.TEARING_DOWN }, { ComponentState.TEARING_DOWN, ComponentState.CANCELED }, { ComponentState.CANCELED, ComponentState.DISPOSING }, // pausing { ComponentState.PREPARING, ComponentState.PAUSING }, { ComponentState.PREPARED, ComponentState.PAUSING }, { ComponentState.STARTING, ComponentState.PAUSING }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.PAUSING }, { ComponentState.PROCESSING_INPUTS, ComponentState.PAUSING }, { ComponentState.WAITING_FOR_APPROVAL, ComponentState.PAUSING }, { ComponentState.IDLING, ComponentState.PAUSING }, { ComponentState.IDLING_AFTER_RESET, ComponentState.PAUSING }, { ComponentState.RESETTING, ComponentState.PAUSING }, { ComponentState.PAUSING, ComponentState.PAUSED }, { ComponentState.PAUSING, ComponentState.PROCESSING_INPUTS }, { ComponentState.PAUSED, ComponentState.CANCELLING }, { ComponentState.PAUSED, ComponentState.RESUMING }, { ComponentState.RESUMING, ComponentState.PREPARING }, { ComponentState.RESUMING, ComponentState.PREPARED }, { ComponentState.RESUMING, ComponentState.WAITING_FOR_RESOURCES }, { ComponentState.RESUMING, ComponentState.IDLING }, { ComponentState.RESUMING, ComponentState.IDLING_AFTER_RESET }, // failures { ComponentState.PREPARING, ComponentState.TEARING_DOWN }, { ComponentState.PREPARED, ComponentState.TEARING_DOWN }, { ComponentState.STARTING, ComponentState.TEARING_DOWN }, { ComponentState.WAITING_FOR_RESOURCES, ComponentState.TEARING_DOWN }, { ComponentState.PROCESSING_INPUTS, ComponentState.TEARING_DOWN }, { ComponentState.WAITING_FOR_APPROVAL, ComponentState.TEARING_DOWN }, { ComponentState.PAUSING, ComponentState.TEARING_DOWN }, { ComponentState.PAUSED, ComponentState.TEARING_DOWN }, { ComponentState.RESUMING, ComponentState.TEARING_DOWN }, { ComponentState.IDLING, ComponentState.TEARING_DOWN }, { ComponentState.IDLING_AFTER_RESET, ComponentState.TEARING_DOWN }, { ComponentState.CANCELLING, ComponentState.TEARING_DOWN }, { ComponentState.RESETTING, ComponentState.TEARING_DOWN }, { ComponentState.TEARING_DOWN, ComponentState.FAILED }, { ComponentState.TEARING_DOWN, ComponentState.RESULTS_REJECTED }, { ComponentState.FAILED, ComponentState.DISPOSING }, { ComponentState.RESULTS_REJECTED, ComponentState.DISPOSING } }; private static ComponentExecutionService comExeService; private static ComponentExecutionStatsService compExeStatsService; private static ComponentExecutionRelatedInstancesFactory compExeInstancesFactory; // visibility is protected for test purposes protected final Map<ComponentStateMachineEventType, EventProcessor> eventProcessors = new HashMap<>(); private ComponentExecutionRelatedInstances compExeRelatedInstances; private AtomicReference<ComponentExecutor> compExecutorRef = new AtomicReference<ComponentExecutor>(null); private Future<?> currentTask = null; private String errorId = null; private String errorMessage = null; private boolean pauseWasRequested = false; private ComponentStateMachineEvent lastEventBeforePaused; private ComponentContextImpl componentContext; private final AsyncTaskService threadPool = ConcurrencyUtils.getAsyncTaskService(); private SortedSet<Integer> executionCountOnResets = new TreeSet<>(); private ScheduledFuture<?> heartbeatFuture; private volatile String latestVerificationToken; private Runnable heartbeatRunnable = new Runnable() { @Override @TaskDescription("Send heartbeat for component") public void run() { if (VERBOSE_LOGGING) { LOG.debug("Sending component heartbeat: " + compExeRelatedInstances.compExeCtx.getExecutionIdentifier()); } compExeRelatedInstances.wfExeCtrlBridgeDelegator .onComponentHeartbeatReceived(compExeRelatedInstances.compExeCtx.getExecutionIdentifier()); } }; @Deprecated public ComponentStateMachine() { super(ComponentState.INIT, VALID_COMPONENT_STATE_TRANSITIONS); } public ComponentStateMachine(ComponentExecutionRelatedInstances compExeRelatedInstances) { super(ComponentState.INIT, VALID_COMPONENT_STATE_TRANSITIONS); this.compExeRelatedInstances = compExeRelatedInstances; boolean isNestedLoopDriver = Boolean.valueOf(compExeRelatedInstances.compExeCtx.getComponentDescription() .getConfigurationDescription().getConfigurationValue(LoopComponentConstants.CONFIG_KEY_IS_NESTED_LOOP)); compExeRelatedInstances.isNestedLoopDriver = isNestedLoopDriver; compExeRelatedInstances.compExeRelatedStates = new ComponentExecutionRelatedStates(); compExeRelatedInstances.typedDatumToOutputWriter = compExeInstancesFactory.createTypedDatumToOutputWriter(compExeRelatedInstances); compExeRelatedInstances.wfExeCtrlBridgeDelegator = compExeInstancesFactory.createWorkflowExecutionControllerBridgeDelegator(compExeRelatedInstances); compExeRelatedInstances.batchingConsoleRowsForwarder = compExeInstancesFactory.createBatchingConsoleRowsForwarder(compExeRelatedInstances); compExeRelatedInstances.consoleRowsSender = compExeInstancesFactory.createConsoleRowsSender(compExeRelatedInstances); compExeRelatedInstances.compCtxBridge = compExeInstancesFactory.createComponentContextBridge(compExeRelatedInstances); heartbeatFuture = threadPool.scheduleAtFixedRateAfterDelay(heartbeatRunnable, Math.round(Math.random() * 10), HEARTBEAT_SEND_INTERVAL_MSEC); initializeEventProcessors(); } // visibility is protected for test purposes protected void initializeEventProcessors() { eventProcessors.put(ComponentStateMachineEventType.PREPARE_REQUESTED, new PrepareRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.START_REQUESTED, new StartRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.PROCESSING_INPUT_DATUMS_REQUESTED, new ProcessingInputsDatumRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RUNNING, new RunningEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESET_REQUESTED, new ResetRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.PAUSE_REQUESTED, new PauseRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESUME_REQUESTED, new ResumeRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.CANCEL_REQUESTED, new CancelRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.DISPOSE_REQUESTED, new DisposeRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.PREPARATION_SUCCESSFUL, new PreparationSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.START_SUCCESSFUL, new StartSuccessfulEventProcessor()); StartAsRunOrProcessingInputsSuccessfulEventProcessor startOrProcessingInputsSuccessfulEventProcessor = new StartAsRunOrProcessingInputsSuccessfulEventProcessor(); eventProcessors.put(ComponentStateMachineEventType.START_AS_RUN_SUCCESSFUL, startOrProcessingInputsSuccessfulEventProcessor); eventProcessors.put(ComponentStateMachineEventType.PROCESSING_INPUTS_SUCCESSFUL, startOrProcessingInputsSuccessfulEventProcessor); eventProcessors.put(ComponentStateMachineEventType.RESULT_APPROVAL_REQUESTED, new ResultsApprovalRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESULTS_APPROVED, new ResultsApprovedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESULTS_REJECTED, new ResultsRejectedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESULTS_APPROVED_COMPLETED, new ResultsApprovedCompletedSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESULTS_REJECTED_COMPLETED, new ResultsRejectedCompletedSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.IDLE_REQUESTED, new IdleRequestedEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.RESET_SUCCESSFUL, new ResetSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.CANCEL_ATTEMPT_SUCCESSFUL, new CancelAttemptSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.DISPOSE_ATTEMPT_SUCCESSFUL, new DisposeAttemptSuccessfulEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.NEW_SCHEDULING_STATE, new NewSchedulingStateEventProcessor()); eventProcessors.put(ComponentStateMachineEventType.FINISHED, new FinishedEventProcessor()); FailedEventProcessor failedEventProcessor = new FailedEventProcessor(); eventProcessors.put(ComponentStateMachineEventType.PROCESSING_INPUTS_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.START_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.RESET_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.PREPARATION_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.SCHEDULING_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.PAUSE_ATTEMPT_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.CANCEL_ATTEMPT_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.WF_CRTL_CALLBACK_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.VERIFICATION_COMPLETION_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.HANDLE_VERIFICATION_TOKEN_FAILED, failedEventProcessor); eventProcessors.put(ComponentStateMachineEventType.TEARED_DOWN, new TearedDownEventProcessor()); } /** * @return latest verification token if component is in {@link ComponentState#WAITING_FOR_APPROVAL} (or {@link ComponentState#PAUSING}), * otherwise <code>null</code> in case of another {@link ComponentState} or in case no token exists. */ public String getVerificationToken() { if (getState().equals(ComponentState.WAITING_FOR_APPROVAL) || getState().equals(ComponentState.PAUSING)) { return latestVerificationToken; } else { return null; } } public boolean isWorkflowControllerReachable() { return compExeRelatedInstances.wfExeCtrlBridgeDelegator.isWorkflowControllerReachable(); } private boolean checkStateChange(ComponentState currentState, ComponentState newState, ComponentStateMachineEvent event) { if (isStateChangeValid(currentState, newState)) { return true; } else { logInvalidStateChangeRequest(currentState, newState, event); return false; } } @Override protected ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) throws StateChangeException { if (pauseWasRequested && event.getType() != ComponentStateMachineEventType.RUNNING) { pauseWasRequested = false; if (!ComponentConstants.FINAL_COMPONENT_STATES_WITH_DISPOSED.contains(currentState)) { lastEventBeforePaused = event; return ComponentState.PAUSED; } } return eventProcessors.get(event.getType()).processEvent(currentState, event); } private void handleFailureEvent(ComponentStateMachineEvent event) { Throwable throwable = event.getThrowable(); if (throwable != null) { final String message = StringUtils.format("Executing component '%s' (%s) failed", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getExecutionIdentifier()); if (throwable.getCause() != null) { errorId = LogUtils.logExceptionWithStacktraceAndAssignUniqueMarker(LOG, message, throwable); } else { errorId = LogUtils.logExceptionAsSingleLineAndAssignUniqueMarker(LOG, message, throwable); } errorMessage = ComponentUtils.createErrorLogMessage(throwable); } else if (event.getErrorId() != null) { errorId = event.getErrorId(); } } private ComponentState handleFailure(ComponentState currentState, ComponentStateMachineEvent event) { handleFailureEvent(event); ComponentState finalCompState = ComponentState.FAILED; switch (event.getType()) { case WF_CRTL_CALLBACK_FAILED: case SCHEDULING_FAILED: cancelAsync(); return ComponentState.CANCELLING_AFTER_FAILURE; default: currentTask = null; tearDownAsync(finalCompState); return ComponentState.TEARING_DOWN; } } private ComponentState handleFinished() { if (compExeRelatedInstances.compExeRelatedStates.executionCount.get() == 0) { tearDownAsync(ComponentState.FINISHED_WITHOUT_EXECUTION); } else { tearDownAsync(ComponentState.FINISHED); } return ComponentState.TEARING_DOWN; } private void logInvalidStateChangeRequest(ComponentState currentState, ComponentState requestedState, ComponentStateMachineEvent event) { LOG.debug(StringUtils.format("Ignored component state change request for component '%s' (%s) of workflow '%s' (%s) " + "as it will cause an invalid state transition: %s -> %s; event was: %s", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(), compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier(), currentState, requestedState, event.getType().name())); } @Override protected void onStateChanged(ComponentState oldState, ComponentState newState) { LOG.debug(StringUtils.format("%s is now %s (previous state: %s)", ComponentExecutionUtils.getStringWithInfoAboutComponentAndWorkflowUpperCase(compExeRelatedInstances.compExeCtx), newState, oldState)); if (newState.equals(ComponentState.FAILED)) { if (errorMessage != null) { compExeRelatedInstances.wfExeCtrlBridgeDelegator.onComponentStateChanged( compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), newState, compExeRelatedInstances.compExeRelatedStates.executionCount.get(), getExecutionCountsOnResetAsString(), errorId, errorMessage); errorMessage = null; } else { compExeRelatedInstances.wfExeCtrlBridgeDelegator.onComponentStateChanged( compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), newState, compExeRelatedInstances.compExeRelatedStates.executionCount.get(), getExecutionCountsOnResetAsString(), errorId); } errorId = null; } else { compExeRelatedInstances.wfExeCtrlBridgeDelegator.onComponentStateChanged( compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), newState, compExeRelatedInstances.compExeRelatedStates.executionCount.get(), getExecutionCountsOnResetAsString()); } if (ComponentConstants.FINAL_COMPONENT_STATES.contains(newState)) { compExeRelatedInstances.consoleRowsSender.sendStateAsConsoleRow(WorkflowLifecyleEventType.COMPONENT_TERMINATED); heartbeatFuture.cancel(false); compExeStatsService.addStatsAtComponentTermination(compExeRelatedInstances.compExeCtx, newState); if (!compExeRelatedInstances.compExeCtx.getComponentDescription().performLazyDisposal()) { try { comExeService.dispose(compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), compExeRelatedInstances.compExeCtx.getComponentDescription().getNode()); } catch (ExecutionControllerException | RemoteOperationException e) { // should not happen; in case it does at least call the component's dispose method anyway LOG.error(StringUtils.format("Failed to dispose component execution controller for %s", ComponentExecutionUtils.getStringWithInfoAboutComponentAndWorkflowLowerCase(compExeRelatedInstances.compExeCtx)), e); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.DISPOSE_REQUESTED)); } } } else if (newState == ComponentState.STARTING) { compExeRelatedInstances.consoleRowsSender.sendStateAsConsoleRow(WorkflowLifecyleEventType.COMPONENT_STARTING); } } private String getExecutionCountsOnResetAsString() { List<String> counts = new ArrayList<>(); synchronized (executionCountOnResets) { for (Integer countOnReset : executionCountOnResets) { counts.add(String.valueOf(countOnReset)); } if (!executionCountOnResets.contains(compExeRelatedInstances.compExeRelatedStates.executionCount.get())) { counts.add(String.valueOf(compExeRelatedInstances.compExeRelatedStates.executionCount.get())); } } return StringUtils.escapeAndConcat(counts); } @Override protected void onStateChangeException(ComponentStateMachineEvent event, StateChangeException e) { LOG.error( StringUtils.format("Invalid state change for component '%s' (%s) of workflow '%s' (%s) attempt, caused by event '%s'", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(), compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier(), event), e); } private void prepareAsync() { currentTask = threadPool.submit(new AsyncPrepareTask()); } private void startAsync() { ComponentExecutor.ComponentExecutionType compExeType = ComponentExecutor.ComponentExecutionType.StartAsInit; synchronized (compExeRelatedInstances.component) { if (compExeRelatedInstances.component.get().treatStartAsComponentRun()) { compExeType = ComponentExecutor.ComponentExecutionType.StartAsRun; } } compExecutorRef.set(new ComponentExecutor(compExeRelatedInstances, compExeType)); currentTask = threadPool.submit(new AsyncStartTask(compExeType)); } private void processInputsAsync() { compExecutorRef.set(new ComponentExecutor(compExeRelatedInstances, ComponentExecutor.ComponentExecutionType.ProcessInputs)); currentTask = threadPool.submit(new AsyncProcessInputsTask()); } private void handleVerificationTokenAsync(String verificationToken) { ComponentExecutionType compExeType = ComponentExecutor.ComponentExecutionType.HandleVerificationToken; compExeType.setVerificationToken(verificationToken); latestVerificationToken = verificationToken; compExecutorRef.set(new ComponentExecutor(compExeRelatedInstances, compExeType)); currentTask = threadPool.submit(new AsyncHandleVerificationTokenTask()); } private void completeVerificationAsync(boolean outputsApproved) { ComponentExecutionType compExeType = ComponentExecutor.ComponentExecutionType.CompleteVerification; if (outputsApproved) { compExeType.setFinalComponentStateAfterRun(FinalComponentRunState.RESULTS_APPROVED); } else { compExeType.setFinalComponentStateAfterRun(FinalComponentRunState.RESULTS_REJECTED); } compExecutorRef.set(new ComponentExecutor(compExeRelatedInstances, ComponentExecutor.ComponentExecutionType.CompleteVerification)); currentTask = threadPool.submit(new AsyncCompleteVerificationTask(outputsApproved)); } private void resetAsync() { compExecutorRef.set(new ComponentExecutor(compExeRelatedInstances, ComponentExecutor.ComponentExecutionType.Reset)); currentTask = threadPool.submit(new AsyncResetTask()); } private void cancelAsync() { threadPool.submit(new AsyncCancelTask(currentTask)); } private void tearDownAsync(final ComponentState compState) { final Component.FinalComponentState finalCompState; FinalComponentState finalStateForDm = null; switch (compState) { case FINISHED_WITHOUT_EXECUTION: finalCompState = Component.FinalComponentState.FINISHED; finalStateForDm = FinalComponentState.FINISHED_WITHOUT_EXECUTION; break; case FINISHED: finalCompState = Component.FinalComponentState.FINISHED; finalStateForDm = FinalComponentState.FINISHED; break; case FAILED: finalCompState = Component.FinalComponentState.FAILED; finalStateForDm = FinalComponentState.FAILED; break; case RESULTS_REJECTED: finalCompState = Component.FinalComponentState.RESULTS_REJECTED; finalStateForDm = FinalComponentState.RESULTS_REJECTED; break; case CANCELED: finalCompState = Component.FinalComponentState.CANCELLED; finalStateForDm = FinalComponentState.CANCELLED; break; default: finalCompState = null; break; } ComponentExecutionType compExeType = ComponentExecutionType.TearDown; compExeType.setFinalComponentStateAfterTearedDown(finalCompState); ComponentExecutor compExecutor = new ComponentExecutor(compExeRelatedInstances, compExeType); compExecutorRef.set(compExecutor); currentTask = threadPool.submit(new AsyncTearDownTask(compState, finalCompState, finalStateForDm)); } private void disposeAsync() { currentTask = threadPool.submit(new AsyncDisposeTask()); } private void checkForIntermediateButNoFinalHistoryDataItemWritten() throws ComponentExecutionException { if (!compExeRelatedInstances.compExeRelatedStates.finalHistoryDataItemWritten.get() && Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { if (compExeRelatedInstances.compExeRelatedStates.intermediateHistoryDataWritten.get()) { LOG.warn(StringUtils.format("No final history data item was written for %s even if intermediate ones were", ComponentExecutionUtils.getStringWithInfoAboutComponentAndWorkflowLowerCase(compExeRelatedInstances.compExeCtx))); } } compExeRelatedInstances.compExeRelatedStates.intermediateHistoryDataWritten.set(false); compExeRelatedInstances.compExeRelatedStates.finalHistoryDataItemWritten.set(false); } private void idle() { if (!compExeRelatedInstances.compExeCtx.isConnectedToEndpointDatumSenders()) { compExeRelatedInstances.compCtxBridge.closeAllOutputs(); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.FINISHED)); } else { compExeRelatedInstances.compExeScheduler.enable(); } } /** * Prepares the component. * * @author Doreen Seider */ private final class AsyncPrepareTask implements Runnable { @Override @TaskDescription("Prepare component") public void run() { compExeStatsService.addStatsAtComponentStart(compExeRelatedInstances.compExeCtx); try { compExeRelatedInstances.component.set(createNewComponentInstance()); ((ComponentExecutionContextImpl) compExeRelatedInstances.compExeCtx) .setWorkingDirectory(createWorkingDirectory()); componentContext = new ComponentContextImpl(compExeRelatedInstances.compExeCtx, compExeRelatedInstances.compCtxBridge); synchronized (compExeRelatedInstances.component) { compExeRelatedInstances.component.get().setComponentContext(componentContext); } compExeRelatedInstances.compExeScheduler.initialize(compExeRelatedInstances.compExeCtx); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PREPARATION_SUCCESSFUL)); } catch (ComponentExecutionException | RuntimeException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PREPARATION_FAILED, e)); } } private Component createNewComponentInstance() throws ComponentExecutionException { final String message = StringUtils.format("Failed to instantiate component '%s' (%s) of workflow '%s' (%s)", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(), compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier()); try { return (Component) Class .forName(compExeRelatedInstances.compExeCtx.getComponentDescription().getClassName()).getConstructor() .newInstance(); } catch (SecurityException | NoSuchMethodException | ClassNotFoundException | IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new ComponentExecutionException(message, e); } } } /** * Starts the component. * * @author Doreen Seider */ private final class AsyncStartTask implements Runnable { private final ComponentExecutor.ComponentExecutionType compExeType; protected AsyncStartTask(ComponentExecutor.ComponentExecutionType compExeType) { this.compExeType = compExeType; } @Override @TaskDescription("Start component") public void run() { try { compExecutorRef.get().executeByConsideringLimitations(); if (compExeType == ComponentExecutor.ComponentExecutionType.StartAsRun) { checkForIntermediateButNoFinalHistoryDataItemWritten(); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.START_AS_RUN_SUCCESSFUL)); } else { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.START_SUCCESSFUL)); } } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.START_FAILED, e)); } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.START_FAILED, e.getMessage())); } } } private File createWorkingDirectory() throws ComponentExecutionException { try { return TempFileServiceAccess.getInstance() .createManagedTempDir("cmp-" + compExeRelatedInstances.compExeCtx.getExecutionIdentifier()); } catch (IOException e) { throw new ComponentExecutionException(StringUtils.format("Failed to create working directory for component '%s' " + "(%s) of workflow '%s' (%s)", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getExecutionIdentifier(), compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(), compExeRelatedInstances.compExeCtx.getWorkflowExecutionIdentifier()), e); } } /** * Resets the component. * * @author Doreen Seider */ private final class AsyncResetTask implements Runnable { @Override @TaskDescription("Reset component") public void run() { try { try { compExecutorRef.get().executeByConsideringLimitations(); } finally { synchronized (executionCountOnResets) { executionCountOnResets.add(compExeRelatedInstances.compExeRelatedStates.executionCount.get()); } } postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESET_SUCCESSFUL)); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESET_FAILED, e)); } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESET_FAILED, e.getMessage())); } } } /** * Let the component process inputs. * * @author Doreen Seider */ private final class AsyncProcessInputsTask implements Runnable { @Override @TaskDescription("Let component process inputs") public void run() { try { compExeRelatedInstances.compExeRelatedStates.executionCount.incrementAndGet(); compExecutorRef.get().executeByConsideringLimitations(); checkForIntermediateButNoFinalHistoryDataItemWritten(); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PROCESSING_INPUTS_SUCCESSFUL)); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PROCESSING_INPUTS_FAILED, e)); } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PROCESSING_INPUTS_FAILED, e.getMessage())); } } } /** * Let the component handle the verification token. * * @author Doreen Seider */ private final class AsyncHandleVerificationTokenTask implements Runnable { @Override @TaskDescription("Let component handle the verification token") public void run() { try { compExecutorRef.get().executeByConsideringLimitations(); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.HANDLE_VERIFICATION_TOKEN_FAILED, e)); } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.HANDLE_VERIFICATION_TOKEN_FAILED, e.getMessage())); } } } /** * Let the component complete the verification. * * @author Doreen Seider */ private final class AsyncCompleteVerificationTask implements Runnable { private final boolean outputsApprove; protected AsyncCompleteVerificationTask(boolean outputsApprove) { this.outputsApprove = outputsApprove; } @Override @TaskDescription("Let component complete verification") public void run() { try { compExecutorRef.get().executeByConsideringLimitations(); if (outputsApprove) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESULTS_APPROVED_COMPLETED)); } else { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESULTS_REJECTED_COMPLETED)); } } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.VERIFICATION_COMPLETION_FAILED, e)); } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.VERIFICATION_COMPLETION_FAILED, e.getMessage())); } } } /** * Cancels the component. * * @author Doreen Seider */ private final class AsyncCancelTask implements Runnable { private static final int WAIT_INTERVAL_CANCEL_SEC = 70; private final Future<?> task; AsyncCancelTask(Future<?> future) { this.task = future; } @Override @TaskDescription("Cancel component") public void run() { ComponentExecutor compExecutor = compExecutorRef.get(); if (compExecutor != null) { compExecutor.onCancelled(); } if (task != null) { try { task.get(WAIT_INTERVAL_CANCEL_SEC, TimeUnit.SECONDS); cancelAttemptSuccessful(); } catch (ExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.CANCEL_ATTEMPT_FAILED, e)); } catch (InterruptedException | TimeoutException e) { task.cancel(true); } } else { cancelAttemptSuccessful(); } } private void cancelAttemptSuccessful() { if (compExeRelatedInstances.compExeScheduler.isLoopResetRequested()) { compExeRelatedInstances.consoleRowsSender.sendLogFileWriteTriggerAsConsoleRow(); } postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.CANCEL_ATTEMPT_SUCCESSFUL)); } } /** * Tears down the component. * * @author Doreen Seider */ private final class AsyncTearDownTask implements Runnable { private final ComponentState compState; private final Component.FinalComponentState finalCompState; private final FinalComponentState finalStateForDm; AsyncTearDownTask(ComponentState compState, Component.FinalComponentState finalCompState, FinalComponentState finalStateForDm) { this.compState = compState; this.finalCompState = finalCompState; this.finalStateForDm = finalStateForDm; } @Override @TaskDescription("Tear down component") public void run() { if (compExeRelatedInstances.component != null) { try { compExecutorRef.get().executeByConsideringLimitations(); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.TEARED_DOWN, ComponentState.FAILED, e)); return; } catch (ComponentException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.TEARED_DOWN, ComponentState.FAILED, e.getMessage())); } if (finalCompState == Component.FinalComponentState.FAILED) { try { checkForIntermediateButNoFinalHistoryDataItemWritten(); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.TEARED_DOWN, ComponentState.FAILED, e)); return; } } } try { compExeRelatedInstances.compExeStorageBridge.setFinalComponentState(finalStateForDm); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.TEARED_DOWN, ComponentState.FAILED, e)); return; } postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.TEARED_DOWN, compState)); } } /** * Disposes the component. * * @author Doreen Seider */ private final class AsyncDisposeTask implements Runnable { @Override @TaskDescription("Dispose component") public void run() { if (compExeRelatedInstances.component != null) { try { synchronized (compExeRelatedInstances.component) { compExeRelatedInstances.component.get().dispose(); } } catch (RuntimeException e) { LOG.error("Failed to dispose " + ComponentExecutionUtils.getStringWithInfoAboutComponentAndWorkflowLowerCase(compExeRelatedInstances.compExeCtx), e); return; } disposeWorkingDirectory(); } postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.DISPOSE_ATTEMPT_SUCCESSFUL)); } } private void disposeWorkingDirectory() { if (compExeRelatedInstances.compExeCtx.getWorkingDirectory() != null) { try { TempFileServiceAccess.getInstance() .disposeManagedTempDirOrFile(compExeRelatedInstances.compExeCtx.getWorkingDirectory()); } catch (IOException e) { LOG.error(StringUtils.format("Failed to dispose working directory of %s ", ComponentExecutionUtils.getStringWithInfoAboutComponentAndWorkflowLowerCase(compExeRelatedInstances.compExeCtx)), e); } } } /** * Processes {@link ComponentStateMachineEvent}s. * * @author Doreen Seider */ private interface EventProcessor { ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event); } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class PrepareRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.PREPARING, event)) { state = ComponentState.PREPARING; prepareAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class StartRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.WAITING_FOR_RESOURCES, event)) { state = ComponentState.WAITING_FOR_RESOURCES; synchronized (compExeRelatedInstances.component) { if (compExeRelatedInstances.component.get().treatStartAsComponentRun()) { compExeRelatedInstances.compExeRelatedStates.executionCount.incrementAndGet(); } } startAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ProcessingInputsDatumRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.WAITING_FOR_RESOURCES, event)) { state = ComponentState.WAITING_FOR_RESOURCES; processInputsAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class RunningEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, event.getNewComponentState(), event)) { state = event.getNewComponentState(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResetRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.WAITING_FOR_RESOURCES, event)) { state = ComponentState.WAITING_FOR_RESOURCES; resetAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class PauseRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { if (compExeRelatedInstances.compExeScheduler.isEnabled()) { compExeRelatedInstances.compExeScheduler.disable(); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PAUSE_REQUESTED)); return currentState; } else { if (currentState == ComponentState.IDLING || currentState == ComponentState.IDLING_AFTER_RESET) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.IDLE_REQUESTED, currentState)); } pauseWasRequested = true; return ComponentState.PAUSING; } } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResumeRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.RESUMING, event)) { state = ComponentState.RESUMING; postEvent(lastEventBeforePaused); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class CancelRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.CANCELLING, event)) { state = ComponentState.CANCELLING; cancelAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class DisposeRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.DISPOSING, event)) { state = ComponentState.DISPOSING; disposeAsync(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class PreparationSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.PREPARED, event)) { currentTask = null; state = ComponentState.PREPARED; } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResetSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.IDLING_AFTER_RESET, event)) { state = ComponentState.IDLING_AFTER_RESET; idle(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class CancelAttemptSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.TEARING_DOWN, event)) { if (currentState == ComponentState.CANCELLING_AFTER_FAILURE) { tearDownAsync(ComponentState.FAILED); } else { tearDownAsync(ComponentState.CANCELED); } state = ComponentState.TEARING_DOWN; } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class DisposeAttemptSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.DISPOSED, event)) { state = ComponentState.DISPOSED; } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class FinishedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.TEARING_DOWN, event)) { state = handleFinished(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class NewSchedulingStateEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { switch (compExeRelatedInstances.compExeScheduler.getSchedulingState()) { case FINISHED: forwardFinishToNonClosedOutputs(); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.FINISHED)); break; case PROCESS_INPUT_DATA: requestProcessingInputDatums(); break; case PROCESS_INPUT_DATA_WITH_NOT_A_VALUE_DATA: ComponentInterface compInterface = compExeRelatedInstances.compExeCtx.getComponentDescription().getComponentInstallation() .getComponentRevision().getComponentInterface(); if (compInterface.getIsLoopDriver() || compInterface.getCanHandleNotAValueDataTypes()) { requestProcessingInputDatums(); } else { // it must contain at least one of DataType 'not a value' if the state was // PROCESS_INPUT_DATA_WITH_NOT_A_VALUE_DATA for (EndpointDatum endpointDatum : compExeRelatedInstances.compExeScheduler.fetchEndpointDatums().values()) { if (endpointDatum.getValue().getDataType().equals(DataType.NotAValue)) { forwardNotAValueData(endpointDatum); break; } } postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.IDLE_REQUESTED)); } break; case RESET: forwardInternalTD(compExeRelatedInstances.compExeScheduler.getResetDatum()); if (!getState().equals(ComponentState.IDLING_AFTER_RESET)) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESET_REQUESTED)); } else { postEvent( new ComponentStateMachineEvent(ComponentStateMachineEventType.IDLE_REQUESTED, ComponentState.IDLING_AFTER_RESET)); } break; case FAILURE_FORWARD: forwardInternalTD(compExeRelatedInstances.compExeScheduler.getFailureDatum()); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.IDLE_REQUESTED)); break; case LOOP_RESET: postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESET_REQUESTED)); break; default: break; } return currentState; } private void forwardInternalTD(InternalTDImpl internalTD) { Queue<WorkflowGraphHop> hopsToTraverse = internalTD.getHopsToTraverse(); WorkflowGraphHop currentHop = hopsToTraverse.poll(); compExeRelatedInstances.typedDatumToOutputWriter.writeTypedDatumToOutputConsideringOnlyCertainInputs( currentHop.getHopOuputName(), internalTD, currentHop.getTargetExecutionIdentifier(), currentHop.getTargetInputName()); } private void forwardFinishToNonClosedOutputs() { for (EndpointDescription output : compExeRelatedInstances.compExeCtx.getComponentDescription() .getOutputDescriptionsManager().getEndpointDescriptions()) { if (!compExeRelatedInstances.compCtxBridge.isOutputClosed(output.getName())) { compExeRelatedInstances.typedDatumToOutputWriter.writeTypedDatumToOutput(output.getName(), new InternalTDImpl(InternalTDImpl.InternalTDType.WorkflowFinish)); } } } private void forwardNotAValueData(EndpointDatum nAVEndpointDatum) { for (EndpointDescription output : compExeRelatedInstances.compExeCtx.getComponentDescription() .getOutputDescriptionsManager() .getEndpointDescriptions()) { compExeRelatedInstances.typedDatumToOutputWriter.writeTypedDatumToOutput(output.getName(), nAVEndpointDatum.getValue()); LOG.info(StringUtils.format("Component '%s' of workflow '%s' did not run because of 'not a value' " + "value at input '%s'", compExeRelatedInstances.compExeCtx.getInstanceName(), compExeRelatedInstances.compExeCtx.getWorkflowInstanceName(), nAVEndpointDatum.getInputName())); } } private void requestProcessingInputDatums() { try { compExeRelatedInstances.compCtxBridge .setEndpointDatumsForExecution(compExeRelatedInstances.compExeScheduler.fetchEndpointDatums()); postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PROCESSING_INPUT_DATUMS_REQUESTED)); } catch (ComponentExecutionException e) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.PROCESSING_INPUTS_FAILED)); } } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class IdleRequestedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; ComponentState newCompState = event.getNewComponentState(); if (newCompState == null) { newCompState = ComponentState.IDLING; } if (checkStateChange(currentState, newCompState, event)) { state = newCompState; idle(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class StartSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { if (!isCanceling(currentState)) { return new IdleRequestedEventProcessor().processEvent(currentState, event); } return currentState; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class StartAsRunOrProcessingInputsSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState newState = currentState; if (!isCanceling(currentState)) { if (ComponentExecutionUtils.isVerificationRequired(compExeRelatedInstances.compExeCtx.getComponentDescription() .getConfigurationDescription().getComponentConfigurationDefinition())) { postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.RESULT_APPROVAL_REQUESTED)); } else { newState = new IdleRequestedEventProcessor().processEvent(currentState, event); } } return newState; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class FailedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.TEARING_DOWN, event)) { state = handleFailure(currentState, event); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class TearedDownEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, event.getNewComponentState(), event)) { handleFailureEvent(event); state = event.getNewComponentState(); } return state; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResultsApprovalRequestedEventProcessor implements EventProcessor { private static final int VERIFICATION_TOKEN_LENGTH = 32; @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { String verificationToken = IdGenerator.secureRandomHexString(VERIFICATION_TOKEN_LENGTH); handleVerificationTokenAsync(verificationToken); return ComponentState.WAITING_FOR_APPROVAL; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResultsApprovedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { latestVerificationToken = null; compExeRelatedInstances.compCtxBridge.flushOutputs(); componentContext.getLog() .componentInfo("Result approved (was done by person responsible for the component) -> outputs sent"); completeVerificationAsync(true); return currentState; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResultsRejectedEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { componentContext.getLog() .componentWarn("Results rejected (was done by person responsible for the component) -> cancel"); completeVerificationAsync(false); return currentState; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResultsApprovedCompletedSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState newState = currentState; if (!isCanceling(currentState)) { newState = new IdleRequestedEventProcessor().processEvent(currentState, event); } return newState; } } /** * Specific implementation of {@link EventProcessor}. * * @author Doreen Seider */ private class ResultsRejectedCompletedSuccessfulEventProcessor implements EventProcessor { @Override public ComponentState processEvent(ComponentState currentState, ComponentStateMachineEvent event) { ComponentState state = currentState; if (checkStateChange(currentState, ComponentState.TEARING_DOWN, event)) { state = ComponentState.TEARING_DOWN; tearDownAsync(ComponentState.RESULTS_REJECTED); } return state; } } private boolean isCanceling(ComponentState state) { return state == ComponentState.CANCELLING || state == ComponentState.CANCELLING_AFTER_FAILURE; } protected void bindComponentExecutionService(ComponentExecutionService newService) { ComponentStateMachine.comExeService = newService; } protected void bindComponentExecutionStatsService(ComponentExecutionStatsService newService) { ComponentStateMachine.compExeStatsService = newService; } protected void bindComponentExecutionRelatedInstancesFactory(ComponentExecutionRelatedInstancesFactory newService) { ComponentStateMachine.compExeInstancesFactory = newService; } }