All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.simpleworkflow.flow.test.TestGenericWorkflowClient Maven / Gradle / Ivy

Go to download

The Amazon Web Services SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

The newest version!
/*
 * Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not
 * use this file except in compliance with the License. A copy of the License is
 * located at
 * 
 * http://aws.amazon.com/apache2.0
 * 
 * or in the "license" file accompanying this file. This file is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package com.amazonaws.services.simpleworkflow.flow.test;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CancellationException;

import com.amazonaws.services.simpleworkflow.flow.ChildWorkflowFailedException;
import com.amazonaws.services.simpleworkflow.flow.DecisionContext;
import com.amazonaws.services.simpleworkflow.flow.DecisionContextProvider;
import com.amazonaws.services.simpleworkflow.flow.DecisionContextProviderImpl;
import com.amazonaws.services.simpleworkflow.flow.SignalExternalWorkflowException;
import com.amazonaws.services.simpleworkflow.flow.StartChildWorkflowFailedException;
import com.amazonaws.services.simpleworkflow.flow.WorkflowClock;
import com.amazonaws.services.simpleworkflow.flow.WorkflowException;
import com.amazonaws.services.simpleworkflow.flow.common.FlowConstants;
import com.amazonaws.services.simpleworkflow.flow.core.Functor;
import com.amazonaws.services.simpleworkflow.flow.core.Promise;
import com.amazonaws.services.simpleworkflow.flow.core.Settable;
import com.amazonaws.services.simpleworkflow.flow.core.Task;
import com.amazonaws.services.simpleworkflow.flow.core.TryCatchFinally;
import com.amazonaws.services.simpleworkflow.flow.generic.ContinueAsNewWorkflowExecutionParameters;
import com.amazonaws.services.simpleworkflow.flow.generic.GenericActivityClient;
import com.amazonaws.services.simpleworkflow.flow.generic.GenericWorkflowClient;
import com.amazonaws.services.simpleworkflow.flow.generic.SignalExternalWorkflowParameters;
import com.amazonaws.services.simpleworkflow.flow.generic.StartChildWorkflowExecutionParameters;
import com.amazonaws.services.simpleworkflow.flow.generic.StartChildWorkflowReply;
import com.amazonaws.services.simpleworkflow.flow.generic.WorkflowDefinition;
import com.amazonaws.services.simpleworkflow.flow.generic.WorkflowDefinitionFactory;
import com.amazonaws.services.simpleworkflow.flow.generic.WorkflowDefinitionFactoryFactory;
import com.amazonaws.services.simpleworkflow.model.StartChildWorkflowExecutionFailedCause;
import com.amazonaws.services.simpleworkflow.model.UnknownResourceException;
import com.amazonaws.services.simpleworkflow.model.WorkflowExecution;
import com.amazonaws.services.simpleworkflow.model.WorkflowType;

public class TestGenericWorkflowClient implements GenericWorkflowClient {

    private static class StartChildWorkflowReplyImpl implements StartChildWorkflowReply {

        private final Settable result;

        private final String runId;

        private StartChildWorkflowReplyImpl(Settable result, String runId) {
            this.result = result;
            this.runId = runId;
        }

        @Override
        public String getRunId() {
            return runId;
        }

        @Override
        public Promise getResult() {
            return result;
        }
    }

    private final class ChildWorkflowTryCatchFinally extends TryCatchFinally {

        private final StartChildWorkflowExecutionParameters parameters;

        private final WorkflowExecution childExecution;

        private final Settable result;

        /**
         * Child workflow doesn't set result to ready state before completing
         * all its tasks. So we need to set external result only in doFinally.
         */
        private final Settable executeResult = new Settable();

        private final WorkflowDefinition childWorkflowDefinition;

        private final DecisionContext childContext;

        private boolean failed;

        private final Settable continueAsNew = new Settable();

        private ChildWorkflowTryCatchFinally(StartChildWorkflowExecutionParameters parameters, WorkflowExecution childExecution,
                WorkflowDefinition childWorkflowDefinition, DecisionContext context, Settable result) {
            this.parameters = parameters;
            this.childExecution = childExecution;
            this.childWorkflowDefinition = childWorkflowDefinition;
            this.childContext = context;
            this.result = result;
        }

        @Override
        protected void doTry() throws Throwable {
            executeResult.chain(childWorkflowDefinition.execute(parameters.getInput()));
        }

        @Override
        protected void doCatch(Throwable e) throws Throwable {
            failed = true;
            if (e instanceof WorkflowException) {
                WorkflowException we = (WorkflowException) e;
                throw new ChildWorkflowFailedException(0, childExecution, parameters.getWorkflowType(), e.getMessage(),
                        we.getDetails());
            }
            else if (e instanceof CancellationException) {
                throw e;
            }
            // Unless there is problem in the framework or generic workflow implementation this shouldn't be executed
            Exception failure = new ChildWorkflowFailedException(0, childExecution, parameters.getWorkflowType(), e.getMessage(),
                    "null");
            failure.initCause(e);
            throw failure;
        }

        @Override
        protected void doFinally() throws Throwable {
            if (!failed) {
                continueAsNew.set(childContext.getWorkflowContext().getContinueAsNewOnCompletion());
                if (continueAsNew.get() == null && executeResult.isReady()) {
                    result.set(executeResult.get());
                }
            }
            else {
                continueAsNew.set(null);
            }
            workflowExecutions.remove(this.childExecution.getWorkflowId());
        }

        public void signalRecieved(final String signalName, final String details) {
            if (getState() != State.TRYING) {
                throw new SignalExternalWorkflowException(0, childExecution, "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION");
            }
            new Task(this) {

                @Override
                protected void doExecute() throws Throwable {
                    childWorkflowDefinition.signalRecieved(signalName, details);
                }
            };
        }

        public StartChildWorkflowExecutionParameters getParameters() {
            return parameters;
        }

        public String getWorkflowState() throws WorkflowException {
            return childWorkflowDefinition.getWorkflowState();
        }

        public WorkflowExecution getWorkflowExecution() {
            return childExecution;
        }

        public Promise getContinueAsNew() {
            return continueAsNew;
        }

    }

    private final Map workflowExecutions = new HashMap();

    protected WorkflowDefinitionFactoryFactory factoryFactory;

    protected DecisionContextProvider decisionContextProvider;

    public TestGenericWorkflowClient(WorkflowDefinitionFactoryFactory factoryFactory,
            DecisionContextProvider decisionContextProvider) {
        this.factoryFactory = factoryFactory;
        this.decisionContextProvider = decisionContextProvider;
    }

    public TestGenericWorkflowClient(WorkflowDefinitionFactoryFactory factoryFactory) {
        this(factoryFactory, new DecisionContextProviderImpl());
    }

    public TestGenericWorkflowClient() {
        this(null, new DecisionContextProviderImpl());
    }

    public WorkflowDefinitionFactoryFactory getFactoryFactory() {
        return factoryFactory;
    }

    public void setFactoryFactory(WorkflowDefinitionFactoryFactory factoryFactory) {
        this.factoryFactory = factoryFactory;
    }

    public DecisionContextProvider getDecisionContextProvider() {
        return decisionContextProvider;
    }

    public void setDecisionContextProvider(DecisionContextProvider decisionContextProvider) {
        this.decisionContextProvider = decisionContextProvider;
    }

    @Override
    public Promise startChildWorkflow(final StartChildWorkflowExecutionParameters parameters) {
        Settable reply = new Settable();
        Settable result = new Settable();
        startChildWorkflow(parameters, reply, result);
        return reply;
    }

    private void startChildWorkflow(final StartChildWorkflowExecutionParameters parameters,
            final Settable reply, final Settable result) {
        String workflowId = parameters.getWorkflowId();
        WorkflowType workflowType = parameters.getWorkflowType();
        WorkflowExecution childExecution = new WorkflowExecution();
        final String runId = UUID.randomUUID().toString();
        //TODO: Validate parameters against registration options to find missing timeouts or other options
        try {
            DecisionContext parentDecisionContext = decisionContextProvider.getDecisionContext();
            if (workflowId == null) {
                workflowId = decisionContextProvider.getDecisionContext().getWorkflowClient().generateUniqueId();
            }
            childExecution.setWorkflowId(workflowId);
            childExecution.setRunId(runId);

            final GenericActivityClient activityClient = parentDecisionContext.getActivityClient();
            final WorkflowClock workflowClock = parentDecisionContext.getWorkflowClock();
            WorkflowDefinitionFactory factory;
            if (factoryFactory == null) {
                throw new IllegalStateException("required property factoryFactory is null");
            }
            factory = factoryFactory.getWorkflowDefinitionFactory(workflowType);
            if (factory == null) {
                String cause = StartChildWorkflowExecutionFailedCause.WORKFLOW_TYPE_DOES_NOT_EXIST.toString();
                throw new StartChildWorkflowFailedException(0, childExecution, workflowType, cause);
            }
            TestWorkflowContext workflowContext = new TestWorkflowContext();
            workflowContext.setWorkflowExecution(childExecution);
            workflowContext.setWorkflowType(parameters.getWorkflowType());
            workflowContext.setParentWorkflowExecution(parentDecisionContext.getWorkflowContext().getWorkflowExecution());
            workflowContext.setTagList(parameters.getTagList());
            workflowContext.setTaskList(parameters.getTaskList());
            DecisionContext context = new TestDecisionContext(activityClient, TestGenericWorkflowClient.this, workflowClock,
                    workflowContext);
            //this, parameters, childExecution, workflowClock, activityClient);
            final WorkflowDefinition childWorkflowDefinition = factory.getWorkflowDefinition(context);
            final ChildWorkflowTryCatchFinally tryCatch = new ChildWorkflowTryCatchFinally(parameters, childExecution,
                    childWorkflowDefinition, context, result);
            workflowContext.setRootTryCatch(tryCatch);
            ChildWorkflowTryCatchFinally currentRun = workflowExecutions.get(workflowId);
            if (currentRun != null) {
                String cause = StartChildWorkflowExecutionFailedCause.WORKFLOW_ALREADY_RUNNING.toString();
                throw new StartChildWorkflowFailedException(0, childExecution, workflowType, cause);
            }
            workflowExecutions.put(workflowId, tryCatch);
            continueAsNewWorkflowExecution(tryCatch, result);
        }
        catch (StartChildWorkflowFailedException e) {
            throw e;
        }
        catch (Throwable e) {
            // This cause is chosen to represent internal error for sub-workflow creation
            String cause = StartChildWorkflowExecutionFailedCause.OPEN_CHILDREN_LIMIT_EXCEEDED.toString();
            StartChildWorkflowFailedException failure = new StartChildWorkflowFailedException(0, childExecution, workflowType,
                    cause);
            failure.initCause(e);
            throw failure;
        }
        finally {
            reply.set(new StartChildWorkflowReplyImpl(result, runId));
        }
    }

    private void continueAsNewWorkflowExecution(final ChildWorkflowTryCatchFinally tryCatch, final Settable result) {
        // It is always set to ready with null if no continuation is necessary
        final Promise continueAsNew = tryCatch.getContinueAsNew();
        new Task(continueAsNew) {

            @Override
            protected void doExecute() throws Throwable {
                ContinueAsNewWorkflowExecutionParameters cp = continueAsNew.get();
                if (cp == null) {
                    return;
                }
                StartChildWorkflowExecutionParameters nextParameters = new StartChildWorkflowExecutionParameters();
                nextParameters.setInput(cp.getInput());
                WorkflowExecution previousWorkflowExecution = tryCatch.getWorkflowExecution();
                String workflowId = previousWorkflowExecution.getWorkflowId();
                nextParameters.setWorkflowId(workflowId);
                StartChildWorkflowExecutionParameters previousParameters = tryCatch.getParameters();
                nextParameters.setWorkflowType(previousParameters.getWorkflowType());
                long startToClose = cp.getExecutionStartToCloseTimeoutSeconds();
                if (startToClose == FlowConstants.NONE) {
                    startToClose = previousParameters.getExecutionStartToCloseTimeoutSeconds();
                }
                nextParameters.setExecutionStartToCloseTimeoutSeconds(startToClose);
                long taskStartToClose = cp.getTaskStartToCloseTimeoutSeconds();
                if (taskStartToClose == FlowConstants.NONE) {
                    taskStartToClose = previousParameters.getTaskStartToCloseTimeoutSeconds();
                }
                nextParameters.setTaskStartToCloseTimeoutSeconds(taskStartToClose);
                Settable reply = new Settable();
                startChildWorkflow(nextParameters, reply, result);
            }

        };
    }

    @Override
    public Promise startChildWorkflow(String workflow, String version, String input) {
        StartChildWorkflowExecutionParameters parameters = new StartChildWorkflowExecutionParameters();
        WorkflowType workflowType = new WorkflowType().withName(workflow).withVersion(version);
        parameters.setWorkflowType(workflowType);
        parameters.setInput(input);
        Settable reply = new Settable();
        Settable result = new Settable();
        startChildWorkflow(parameters, reply, result);
        return result;
    }

    @Override
    public Promise startChildWorkflow(final String workflow, final String version, final Promise input) {
        return new Functor(input) {

            @Override
            protected Promise doExecute() throws Throwable {
                return startChildWorkflow(workflow, version, input.get());
            }
        };
    }

    @Override
    public Promise signalWorkflowExecution(final SignalExternalWorkflowParameters signalParameters) {
        WorkflowExecution signaledExecution = new WorkflowExecution();
        signaledExecution.setWorkflowId(signalParameters.getWorkflowId());
        signaledExecution.setRunId(signalParameters.getRunId());
        final ChildWorkflowTryCatchFinally childTryCatch = workflowExecutions.get(signalParameters.getWorkflowId());
        if (childTryCatch == null) {
            throw new SignalExternalWorkflowException(0, signaledExecution, "UNKNOWN_EXTERNAL_WORKFLOW_EXECUTION");
        }
        String openExecutionRunId = childTryCatch.getWorkflowExecution().getRunId();
        if (signalParameters.getRunId() != null && !openExecutionRunId.equals(signalParameters.getRunId())) {
            throw new SignalExternalWorkflowException(0, signaledExecution, "Unknown Execution (runId doesn't match)");
        }
        childTryCatch.signalRecieved(signalParameters.getSignalName(), signalParameters.getInput());
        return Promise.Void();
    }

    @Override
    public void requestCancelWorkflowExecution(WorkflowExecution execution) {
        String workflowId = execution.getWorkflowId();
        if (workflowId == null) {
            throw new IllegalArgumentException("null workflowId");
        }
        final ChildWorkflowTryCatchFinally childTryCatch = workflowExecutions.get(workflowId);
        if (childTryCatch == null) {
            throw new UnknownResourceException("Unknown excution: " + execution.toString());
        }
        String openExecutionRunId = childTryCatch.getWorkflowExecution().getRunId();
        if (execution.getRunId() != null && !openExecutionRunId.equals(execution.getRunId())) {
            throw new UnknownResourceException("Unknown Execution (runId doesn't match)");
        }
        childTryCatch.cancel(new CancellationException());
    }

    public String getWorkflowState(WorkflowExecution execution) throws WorkflowException {
        String workflowId = execution.getWorkflowId();
        if (workflowId == null) {
            throw new IllegalArgumentException("null workflowId");
        }
        final ChildWorkflowTryCatchFinally childTryCatch = workflowExecutions.get(workflowId);
        if (childTryCatch == null) {
            throw new UnknownResourceException(execution.toString());
        }
        String openExecutionRunId = childTryCatch.getWorkflowExecution().getRunId();
        if (execution.getRunId() != null && !openExecutionRunId.equals(execution.getRunId())) {
            throw new UnknownResourceException("Unknown Execution (runId doesn't match)");
        }
        return childTryCatch.getWorkflowState();
    }

    @Override
    public void continueAsNewOnCompletion(ContinueAsNewWorkflowExecutionParameters parameters) {
        DecisionContext decisionContext = decisionContextProvider.getDecisionContext();
        decisionContext.getWorkflowContext().setContinueAsNewOnCompletion(parameters);
    }

    @Override
    public String generateUniqueId() {
        return UUID.randomUUID().toString();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy