Did this page help you?

   Yes   No   Tell us about it...

com.amazonaws.services.simpleworkflow.flow.core
Class TryCatchFinally

java.lang.Object
  extended by com.amazonaws.services.simpleworkflow.flow.core.TryCatchFinally
All Implemented Interfaces:
Cancelable
Direct Known Subclasses:
TryCatch, TryFinally

public abstract class TryCatchFinally
extends Object
implements Cancelable

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 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(Throwable) methods serve as parent scopes for tasks created during their execution.

For example if any Task (or method annotated with 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(Throwable) is called. Through this cancellation mechanism it is guaranteed that doCatch(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(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 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(Throwable) with 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(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(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(Throwable) method. In case of explicit cancellation any exception including CancellationException that is rethrown from doCatch(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 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.Task(boolean, Promise...) constructor to mark a Task as daemon. Use 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, 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(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 CancellationException the last exception "wins". I.e. it becomes the exception delivered to doCatch(Throwable) method.

Note that instances of Promise do not participate in error handling the way 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);
 }
 


Nested Class Summary
static class TryCatchFinally.State
           
 
Constructor Summary
TryCatchFinally()
           
TryCatchFinally(com.amazonaws.services.simpleworkflow.flow.core.AsyncContextAware parent, boolean daemon, Promise<?>... waitFor)
           
TryCatchFinally(com.amazonaws.services.simpleworkflow.flow.core.AsyncContextAware parent, Promise<?>... waitFor)
           
TryCatchFinally(boolean daemon, Promise<?>... waitFor)
           
TryCatchFinally(Promise<?>... waitFor)
           
 
Method Summary
 void cancel(Throwable cause)
           
 List<AsyncTaskInfo> getAsynchronousStackTraceDump()
           
 String getAsynchronousStackTraceDumpAsString()
           
 String getName()
           
 StackTraceElement[] getStackTrace()
           
 TryCatchFinally.State getState()
           
 boolean isCancelRequested()
           
 void setName(String name)
           
 String toString()
           
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Constructor Detail

TryCatchFinally

public TryCatchFinally()

TryCatchFinally

public TryCatchFinally(Promise<?>... waitFor)

TryCatchFinally

public TryCatchFinally(boolean daemon,
                       Promise<?>... waitFor)

TryCatchFinally

public TryCatchFinally(com.amazonaws.services.simpleworkflow.flow.core.AsyncContextAware parent,
                       boolean daemon,
                       Promise<?>... waitFor)

TryCatchFinally

public TryCatchFinally(com.amazonaws.services.simpleworkflow.flow.core.AsyncContextAware parent,
                       Promise<?>... waitFor)
Method Detail

getName

public String getName()

setName

public void setName(String name)

cancel

public void cancel(Throwable cause)
Specified by:
cancel in interface Cancelable

isCancelRequested

public boolean isCancelRequested()
Specified by:
isCancelRequested in interface Cancelable

getStackTrace

public StackTraceElement[] getStackTrace()

getAsynchronousStackTraceDump

public List<AsyncTaskInfo> getAsynchronousStackTraceDump()

getAsynchronousStackTraceDumpAsString

public String getAsynchronousStackTraceDumpAsString()

getState

public TryCatchFinally.State getState()

toString

public String toString()
Overrides:
toString in class Object


Copyright © 2010 Amazon Web Services, Inc. All Rights Reserved.