

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS Flow Framework for Java のタスクについて
<a name="details"></a>

**Topics**
+ [タスク](#details.task)
+ [実行順](#details.order)
+ [ワークフロー実行](#details.workflow)
+ [非決定論](#details.non)

## タスク
<a name="details.task"></a>

 AWS Flow Framework for Java が非同期コードの実行を管理するために使用する基盤となるプリミティブは `Task` クラスです。`Task` 型のオブジェクトは、非同期に実行する必要がある仕事を表します。非同期メソッドを呼び出すと、フレームワークでは、そのメソッドのコードを実行するための `Task` を作成し、それをリストに入れて後で実行できるようにします。同様に、`Activity` を呼び出すと、それに対する `Task` が作成されます。この後でメソッド呼び出しが戻り、通常、呼び出しの将来の結果として `Promise<T>` を返します。

`Task` クラスはパブリックであり、直接使用できます。たとえば、Hello World の例を書き換えて、非同期メソッドの代わりに `Task` を使用できます。

```
@Override
public void startHelloWorld(){
       final Promise<String> greeting = client.getName();
        new Task(greeting) {
        @Override
        protected void doExecute() throws Throwable {
        	client.printGreeting("Hello " + greeting.get() +"!");
        }
    };
}
```

フレームワークでは、`Task` のコンストラクタに渡したすべての `Promise` が準備完了状態になると、`doExecute()` メソッドを呼び出します。`Task` クラスの詳細については、 AWS SDK for Java ドキュメントを参照してください。

また、フレームワークには `Functor` というクラスも含まれています。このクラスは、`Promise<T>` でもある `Task` を表します。`Functor` オブジェクトは、`Task` が完了すると、準備完了状態になります。次の例では、挨拶メッセージを取得するための `Functor` を作成しています。

```
Promise<String> greeting = new Functor<String>() {
    @Override
    protected Promise<String> doExecute() throws Throwable {
        return client.getGreeting();
    }
};
client.printGreeting(greeting);
```

## 実行順
<a name="details.order"></a>

タスクを実行できるのは、対応する非同期メソッドやアクティビティに渡したすべての `Promise<T>` で型指定されたパラメータが準備完了状態になった場合に限ります。実行準備が完了した `Task` は、準備完了キューに論理的に移動されます。つまり、実行が予定されます。ワーカークラスは、タスクを実行するために、ユーザーが非同期メソッドの本文を記述したコードを呼び出します。または、アクティビティメソッドの場合は、アクティビティタスクを Amazon Simple Workflow Service (AWS) でスケジュールします。

タスクが実行されて結果が生成されると、それに伴って他のタスクが準備完了状態となり、プログラムが続行されます。フレームワークでタスクを実行する方法は、非同期コードの実行順を理解するために重要です。コードは、プログラムに表示される順で実際に実行されるとは限りません。

```
Promise<String> name = getUserName();
printHelloName(name);
printHelloWorld();
System.out.println("Hello, Amazon!");

@Asynchronous
private Promise<String> getUserName(){
	return Promise.asPromise("Bob");
}
@Asynchronous
private void printHelloName(Promise<String> name){
	System.out.println("Hello, " + name.get() + "!");
}
@Asynchronous
private void printHelloWorld(){
	System.out.println("Hello, World!");
}
```

上のコードの出力は以下のとおりです。

```
Hello, Amazon!
Hello, World!
Hello, Bob
```

これは期待と異なる結果ですが、非同期メソッドのタスクがどのように実行されたかを考えると簡単に説明できます。

1. `getUserName` への呼び出しで `Task` が作成されます。これを `Task1` とします。`getUserName` はパラメータを取らないため、 `Task1`はすぐに準備完了キューに入れられます。

1. 次に、`printHelloName` への呼び出しで作成される `Task` では、`getUserName` の結果を待つ必要があります。これを `Task2` とします。必要な値はまだ準備されていないため、 `Task2`は待機リストに入れられます。

1. 次に、` printHelloWorld` のタスクが作成されて、準備完了キューに追加されます。これを `Task3` とします。

1. その後、`println` ステートメントで「Hello, Amazon\$1」が コンソールに出力されます。

1. この時点で、`Task1` と `Task3` は準備完了キューにあり、`Task2` は待機リストにあります。

1. ワーカーによって `Task1` が実行され、その結果によって `Task2` が準備状態になります。`Task2` は、`Task3` の背景のキューに追加されます。

1. `Task3` と `Task2` が、この順序で実行されます。

アクティビティの実行も同じパターンに従います。アクティビティクライアントでメソッドを呼び出すと、`Task` が作成され、これが実行されると、Amazon SWF でアクティビティがスケジュールされます。

フレームワークは、コード生成や動的プロキシなどの機能に依存することで、メソッド呼び出しをアクティビティ呼び出しや非同期タスクに変換するロジックをプログラムに挿入します。

## ワークフロー実行
<a name="details.workflow"></a>

ワークフロー実装の実行もワーカークラスによって管理されます。ワークフロークライアントでメソッドを呼び出すと、Amazon SWF が呼び出されてワークフローインスタンスが作成されます。Amazon SWF のタスクはフレームワークのタスクとは異なるので、混同しないでください。Amazon SWF のタスクは、アクティビティタスクまたは決定タスクです。アクティビティタスクの実行はシンプルです。アクティビティワーカークラスは、Amazon SWF からアクティビティタスクを受け取り、実装の適切なアクティビティメソッドを呼び出して、その結果を Amazon SWF に返します。

決定タスクの実行はもう少し複雑です。ワークフローワーカーは Amazon SWF から決定タスクを受け取ります。決定タスクは、実質的には、ワークフローロジックに次に何をするかを問い合わせるリクエストです。ワークフロークライアントを通じてワークフローインスタンスが開始されると、最初の決定タスクが生成されます。この決定タスクを受け取ると、フレームワークは `@Execute` 注釈が設定されたワークフローメソッドでコードの実行を開始します。このメソッドは、アクティビティをスケジュールする調整ロジックを実行します。ワークフローインスタンスの状態が変わると (例えばアクティビティが完了する)、さらに決定タスクがスケジュールされます。この時点で、ワークフローロジックはアクティビティの結果に基づいてアクションを実行することを決定できます。たとえば、別のアクティビティをスケジュールすることを決定できます。

フレームワークは、決定タスクをワークフローロジックにシームレスに変換することで、これらすべての詳細を開発者から隠します。開発者からは、コードが通常のプログラムのように見えます。表面下では、フレームワークは Amazon SWF に保持されている履歴を使用して、コードを Amazon SWF や決定タスクへの呼び出しにマッピングします。決定タスクが到着すると、フレームワークはプログラムの実行を再生し、その結果を現時点までの完了済みアクティビティの結果に追加します。これらの結果を待機していた非同期メソッドやアクティビティがブロック解除され、プログラムの実行が先に進みます。

イメージ処理ワークフロー例の実行および対応する履歴を次の表に示します。


**サムネイルワークフローの実行**  

| ワークフロープログラムの実行 | Amazon SWF で管理される履歴  | 
| --- | --- | 
| 最初の実行 | 
|  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  | 
| リプレイ | 
|  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  | 
| リプレイ | 
|  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  | 
| リプレイ | 
|  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/amazonswf/latest/awsflowguide/details.html)  | 

`processImage` を呼び出すと、フレームワークは Amazon SWF に新しいワークフローインスタンスを作成します。これは、開始するワークフローインスタンスの永続的なレコードです。プログラムは、`downloadImage` アクティビティへの呼び出しまで実行されます。この呼び出しでアクティビティをスケジュールすることを Amazon SWF にリクエストします。ワークフローはさらに実行され、後続のアクティビティのタスクを作成しますが、`downloadImage` アクティビティが完了するまで実行できません。したがって、このリプレイのエピソードは終了します。Amazon SWF は、実行のための `downloadImage` アクティビティのタスクをディスパッチし、タスクが完了すると、結果とともに履歴にレコードが作成されます。ワークフローは続行の準備が完了し、決定タスクが Amazon SWF で生成されます。フレームワークは決定タスクを受け取ってワークフローを再生し、その結果を履歴に記録されたダウンロード済みイメージの結果に追加します。これに伴って `createThumbnail` のタスクがブロック解除され、プログラムは Amazon SWF の `createThumbnail` アクティビティタスクがスケジュールされることで続行します。同じプロセスが `uploadImage` で繰り返されます。このようにプログラムの実行が継続され、最終的にワークフローですべてのイメージが処理され、保留中のタスクがなくなります。実行状態はローカルに保存されないため、各決定タスクは別のマシンで実行される可能性があります。これにより、フォールトトレラントでスケーラブルなプログラムを簡単に記述できます。

## 非決定論
<a name="details.non"></a>

フレームワークはリプレイに依存するため、オーケストレーションコード (アクティビティ実装を除くすべてのワークフローコード) が決定的であることが重要です。たとえば、プログラムの制御フローは乱数や現在の時間に依存すべきではありません。これらのモノは呼び出し間で変わるため、リプレイはオーケストレーションロジックの同じパスをたどらない場合があります。そのため、予期しない結果やエラーの原因となります。現在の時間を決定論的に取得するには、フレームワークが提供する `WorkflowClock` を使用できます。詳細については、「[実行コンテキスト](executioncontext.md)」セクションを参照してください。

**注記**  
Spring のワークフロー実装オブジェクトのワイヤリングが不正確である場合にも非決定論につながることがあります。ワークフロー実装の Bean およびこれらが依存する Bean は、ワークフロースコープ (`WorkflowScope`) に存在する必要があります。たとえば、ワークフロー実装の Bean を、グローバルコンテキストで状態を保持する Bean にワイヤリングすると、予期しない動作が発生します。詳細については、「[Spring との統合](test.md#test.spring)」セクションを参照してください。