Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2012 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.core;
 
 import java.util.List;
 
Asynchronous equivalent of synchronous try-catch-finally.

Overview

SWF Flow error handling relies on the idea that any asynchronous task (which includes methods annotated as com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous) is executed in a parent context. In the case of an exception being thrown from a task, all sibling tasks that share the same parent context are canceled. After successful cancellation the exception is propagated to the parent context for handling.

TryCatchFinally doTry(), doFinally() and doCatch(java.lang.Throwable) methods serve as parent scopes for tasks created during their execution.

For example if any Task (or method annotated with com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous) that originates (possibly indirectly through parent task) in doTry() throws an exception all other tasks that originated in doTry() are canceled and only then doCatch(java.lang.Throwable) is called. Through this cancellation mechanism it is guaranteed that doCatch(java.lang.Throwable) is called at most once even if there are multiple parallel tasks executing in the doTry() scope.

If failure happens in a task originated in doCatch(java.lang.Throwable) then all its siblings are canceled first and then doFinally() is called.

The same happens if task originated in doFinally() fails. All it siblings are canceled and then parent scope of the TryCatchFinally is given chance to handle the exception.

Cancellation

The cancellation semantic depends on the task implementation. Task (or method annotated with com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous) that has not started execution is never given chance to execute after cancellation. Task which is already executing is not interrupted and completes (or fails). ExternalTask cancellation depends on the external resource. For example SWF activities and child workflows that are modeled as ExternalTasks are canceled through SWF API.

When TryCatchFinally itself is canceled because of sibling task failure it handles its own cancellation in the following order:

  1. If doTry hasn't started yet then TryCatchFinally is considered canceled immediately.
  2. If are any outstanding task that originated in doTry() then cancellation of all of them is requested.
  3. After all tasks originated in doTry are canceled call doCatch(java.lang.Throwable) with java.util.concurrent.CancellationException.
  4. After all tasks originated in doCatch are completed call doFinally().
  5. After all tasks originated in doFinally() are completed consider TryCathFinally canceled

doCatch(java.lang.Throwable) and doFinally() are not cancelable. It means that cancellation request always waits for completion of all tasks that originate in these methods. Special care should be taken when writing code in doCatch(java.lang.Throwable) and in doFinally() to ensure that in all possible failure scenarios they don't end up in a stuck state.

TryCatchFinally can be canceled explicitly through cancel(java.lang.Throwable) method. In case of explicit cancellation any exception including java.util.concurrent.CancellationException that is rethrown from doCatch(java.lang.Throwable) or doFinally() is propagated to the parent context.

Daemon Tasks

It is pretty common to have tasks that have their lifecycle linked to some other tasks. For example notification activity that is sent out if some other activity is not completed in a specified period of time. The timer and the notification activity should be canceled as soon as the monitored activity is completed. Such use case can be supported by wrapping timer and notification activity in TryCatchFinally which is explicitly canceled upon monitored activity completion. The more convenient way to perform such cleanup is by marking a Task (or method annotated with com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous) as daemon. The asynchronous scope in absence of failures is executed in the following sequence:

  1. The scope method (for example doTry()) is executed.
  2. All tasks that are created during its execution are executed (when Promises they are waiting on become ready) possibly creating more tasks.
  3. When all non daemon tasks originated in the scope method are completed cancellation requests are sent to all daemon siblings.
  4. The scope is completed after all daemon tasks are canceled.

Pass true to the first argument of Task.(boolean,com.amazonaws.services.simpleworkflow.flow.core.Promise[]) constructor to mark a Task as daemon. Use com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous.daemon() annotation parameter to mark an asynchronous method as daemon. Any task that is created during execution of a daemon task is marked as daemon. TryCatchFinally also can be marked as daemon through TryCatchFinally(boolean,com.amazonaws.services.simpleworkflow.flow.core.Promise[]) constructor or by by being created by a daemon task. Note that TryCatchFinally doesn't pass its daemon flag to tasks created in doTry(), doCatch(java.lang.Throwable) and doFinally(). It is because each of these methods acts as a separate asynchronous scope and the rules of execution described above apply to them.

Miscellaneous

In case of multiple simultaneous exceptions (which is possible for external tasks) or if cancellation of a child results in any exception that is not java.util.concurrent.CancellationException the last exception "wins". I.e. it becomes the exception delivered to doCatch(java.lang.Throwable) method.

Note that instances of Promise do not participate in error handling the way java.util.concurrent.Future does. If method that returns Promise throws an exception the Promise state and return value are not changed. It is similar to behavior of variables in case of synchronous code.

Usage Examples

Basic error handling:

 new TryCatchFinally() {
 
     final List<Promise<String>> instances = new ArrayList<Promise<String>>();
 
     protected void doTry() throws Throwable {
         for (int i = 0; i < count; i++) {
             Promise<String> instanceId = ec2.startInstance();
             instances.add(instanceId);
         }
         performSimulation(instances);
     }
 
     protected void doCatch(Throwable e) throws Throwable {
         mail.notifySimulationFailure(e);
     }
 
     protected void doFinally() throws Throwable {
         for (int i = 0; i < count; i++) {
             Promise<String> instanceId = instances.get(i);
             if (instanceId.isReady()) {
                 ec2.stopInstance(instanceId.get());
             }
         }
     }
 };
 

Daemon example:

 
 
 protected void doTry() throws Throwable {
     for (int i = 0; i < count; i++) {
         Promise<String> instanceId = ec2.startInstance();
         instances.add(instanceId);
     }
     performSimulation(instances);
     notifyOnDelay();
 }
 
 @Asynchronous(daemon = true)
 public void notifyOnDelay() {
     Promise<Void> timer = clock.scheduleTimer(3600);
     mail.notifyDelay(timer);
 }
 

Author(s):
fateev
public abstract class TryCatchFinally extends AsyncContextAware implements Cancelable {
    public enum State {
        CREATED,
        TRYING,
        CATCHING,
        FINALIZING,
        CLOSED
    }
    private final TryCatchFinallyContext context;
    public TryCatchFinally() {
        this(nullnull, 7, null);
    }
    public TryCatchFinally(Promise<?>... waitFor) {
        this(nullnull, 7, waitFor);
    }
    public TryCatchFinally(boolean daemon, Promise<?>... waitFor) {
        this(daemonnull, 7, waitFor);
    }
    public TryCatchFinally(AsyncContextAware parentboolean daemon, Promise<?>... waitFor) {
         = new TryCatchFinallyContext(parent.getContext(), thisdaemonnull, 5, waitFor);
    }
    public TryCatchFinally(AsyncContextAware parent, Promise<?>... waitFor) {
         = new TryCatchFinallyContext(parent.getContext(), thisnullnull, 5, waitFor);
    }
    protected TryCatchFinally(Boolean daemonString parentTaskMethodNameint skipStackLines, Promise<?>[] waitFor) {
         = new TryCatchFinallyContext(thisdaemonparentTaskMethodNameskipStackLineswaitFor);
    }
    protected TryCatchFinally(AsyncContextAware parentBoolean daemonString parentTaskMethodNameint skipStackLines,
            Promise<?>[] waitFor) {
         = new TryCatchFinallyContext(parent.getContext(), thisdaemonparentTaskMethodNameskipStackLineswaitFor);
    }
    public String getName() {
        return .getName();
    }
    public void setName(String name) {
        .setName(name);
    }
    @Override
        return ;
    }
    public void cancel(Throwable cause) {
        .cancel(cause);
    }
    public boolean isCancelRequested() {
        return .isCancelRequested();
    }
    public StackTraceElement[] getStackTrace() {
        return .getStackTrace().getStackTrace();
    }
        List<AsyncTaskInforesult = new ArrayList<AsyncTaskInfo>();
        .getAsynchronousStackTraceDump(result);
        return result;
    }
    }
    public State getState() {
        return .getState();
    }
    @Override
    public String toString() {
        return .toString();
    }
    protected void rethrow(final Throwable e, Promise<?>... waitFor) {
        new Task(waitFor) {
            @Override
            protected void doExecute() throws Throwable {
                throw e;
            }
        };
    }
    protected abstract void doTry() throws Throwable;
    protected abstract void doCatch(Throwable ethrows Throwable;
    protected abstract void doFinally() throws Throwable;
New to GrepCode? Check out our FAQ X