Java tutorial
/******************************************************************************* * (c) Copyright 2014 Hewlett-Packard Development Company, L.P. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License v2.0 which accompany this distribution. * * The Apache License is available at * http://www.apache.org/licenses/LICENSE-2.0 * *******************************************************************************/ package io.cloudslang.worker.execution.services; import io.cloudslang.score.api.ExecutionPlan; import io.cloudslang.score.api.ExecutionStep; import io.cloudslang.score.api.StartBranchDataContainer; import io.cloudslang.score.api.execution.ExecutionMetadataConsts; import io.cloudslang.score.api.execution.ExecutionParametersConsts; import io.cloudslang.score.events.EventBus; import io.cloudslang.score.events.EventConstants; import io.cloudslang.score.events.ScoreEvent; import io.cloudslang.score.facade.TempConstants; import io.cloudslang.score.facade.entities.Execution; import io.cloudslang.score.facade.entities.RunningExecutionPlan; import io.cloudslang.score.facade.execution.ExecutionStatus; import io.cloudslang.score.facade.execution.ExecutionSummary; import io.cloudslang.score.facade.execution.PauseReason; import io.cloudslang.score.lang.SystemContext; import io.cloudslang.orchestrator.services.PauseResumeService; import io.cloudslang.worker.execution.reflection.ReflectionAdapter; import io.cloudslang.worker.management.WorkerConfigurationService; import io.cloudslang.worker.management.services.dbsupport.WorkerDbSupportService; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; /** * @author * @author Avi Moradi * @since 08/01/2011 * @version $Id$ */ public final class ExecutionServiceImpl implements ExecutionService { private static final Logger logger = Logger.getLogger(ExecutionServiceImpl.class); @Autowired private PauseResumeService pauseService; @Autowired private ReflectionAdapter reflectionAdapter; @Autowired private WorkerDbSupportService workerDbSupportService; @Autowired private WorkerConfigurationService workerConfigurationService; @Autowired private EventBus eventBus; @Override public Execution execute(Execution execution) throws InterruptedException { try { // handle flow cancellation if (handleCancelledFlow(execution)) { return execution; } ExecutionStep currStep = loadExecutionStep(execution); // Check if this execution was paused if (!isDebuggerMode(execution.getSystemContext()) && handlePausedFlow(execution)) { return null; } // dum bus event dumpBusEvents(execution); // Run the execution step executeStep(execution, currStep); // Run the navigation navigate(execution, currStep); // currently handles groups and jms optimizations postExecutionSettings(execution); // If execution was paused in language - to avoid delay of configuration if (execution.getSystemContext().isPaused()) { if (handlePausedFlowAfterStep(execution)) { return null; } } // dum bus event dumpBusEvents(execution); if (logger.isDebugEnabled()) { logger.debug("End of step: " + execution.getPosition() + " in execution id: " + execution.getExecutionId()); } return execution; } catch (InterruptedException ex) { throw ex; } catch (Exception ex) { logger.error("Error during execution: ", ex); execution.getSystemContext().setStepErrorKey(ex.getMessage()); // this is done only fo reporting execution.getSystemContext().setFlowTerminationType(ExecutionStatus.SYSTEM_FAILURE); execution.setPosition(null); // this ends the flow!!! return execution; } } @Override // returns null in case the split was not done - flow is paused or cancelled public List<Execution> executeSplit(Execution execution) throws InterruptedException { try { ExecutionStep currStep = loadExecutionStep(execution); // Check if this execution was paused if (!isDebuggerMode(execution.getSystemContext()) && handlePausedFlow(execution)) { return null; } // dum bus event dumpBusEvents(execution); executeStep(execution, currStep); failFlowIfSplitStepFailed(execution); dumpBusEvents(execution); // Run the split step List<StartBranchDataContainer> newBranches = execution.getSystemContext().removeBranchesData(); List<Execution> newExecutions = createChildExecutions(execution.getExecutionId(), newBranches); // Run the navigation navigate(execution, currStep); dumpBusEvents(execution); if (logger.isDebugEnabled()) { logger.debug("End of step: " + execution.getPosition() + " in execution id: " + execution.getExecutionId()); } return newExecutions; } catch (Exception ex) { logger.error("Exception during the split step!", ex); throw ex; } } private void failFlowIfSplitStepFailed(Execution execution) throws InterruptedException { if (execution.getSystemContext().hasStepErrorKey()) { String exception = execution.getSystemContext().getStepErrorKey(); execution.getSystemContext().setFlowTerminationType(ExecutionStatus.SYSTEM_FAILURE); execution.setPosition(null); // this ends the flow!!! try { createErrorEvent(exception, "Error occurred during split step ", EventConstants.SCORE_STEP_SPLIT_ERROR, execution.getSystemContext()); } catch (RuntimeException eventEx) { logger.error("Failed to create event: ", eventEx); } throw new RuntimeException(exception); } } private static List<Execution> createChildExecutions(Long executionId, List<StartBranchDataContainer> newBranches) { List<Execution> newExecutions = new ArrayList<>(); String splitId = UUID.randomUUID().toString(); for (int i = 0; i < newBranches.size(); i++) { StartBranchDataContainer from = newBranches.get(i); Execution to = new Execution(executionId, from.getExecutionPlanId(), from.getStartPosition(), from.getContexts(), from.getSystemContext()); to.getSystemContext().setSplitId(splitId); to.getSystemContext().setBranchId(splitId + ":" + (i + 1)); newExecutions.add(to); } return newExecutions; } @Override public boolean isSplitStep(Execution execution) { ExecutionStep currStep = loadExecutionStep(execution); return currStep.isSplitStep(); } protected boolean handleCancelledFlow(Execution execution) { boolean executionIsCancelled = workerConfigurationService.isExecutionCancelled(execution.getExecutionId()); // in this case - just check if need to cancel. It will set as cancelled later on QueueEventListener // Another scenario of getting canceled - it was cancelled from the SplitJoinService (the configuration can still be not updated). Defect #:22060 if (ExecutionStatus.CANCELED.equals(execution.getSystemContext().getFlowTerminationType())) { executionIsCancelled = true; } if (executionIsCancelled) { // NOTE: an execution can be cancelled directly from CancelExecutionService, if it's currently paused. // Thus, if you change the code here, please check CancelExecutionService as well. execution.getSystemContext().setFlowTerminationType(ExecutionStatus.CANCELED); execution.setPosition(null); return true; } return false; } // check if the execution should be Paused, and pause it if needed protected boolean handlePausedFlow(Execution execution) throws InterruptedException { String branchId = execution.getSystemContext().getBranchId(); PauseReason reason = findPauseReason(execution.getExecutionId(), branchId); if (reason != null) { // need to pause the execution pauseFlow(reason, execution); return true; } return false; } // no need to check if paused - because this is called after the step, when the Pause flag exists in the context private boolean handlePausedFlowAfterStep(Execution execution) throws InterruptedException { String branchId = execution.getSystemContext().getBranchId(); PauseReason reason = null; ExecutionSummary execSummary = pauseService.readPausedExecution(execution.getExecutionId(), branchId); if (execSummary != null && execSummary.getStatus().equals(ExecutionStatus.PENDING_PAUSE)) { reason = execSummary.getPauseReason(); } if (reason != null) { // need to pause the execution pauseFlow(reason, execution); return true; } return false; } private void pauseFlow(PauseReason reason, Execution execution) throws InterruptedException { SystemContext systemContext = execution.getSystemContext(); Long executionId = execution.getExecutionId(); String branchId = systemContext.getBranchId(); // If USER_PAUSED send such event if (!isDebuggerMode(execution.getSystemContext()) && reason.equals(PauseReason.USER_PAUSED)) { if (branchId != null) { // we pause the branch because the Parent was user-paused (see findPauseReason) pauseService.pauseExecution(executionId, branchId, reason); // this creates a DB record for this branch, as Pending-paused } } addPauseEvent(systemContext); // dump bus events here because out side is too late dumpBusEvents(execution); // Write execution to the db! Pay attention - do not do anything to the execution or its context after this line!!! pauseService.writeExecutionObject(executionId, branchId, execution); if (logger.isDebugEnabled()) { logger.debug("Execution with execution_id: " + execution.getExecutionId() + " is paused!"); } } private void addPauseEvent(SystemContext systemContext) throws InterruptedException { HashMap<String, Serializable> eventData = new HashMap<>(); eventData.put(ExecutionParametersConsts.SYSTEM_CONTEXT, new HashMap<>(systemContext)); ScoreEvent eventWrapper = new ScoreEvent(EventConstants.SCORE_PAUSED_EVENT, eventData); eventBus.dispatch(eventWrapper); } private PauseReason findPauseReason(Long executionId, String branchId) { // 1. Check the configuration according to branch (can be null or not null...) if (workerConfigurationService.isExecutionPaused(executionId, branchId)) { ExecutionSummary execSummary = pauseService.readPausedExecution(executionId, branchId); if (execSummary != null && execSummary.getStatus().equals(ExecutionStatus.PENDING_PAUSE)) { return execSummary.getPauseReason(); } // 2. Check the parent if we're in branch (subflow or MI\Parallel lane). // If the user pressed Pause on the Parent then we need to pause the branch (the parent is in the Suspended table). } else if (branchId != null && workerConfigurationService.isExecutionPaused(executionId, null)) { ExecutionSummary execSummary = pauseService.readPausedExecution(executionId, null); if (execSummary != null && execSummary.getStatus().equals(ExecutionStatus.PENDING_PAUSE)) { PauseReason reason = execSummary.getPauseReason(); // we only care about User-Paused here! // we don't want to Pause if the parent is paused due to branch_paused! (other branch is paused for some reason (e.g. required_input), so the parent is paused as well). if (PauseReason.USER_PAUSED.equals(reason)) { return reason; } } } return null; // not paused } private static boolean isDebuggerMode(Map<String, Serializable> systemContext) { Boolean isDebuggerMode = (Boolean) systemContext.get(TempConstants.DEBUGGER_MODE); if (isDebuggerMode == null) { return false; } return isDebuggerMode; } private void dumpBusEvents(Execution execution) throws InterruptedException { ArrayDeque<ScoreEvent> eventsQueue = execution.getSystemContext().getEvents(); if (eventsQueue == null) { return; } for (ScoreEvent eventWrapper : eventsQueue) { eventBus.dispatch(eventWrapper); } eventsQueue.clear(); } protected ExecutionStep loadExecutionStep(Execution execution) { RunningExecutionPlan runningExecutionPlan; if (execution != null) { // Optimization for external workers - run the content only without loading the execution plan if (execution.getSystemContext().get(TempConstants.CONTENT_EXECUTION_STEP) != null) { return (ExecutionStep) execution.getSystemContext().get(TempConstants.CONTENT_EXECUTION_STEP); } Long position = execution.getPosition(); if (position != null) { runningExecutionPlan = workerDbSupportService .readExecutionPlanById(execution.getRunningExecutionPlanId()); if (runningExecutionPlan != null) { updateMetadata(execution, runningExecutionPlan); ExecutionStep currStep = runningExecutionPlan.getExecutionPlan().getStep(position); if (logger.isDebugEnabled()) { logger.debug("Begin step: " + position + " in flow " + runningExecutionPlan.getExecutionPlan().getFlowUuid() + " [" + execution.getExecutionId() + "]"); } if (currStep != null) { return currStep; } } } } // If we got here - one of the objects was null throw new RuntimeException("Failed to load ExecutionStep!"); } private void updateMetadata(Execution execution, RunningExecutionPlan runningExecutionPlan) { Map<String, Serializable> executionMetadata = (Map<String, Serializable>) execution.getSystemContext() .getMetaData(); ExecutionPlan executionPlan = runningExecutionPlan.getExecutionPlan(); executionMetadata.put(ExecutionMetadataConsts.EXECUTION_PLAN_ID, executionPlan.getFlowUuid()); executionMetadata.put(ExecutionMetadataConsts.EXECUTION_PLAN_NAME, executionPlan.getName()); } protected void executeStep(Execution execution, ExecutionStep currStep) { try { Map<String, Object> stepData = prepareStepData(execution, currStep); reflectionAdapter.executeControlAction(currStep.getAction(), stepData); } catch (RuntimeException ex) { handleStepExecutionException(execution, ex); } } private static void handleStepExecutionException(Execution execution, RuntimeException ex) { logger.error("Error occurred during operation execution. Execution id: " + execution.getExecutionId(), ex); execution.getSystemContext().setStepErrorKey(ex.getMessage()); } private Map<String, Object> prepareStepData(Execution execution, ExecutionStep currStep) { Map<String, ?> actionData = currStep.getActionData(); Map<String, Object> stepData = new HashMap<>(); if (actionData != null) { stepData.putAll(actionData); } // We add all the contexts to the step data - so inside of each control action we will have access to all contexts addContextData(stepData, execution); return stepData; } private void createErrorEvent(String ex, String logMessage, String errorType, SystemContext systemContext) throws InterruptedException { HashMap<String, Serializable> eventData = new HashMap<>(); eventData.put(ExecutionParametersConsts.SYSTEM_CONTEXT, new HashMap<>(systemContext)); eventData.put(EventConstants.SCORE_ERROR_MSG, ex); eventData.put(EventConstants.SCORE_ERROR_LOG_MSG, logMessage); eventData.put(EventConstants.SCORE_ERROR_TYPE, errorType); ScoreEvent eventWrapper = new ScoreEvent(EventConstants.SCORE_ERROR_EVENT, eventData); eventBus.dispatch(eventWrapper); } protected void navigate(Execution execution, ExecutionStep currStep) throws InterruptedException { Long position; try { if (currStep.getNavigation() != null) { Map<String, Object> navigationData = new HashMap<>(currStep.getNavigationData()); // We add all the contexts to the step data - so inside of each control action we will have access to all contexts addContextData(navigationData, execution); position = (Long) reflectionAdapter.executeControlAction(currStep.getNavigation(), navigationData); execution.setPosition(position); } else { execution.setPosition(null); // terminate the flow - we got to the last step! } } catch (RuntimeException navEx) { // If Exception occurs in navigation (almost impossible since now we always have Flow Exception Step) we can not continue since we don't know which step is the next step... // terminating... logger.error("Error occurred during navigation execution. Execution id: " + execution.getExecutionId(), navEx); execution.getSystemContext().setStepErrorKey(navEx.getMessage()); // this is done only fo reporting execution.getSystemContext().setFlowTerminationType(ExecutionStatus.SYSTEM_FAILURE); execution.setPosition(null); // this ends the flow!!! try { createErrorEvent(navEx.getMessage(), "Error occurred during navigation execution ", EventConstants.SCORE_STEP_NAV_ERROR, execution.getSystemContext()); } catch (RuntimeException eventEx) { logger.error("Failed to create event: ", eventEx); } } } private static boolean useDefaultGroup(Execution execution) { Boolean useDefaultGroup = (Boolean) execution.getSystemContext().get(TempConstants.USE_DEFAULT_GROUP); if (useDefaultGroup == null) { return false; } return useDefaultGroup; } protected static void postExecutionSettings(Execution execution) { // Decide on Group String group = (String) execution.getSystemContext().get(TempConstants.ACTUALLY_OPERATION_GROUP); execution.setGroupName(group); if (isDebuggerMode(execution.getSystemContext())) { if (!StringUtils.isEmpty(group) && useDefaultGroup(execution)) { execution.setGroupName(null); } } //if there is a request to change the running execution plan id, we update the execution to the new execution plan ID Long requestForChangingExecutionPlan = execution.getSystemContext().pullRequestForChangingExecutionPlan(); if (requestForChangingExecutionPlan != null) { execution.setRunningExecutionPlanId(requestForChangingExecutionPlan); } } private static void addContextData(Map<String, Object> data, Execution execution) { data.putAll(execution.getContexts()); data.put(ExecutionParametersConsts.SYSTEM_CONTEXT, execution.getSystemContext()); data.put(ExecutionParametersConsts.EXECUTION_RUNTIME_SERVICES, execution.getSystemContext()); data.put(ExecutionParametersConsts.EXECUTION, execution); data.put(ExecutionParametersConsts.EXECUTION_CONTEXT, execution.getContexts()); data.put(ExecutionParametersConsts.RUNNING_EXECUTION_PLAN_ID, execution.getRunningExecutionPlanId()); } }