

 适用于 Java 的 Amazon SDK 1.x于2025年 end-of-support 12月31日达到。我们建议您迁移到 [Amazon SDK for Java 2.x](https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/home.html) 以继续获得新功能、可用性改进和安全更新。

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 构建简单 Amazon SWF 应用程序
<a name="swf-hello"></a>

此主题探讨如何使用适用于 Java 的 Amazon SDK 编写 [Amazon SWF](https://www.amazonaws.cn/swf/) 应用程序，并在此过程中介绍了一些重要概念。

## 关于示例
<a name="about-the-example"></a>

示例项目将创建带有一个活动的工作流，接受通过 Amazon Cloud 传递的工作流数据（在 HelloWorld 的传统中，这应该是要问候的某个人的名字）并在响应中打印问候语。

虽然表面上看起来这非常简单，不过 Amazon SWF 应用程序由多个协同工作的部件组成：
+ 一个**域**，用作工作流执行数据的逻辑容器。
+ 一个或多个**工作流程**，它们表示代码组件，这些组件定义工作流程的活动和子工作流程执行的逻辑顺序。
+ 一个**工作流工作线程**，也称为*决策程序*，轮询决策任务并在响应中计划活动或子工作流。
+ 一个或多个**活动**，每个活动表示工作流中的一个工作单元。
+ 一个**活动工作线程**，轮询活动任务并在响应中运行活动方法。
+ 一个或多个**任务列表**，这是由 Amazon SWF 维护的队列，用于发布请求到工作流和活动工作线程。任务列表上用于工作流工作线程的任务称为*决策任务*。用于活动工作线程的任务称为*活动任务*。
+ 一个**工作流启动程序**，用于开始工作流的执行。

在后台，Amazon SWF 协调这些组件的操作，协调从 Amazon Cloud 的传输，在它们之间传递数据，处理超时和检测信号通知，以及记录工作流执行历史记录。

## 先决条件
<a name="prerequisitesswf"></a>

### 开发环境
<a name="development-environment"></a>

此教程中使用的开发环境包括：
+ -[适用于 Java 的 Amazon SDK](https://www.amazonaws.cn/sdk-for-java/) 。
+  [Apache Maven](http://maven.apache.org/) (3.3.1)。
+ JDK 1.7 或更高版本。本教程使用 JDK 1.8.0 开发和测试。
+ 一个适用的 Java 文本编辑器 (由您选择)。

**注意**  
如果您使用的构建系统不是 Maven，则仍可以使用适用于您环境的相应步骤创建项目，并在这个过程中使用此处提供的概念。适用于 Java 的 Amazon SDK入门[中提供了在不同编译系统中配置和使用 ](getting-started.md) 的更多信息。  
与此类似，但需要更多工作，此处列出的步骤也可以使用支持 Amazon SWF 的任意 Amazon SDK 实施。

所有必需的外部依赖项包括在适用于 Java 的 Amazon SDK 中，因此无需下载其他内容。

### Amazon 访问
<a name="aws-access"></a>

要成功完成本教程，您必须有权访问 Amazon 访问门户，如本指南的[基本设置部分所述](signup-create-iam-user.md#signup-create-iam-user-overview)。

这些说明描述了如何访问您复制并粘贴到本地共享 `credentials` 文件中的临时凭证。您粘贴的临时凭证必须与 Amazon IAM Identity Center 中有权访问 Amazon SWF 的 IAM 角色关联。粘贴临时凭证后，您的 `credentials` 文件将类似于以下内容。

```
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
aws_session_token=IQoJb3JpZ2luX2IQoJb3JpZ2luX2IQoJb3JpZ2luX2IQoJb3JpZ2luX2IQoJb3JpZVERYLONGSTRINGEXAMPLE
```

这些临时凭证与 `default` 配置文件相关联。

## 创建 SWF 项目
<a name="create-a-swf-project"></a>

1. 使用 Maven 启动新项目：

   ```
   mvn archetype:generate -DartifactId=helloswf \
   -DgroupId=aws.example.helloswf -DinteractiveMode=false
   ```

   这将创建具有标准 maven 项目结构的新项目：

   ```
   helloswf
   ├── pom.xml
   └── src
       ├── main
       │   └── java
       │       └── aws
       │           └── example
       │               └── helloswf
       │                   └── App.java
       └── test
           └── ...
   ```

   您可以忽略或删除 `test` 目录及其中包含的所有内容，我们不会将其用于此教程。您还可以删除 `App.java`，因为我们将使用新类来替换它。

1. 编辑项目的 `pom.xml` 文件，通过将 **aws-java-sdk-simpleworkflow** 模块的依赖项添加到 `<dependencies>` 块中来添加该模块。

   ```
   <dependencies>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-java-sdk-simpleworkflow</artifactId>
       <version>1.11.1000</version>
     </dependency>
   </dependencies>
   ```

1.  *确保 Maven 使用 JDK 1.7\$1 支持构建您的项目*。将以下内容添加到您项目 (在 `<dependencies>` 块之前或之后) 的 `pom.xml` 中：

   ```
   <build>
     <plugins>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.6.1</version>
         <configuration>
             <source>1.8</source>
             <target>1.8</target>
         </configuration>
       </plugin>
     </plugins>
   </build>
   ```

## 编码项目
<a name="code-the-project"></a>

示例项目包括四个独立的应用程序，我们将逐个查看：
+  **HelloTypes.java** -- 包含项目的域、活动和工作流类型数据，与其他组件共享。它还处理这些类型在 SWF 中的注册。
+  **ActivityWorker.java** -- 包含活动工作线程，将轮询活动任务并在响应中运行活动。
+  **WorkflowWorker.java** -- 包含工作流工作线程（决策程序），将轮询决策任务并计划新活动。
+  **WorkflowStarter.java** -- 包含工作流启动程序，将启动新的工作流执行，这将导致 SWF 开始生成决策和工作流任务供工作线程使用。

### 所有源文件的常见步骤
<a name="swf-hello-common"></a>

您创建的用于托管 Java 类的所有文件都有几个共同点。出于时间考虑，这些步骤*在每次添加新文件到项目时是隐含的*：

1. 在项目的 `src/main/java/aws/example/helloswf/` 目录中创建文件。

1. 添加 `package` 声明到每个文件的开头用于声明其命名空间。示例项目使用：

   ```
   package aws.example.helloswf;
   ```

1. 为 [AmazonSimpleWorkflowClient](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/AmazonSimpleWorkflowClient.html) 类和 `com.amazonaws.services.simpleworkflow.model` 命名空间中的多个类添加 `import` 声明。为了简化操作，我们使用：

   ```
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClientBuilder;
   import com.amazonaws.services.simpleworkflow.model.*;
   ```

### 注册域、工作流程和活动类型
<a name="swf-hello-hellotypes"></a>

我们将从创建新的可执行类 `HelloTypes.java` 开始。此文件将包含共享数据，您的工作流中的不同部分需要这些数据，例如活动的名称和版本以及工作流类型，域名和任务列表名称。

1. 打开文本编辑器并创建文件 `HelloTypes.java`，添加程序包声明并根据[通用步骤](#swf-hello-common)导入。

1. 声明 `HelloTypes` 类并向其提供值，以供注册的活动和工作流类型使用：

   ```
       public static final String DOMAIN = "HelloDomain";
       public static final String TASKLIST = "HelloTasklist";
       public static final String WORKFLOW = "HelloWorkflow";
       public static final String WORKFLOW_VERSION = "1.0";
       public static final String ACTIVITY = "HelloActivity";
       public static final String ACTIVITY_VERSION = "1.0";
   ```

   这些值将在代码中使用。

1. 在字符串声明之后，创建 [AmazonSimpleWorkflowClient](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/AmazonSimpleWorkflowClient.html) 类的实例。这是由Amazon SWF向 适用于 Java 的 Amazon SDK 方法提供的基本接口。

   ```
   private static final AmazonSimpleWorkflow swf =
       AmazonSimpleWorkflowClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
   ```

   前面的代码片段假设临时凭证与 `default` 配置文件相关联。如果您使用其他配置文件，请按如下方式修改上面的代码，然后将 *profile\$1name* 替换为实际配置文件的名称。

   ```
   private static final AmazonSimpleWorkflow swf =
           AmazonSimpleWorkflowClientBuilder
                   .standard()
                   .withCredentials(new ProfileCredentialsProvider("profile_name"))
                   .withRegion(Regions.DEFAULT_REGION)
                   .build();
   ```

1. 添加新函数以注册到 SWF 域。*域* 是多种相关 SWF 活动和工作流类型的逻辑容器。SWF 组件只有在位于同一个域中时才能彼此通信。

   ```
       try {
           System.out.println("** Registering the domain '" + DOMAIN + "'.");
           swf.registerDomain(new RegisterDomainRequest()
               .withName(DOMAIN)
               .withWorkflowExecutionRetentionPeriodInDays("1"));
       } catch (DomainAlreadyExistsException e) {
           System.out.println("** Domain already exists!");
       }
   ```

   在注册域时，您需要提供*名称*（不含 `:`、`/`、`|`、控制字符或文本字符串“arn”的 1 至 256 个字符组合）以及*保留期*，这是在工作流执行完成后，Amazon SWF 保留工作流执行历史记录数据的天数。最长的工作流执行保留期为 90 天。有关更多信息，请参阅 [RegisterDomainRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/RegisterDomainRequest.html)。

   如果具有该名称的域已存在，则将引发 [DomainAlreadyExistsException](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/DomainAlreadyExistsException.html)。因为我们并不关注是否已经创建了域，因此可以忽略此异常。
**注意**  
此代码演示了使用适用于 Java 的 Amazon SDK方法时的一个通用模式，方法的数据由 `simpleworkflow.model` 命名空间中的类提供，该命名空间使用可链接的 `0with*` 方法实例化和填充。

1. 添加函数以注册新活动类型。*活动* 表示工作流中的一个工作单元。

   ```
       try {
           System.out.println("** Registering the activity type '" + ACTIVITY +
               "-" + ACTIVITY_VERSION + "'.");
           swf.registerActivityType(new RegisterActivityTypeRequest()
               .withDomain(DOMAIN)
               .withName(ACTIVITY)
               .withVersion(ACTIVITY_VERSION)
               .withDefaultTaskList(new TaskList().withName(TASKLIST))
               .withDefaultTaskScheduleToStartTimeout("30")
               .withDefaultTaskStartToCloseTimeout("600")
               .withDefaultTaskScheduleToCloseTimeout("630")
               .withDefaultTaskHeartbeatTimeout("10"));
       } catch (TypeAlreadyExistsException e) {
           System.out.println("** Activity type already exists!");
       }
   ```

   活动类型由*名称*和*版本*标识，它们在所注册到的域中用于将活动与任何其他活动区分开。活动还包含多种可选参数，例如用于从 SWF 接收任务和数据的默认任务列表，以及您可用来对活动各个部分执行所用时长施加限制的不同超时。有关更多信息，请参阅 [RegisterActivityTypeRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/RegisterActivityTypeRequest.html)。
**注意**  
所有超时值以*秒* 为单位指定。有关超时如何影响工作流执行的完整说明，请参阅 [Amazon SWF Timeout Types](https://docs.amazonaws.cn/amazonswf/latest/developerguide/swf-timeout-types.html)。

如果您尝试注册的活动类型已存在，则将引发 [TypeAlreadyExistsException](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/TypeAlreadyExistsException.html)。添加函数以注册新工作流类型。*工作流程* 也称为*决策程序*，表示工作流程执行的逻辑。

\$1

```
    try {
        System.out.println("** Registering the workflow type '" + WORKFLOW +
            "-" + WORKFLOW_VERSION + "'.");
        swf.registerWorkflowType(new RegisterWorkflowTypeRequest()
            .withDomain(DOMAIN)
            .withName(WORKFLOW)
            .withVersion(WORKFLOW_VERSION)
            .withDefaultChildPolicy(ChildPolicy.TERMINATE)
            .withDefaultTaskList(new TaskList().withName(TASKLIST))
            .withDefaultTaskStartToCloseTimeout("30"));
    } catch (TypeAlreadyExistsException e) {
        System.out.println("** Workflow type already exists!");
    }
```

\$1

与活动类型类似，工作流类型由*名称* 和*版本* 标识，也具有可配置的超时。有关更多信息，请参阅 [RegisterWorkflowTypeRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/RegisterWorkflowTypeRequest.html)。

\$1

如果您尝试注册的工作流类型已存在，则将引发 [TypeAlreadyExistsException](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/TypeAlreadyExistsException.html)。最后，请通过向类提供 `main` 方法确保其可执行，这反过来会注册域、活动类型和工作流类型：

\$1

```
    registerDomain();
    registerWorkflowType();
    registerActivityType();
```

现在，您可以[编译](#swf-hello-build)并[运行](#swf-hello-run-register)应用程序来运行注册脚本，或者继续对活动和工作流工作线程编写代码。注册了域、工作流和活动之后，您无需重新运行此步骤，这些内容将保留，直至您自行弃用它们。

### 实施活动工作线程
<a name="implement-the-activity-worker"></a>

*活动* 是工作流中的基本工作单元。工作流提供逻辑、要运行的计划活动 (或要采取的其他操作) 来响应决策任务。典型的工作流通常包含多种活动，可以同步、异步或者以两种方式结合运行。

*活动工作线程* 是一段代码，轮询由 Amazon SWF 生成的活动任务来响应工作流决策。在收到活动任务时，它将运行对应的活动并将成功/失败响应返回到工作流。

我们将实施驱动单个活动的简单活动工作线程。

1. 打开文本编辑器并创建文件 `ActivityWorker.java`，添加程序包声明并根据[通用步骤](#swf-hello-common)导入。

   ```
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClientBuilder;
   import com.amazonaws.services.simpleworkflow.model.*;
   ```

1. 向文件中添加 `ActivityWorker` 类，并向其提供数据成员以保存用来与 Amazon SWF 交互的 SWF 客户端：

   ```
       private static final AmazonSimpleWorkflow swf =
               AmazonSimpleWorkflowClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
   ```

1. 添加将用作活动的方法：

   ```
   private static String sayHello(String input) throws Throwable {
       return "Hello, " + input + "!";
   }
   ```

   该活动就是获取字符串，将其组合到问候语中，然后返回结果。虽然此活动有很小的可能性会引发异常，但最好的做法是将活动设计为在出现问题时会引发错误。

1. 添加我们将用作活动任务轮询方法的 `main` 方法。我们首先添加一些代码来轮询任务列表中的活动任务：

   ```
           System.out.println("Polling for an activity task from the tasklist '"
                   + HelloTypes.TASKLIST + "' in the domain '" +
                   HelloTypes.DOMAIN + "'.");
   
           ActivityTask task = swf.pollForActivityTask(
               new PollForActivityTaskRequest()
                   .withDomain(HelloTypes.DOMAIN)
                   .withTaskList(
                       new TaskList().withName(HelloTypes.TASKLIST)));
   
           String task_token = task.getTaskToken();
   ```

   活动通过调用 Amazon SWF 客户端的 `pollForActivityTask` 方法从 SWF 接收任务，指定在传入的 [PollForActivityTaskRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/PollForActivityTaskRequest.html) 中使用的域和任务列表。

   一旦收到任务，我们将通过调用任务的 `getTaskToken` 方法来检索它的唯一标识符。

1. 接下来，写入一些代码来处理传入的任务。将以下内容添加到您的 `main` 方法，就在轮询任务和检索其任务令牌代码的后方。

   ```
       if (task_token != null) {
           String result = null;
           Throwable error = null;
   
           try {
               System.out.println("Executing the activity task with input '" +
                       task.getInput() + "'.");
               result = sayHello(task.getInput());
           } catch (Throwable th) {
               error = th;
           }
   
           if (error == null) {
               System.out.println("The activity task succeeded with result '"
                       + result + "'.");
               swf.respondActivityTaskCompleted(
                   new RespondActivityTaskCompletedRequest()
                       .withTaskToken(task_token)
                       .withResult(result));
           } else {
               System.out.println("The activity task failed with the error '"
                       + error.getClass().getSimpleName() + "'.");
               swf.respondActivityTaskFailed(
                   new RespondActivityTaskFailedRequest()
                       .withTaskToken(task_token)
                       .withReason(error.getClass().getSimpleName())
                       .withDetails(error.getMessage()));
           }
       }
   ```

   如果任务令牌不是 `null`，则我们可以开始运行活动方法 (`sayHello`)，只要它具有随任务发送的输入数据。

   如果任务*成功*（未生成任何错误），则 worker 通过调用 SWF 客户端的 `respondActivityTaskCompleted` 方法来响应 SWF，该方法使用包含任务令牌和活动结果数据的 [RespondActivityTaskCompletedRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/RespondActivityTaskCompletedRequest.html) 对象。

   另一方面，如果任务*失败*，则我们通过调用带有 [RespondActivityTaskFailedRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/RespondActivityTaskFailedRequest.html) 对象的 `respondActivityTaskFailed` 方法进行响应，向其传递任务令牌和有关错误的信息。

**注意**  
如果终止，此活动不会正常关闭。虽然这超出了本教程的范围，不过在相关主题[适当地关闭活动和工作流工作线程](swf-graceful-shutdown.md)中提供了此活动工作线程的替代实施方法。

### 实施工作流工作线程
<a name="implement-the-workflow-worker"></a>

您的工作流逻辑位于称为**工作流工作线程**的代码块中。工作流工作线程在工作流类型注册到的默认任务列表上，轮询域中 Amazon SWF 发送的决策任务。

工作流工作线程接收任务时，它会做出某种类型的决策 (通常为是否计划新活动) 并采取相应操作 (例如计划活动)。

1. 打开文本编辑器并创建文件 `WorkflowWorker.java`，添加程序包声明并根据[通用步骤](#swf-hello-common)导入。

1. 将一些额外的导入添加到文件中：

   ```
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClientBuilder;
   import com.amazonaws.services.simpleworkflow.model.*;
   import java.util.ArrayList;
   import java.util.List;
   import java.util.UUID;
   ```

1. 声明 `WorkflowWorker` 类，创建用于访问 SWF 方法的 [AmazonSimpleWorkflowClient](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/AmazonSimpleWorkflowClient.html) 类的实例。

   ```
       private static final AmazonSimpleWorkflow swf =
               AmazonSimpleWorkflowClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
   ```

1. 添加 `main` 方法。该方法持续循环，使用 SWF 客户端的 `pollForDecisionTask` 方法轮询决策任务。[PollForDecisionTaskRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/PollForDecisionTaskRequest.html) 提供详细信息。

   ```
       PollForDecisionTaskRequest task_request =
           new PollForDecisionTaskRequest()
               .withDomain(HelloTypes.DOMAIN)
               .withTaskList(new TaskList().withName(HelloTypes.TASKLIST));
   
       while (true) {
           System.out.println(
                   "Polling for a decision task from the tasklist '" +
                   HelloTypes.TASKLIST + "' in the domain '" +
                   HelloTypes.DOMAIN + "'.");
   
           DecisionTask task = swf.pollForDecisionTask(task_request);
   
           String taskToken = task.getTaskToken();
           if (taskToken != null) {
               try {
                   executeDecisionTask(taskToken, task.getEvents());
               } catch (Throwable th) {
                   th.printStackTrace();
               }
           }
       }
   ```

   在收到任务之后，我们调用其 `getTaskToken` 方法，这会返回可用于标识任务的字符串。如果返回的令牌不是 `null`，则我们在 `executeDecisionTask` 方法中进一步处理它，向它传递随任务发送的任务令牌以及 [HistoryEvent](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/HistoryEvent.html) 对象的列表。

1. 添加 `executeDecisionTask` 方法，获取任务令牌 (`String`) 和 `HistoryEvent` 列表。

   ```
       List<Decision> decisions = new ArrayList<Decision>();
       String workflow_input = null;
       int scheduled_activities = 0;
       int open_activities = 0;
       boolean activity_completed = false;
       String result = null;
   ```

   我们还可以设置一些数据成员来跟踪内容，例如：
   + 用于报告任务处理结果的[决策](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/Decision.html)对象列表。
   + 用于保存由“WorkflowExecutionStarted”事件提供的工作流输入的字符串
   + 已计划和打开 (正在运行) 活动的计数，用于避免再次计划已经计划或者当前正在运行的相同活动。
   + 用于指示活动已完成的布尔值。
   + 用于保存活动结果的字符串，以将其作为我们的工作流结果返回。

1. 接下来，添加一些代码到 `executeDecisionTask`，基于 `HistoryEvent` 方法报告的事件类型处理随任务发送的 `getEventType` 对象。

   ```
   System.out.println("Executing the decision task for the history events: [");
   for (HistoryEvent event : events) {
       System.out.println("  " + event);
       switch(event.getEventType()) {
           case "WorkflowExecutionStarted":
               workflow_input =
                   event.getWorkflowExecutionStartedEventAttributes()
                        .getInput();
               break;
           case "ActivityTaskScheduled":
               scheduled_activities++;
               break;
           case "ScheduleActivityTaskFailed":
               scheduled_activities--;
               break;
           case "ActivityTaskStarted":
               scheduled_activities--;
               open_activities++;
               break;
           case "ActivityTaskCompleted":
               open_activities--;
               activity_completed = true;
               result = event.getActivityTaskCompletedEventAttributes()
                             .getResult();
               break;
           case "ActivityTaskFailed":
               open_activities--;
               break;
           case "ActivityTaskTimedOut":
               open_activities--;
               break;
       }
   }
   System.out.println("]");
   ```

   对于我们的工作流，我们最感兴趣的是：
   + “WorkflowExecutionStarted”事件，这指示工作流执行已启动 (通常意味着您应该运行工作流中的第一个活动)，并且这提供了初始输入 (提供到工作流中)。在这种情况下，这是我们问候语的名称部分，因此将其保存在字符串中以在计划活动运行时使用。
   + “ActivityTaskCompleted”事件在计划的活动完成后立即发送。事件数据还包括已完成活动的返回值。因为我们仅有一个活动，我们将使用该值作为整个工作流程的结果。

   其他事件类型在工作流需要时可以使用。有关各个事件类型的信息，请参阅 [HistoryEvent](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/HistoryEvent.html) 类说明。

   \$1 注意：`switch` 语句中的字符串在 Java 7 中引入。如果您使用的是 Java 的较早版本，则可以使用 [EventType](https://docs.amazonaws.cn/AWSJavaSDK/latest/javadoc/com/amazonaws/services/simpleworkflow/model/EventType.html) 类将 `history_event.getType()` 返回的 `String` 转换为枚举值，然后可在需要时将其转换回 `String`：

```
EventType et = EventType.fromValue(event.getEventType());
```

1. 在 `switch` 语句之后，添加更多代码，根据所收到的任务采用合适的*决策* 进行响应。

   ```
   if (activity_completed) {
       decisions.add(
           new Decision()
               .withDecisionType(DecisionType.CompleteWorkflowExecution)
               .withCompleteWorkflowExecutionDecisionAttributes(
                   new CompleteWorkflowExecutionDecisionAttributes()
                       .withResult(result)));
   } else {
       if (open_activities == 0 && scheduled_activities == 0) {
   
           ScheduleActivityTaskDecisionAttributes attrs =
               new ScheduleActivityTaskDecisionAttributes()
                   .withActivityType(new ActivityType()
                       .withName(HelloTypes.ACTIVITY)
                       .withVersion(HelloTypes.ACTIVITY_VERSION))
                   .withActivityId(UUID.randomUUID().toString())
                   .withInput(workflow_input);
   
           decisions.add(
                   new Decision()
                       .withDecisionType(DecisionType.ScheduleActivityTask)
                       .withScheduleActivityTaskDecisionAttributes(attrs));
       } else {
           // an instance of HelloActivity is already scheduled or running. Do nothing, another
           // task will be scheduled once the activity completes, fails or times out
       }
   }
   
   System.out.println("Exiting the decision task with the decisions " + decisions);
   ```
   + 如果尚未计划活动，我们使用 `ScheduleActivityTask` 决策进行响应，这在 [ScheduleActivityTaskDecisionAttributes](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/ScheduleActivityTaskDecisionAttributes.html) 结构中提供关于 Amazon SWF 接下来应计划的活动的信息，也包括 Amazon SWF 应发送到活动的任何数据。
   + 如果活动已完成，则我们将考虑完成的整个工作流，并使用 `CompletedWorkflowExecution` 决策进行响应，填入 [CompleteWorkflowExecutionDecisionAttributes](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/CompleteWorkflowExecutionDecisionAttributes.html) 结构以提供有关已完成工作流的详细信息。在这种情况下，我们将返回活动的结果。

   在任何一种情况下，决策信息将添加到在方法顶部声明的 `Decision` 列表。

1. 返回在处理任务时收集的 `Decision` 对象列表来完成决策任务。在我们所编写的 `executeDecisionTask` 方法尾部添加此代码：

   ```
   swf.respondDecisionTaskCompleted(
       new RespondDecisionTaskCompletedRequest()
           .withTaskToken(taskToken)
           .withDecisions(decisions));
   ```

   SWF 客户端的 `respondDecisionTaskCompleted` 方法获取标识任务的任务令牌以及 `Decision` 对象列表。

### 实施工作流启动程序
<a name="implement-the-workflow-starter"></a>

最后，我们将编写一些代码用于启动工作流程执行。

1. 打开文本编辑器并创建文件 `WorkflowStarter.java`，添加程序包声明并根据[通用步骤](#swf-hello-common)导入。

1. 添加 `WorkflowStarter` 类：

   ```
   package aws.example.helloswf;
   
   
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflow;
   import com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClientBuilder;
   import com.amazonaws.services.simpleworkflow.model.*;
   
   public class WorkflowStarter {
       private static final AmazonSimpleWorkflow swf =
               AmazonSimpleWorkflowClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
       public static final String WORKFLOW_EXECUTION = "HelloWorldWorkflowExecution";
   
       public static void main(String[] args) {
           String workflow_input = "{SWF}";
           if (args.length > 0) {
               workflow_input = args[0];
           }
   
           System.out.println("Starting the workflow execution '" + WORKFLOW_EXECUTION +
                   "' with input '" + workflow_input + "'.");
   
           WorkflowType wf_type = new WorkflowType()
               .withName(HelloTypes.WORKFLOW)
               .withVersion(HelloTypes.WORKFLOW_VERSION);
   
           Run run = swf.startWorkflowExecution(new StartWorkflowExecutionRequest()
               .withDomain(HelloTypes.DOMAIN)
               .withWorkflowType(wf_type)
               .withWorkflowId(WORKFLOW_EXECUTION)
               .withInput(workflow_input)
               .withExecutionStartToCloseTimeout("90"));
   
           System.out.println("Workflow execution started with the run id '" +
                   run.getRunId() + "'.");
       }
   }
   ```

   `WorkflowStarter` 类包含一个方法 `main`，它获取命令行上传递的可选参数作为工作流的输入数据。

   SWF 客户端方法 `startWorkflowExecution`，获取 [StartWorkflowExecutionRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/StartWorkflowExecutionRequest.html) 对象作为输入。此处，除了指定要运行的域和工作流类型之外，我们提供了：
   + 便于阅读的工作流执行名称
   + 工作流输入数据 (我们的示例中在命令行上提供)
   + 超时值，以秒为单位，表示整个工作流运行所应使用的时长。

   [ 返回的](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/simpleworkflow/model/Run.html)运行`startWorkflowExecution`对象提供了*运行 ID*，这是用于在 Amazon SWF 的工作流执行历史记录中标识此特定工作流执行的值。

   \$1 注意：运行 ID 由 Amazon SWF 生成，*不同于* 您在启动工作流执行时传入的工作流执行名称。

## 编译示例
<a name="swf-hello-build"></a>

要使用 Maven 编译示例项目，请转到 `helloswf` 目录并键入：

```
mvn package
```

生成的 `helloswf-1.0.jar` 将在 `target` 目录中生成。

## 运行示例
<a name="run-the-example"></a>

示例包括四个独立的可执行类，彼此独立运行。

**注意**  
如果您使用的是 Linux、macOS 或 Unix 系统，您可以在单个终端窗口中将它们全部逐个运行。如果您运行的是 Windows，则应该打开两个额外的命令行实例并分别导航到 `helloswf` 目录。

### 设置 Java 类路径
<a name="swf-hello-set-classpath"></a>

虽然 Maven 已经为您处理了依赖项来运行示例，您仍需要在 Java 类路径上提供 Amazon SDK 库及其依赖项。您可以将 `CLASSPATH` 环境变量设置为 Amazon SDK 库的位置，以及 SDK 中包括必要依赖项的 `third-party/lib` 目录：

```
export CLASSPATH='target/helloswf-1.0.jar:/path/to/sdk/lib/*:/path/to/sdk/third-party/lib/*'
java example.swf.hello.HelloTypes
```

或者使用 **` java `** 命令的 `-cp` 选项在运行各个应用程序时设置类路径。

```
java -cp target/helloswf-1.0.jar:/path/to/sdk/lib/*:/path/to/sdk/third-party/lib/* \
  example.swf.hello.HelloTypes
```

您使用的样式由您决定。如果您在编译代码时没有问题，但在尝试运行示例时遇到一系列“NoClassDefFound”错误，则可能是因为类路径设置不正确。

### 注册域、工作流程和活动类型
<a name="swf-hello-run-register"></a>

在运行工作线程和工作流程启动程序之前，您需要注册域以及工作流程和活动类型。执行此操作的代码在[注册域、工作流和活动类型](#swf-hello-hellotypes)中实施。

在编译之后，如果您已[设置 CLASSPATH](#swf-hello-set-classpath)，则可以通过执行以下命令运行注册代码：

```
    echo 'Supply the name of one of the example classes as an argument.'
```

### 启动活动和工作流工作线程
<a name="swf-hello-run-workers"></a>

现在类型已注册，您可以启动活动和工作流工作线程。它们将持续运行并轮询任务，直至终止，因此您应该在单独终端窗口中运行它们，或者，如果您在 Linux、macOS 或 Unix 上运行它们，则可以使用 `&` 运算符来使得它们中的每一个在运行时生成单独进程。

```
    echo 'If there are arguments to the class, put them in quotes after the class name.'
    exit 1
```

如果您在单独窗口中运行这些命令，则忽略每一行最后的 `&` 运算符。

### 启动工作流执行
<a name="swf-hello-start-execution"></a>

现在正在轮询您的活动和工作流工作线程，您可以启动工作流执行。此进程将运行直至工作流返回已完成状态。您应在新终端窗口中运行它 (除非您使用 `&` 运算符将工作线程作为新生成的进程运行)。

```
fi
```

**注意**  
如果您要提供自己的输入数据 (这将首先传递到工作流，然后传递到活动)，则将其添加到命令行中。例如：  

```
echo "## Running $className..."
```

一旦开始工作流执行，您应该开始查看这两种工作线程以及工作流执行本身提供的输出。工作流最终完成之后，其输出将显示在屏幕上。

## 此示例的完整源代码
<a name="complete-source-for-this-example"></a>

您可以在 Github 的 [aws-java-developer-guide](https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/java/example_code/swf) 存储库中浏览此示例的*完整源代码*。

## 有关更多信息
<a name="for-more-information"></a>
+ 如果在工作流轮询仍在进行时关闭此处提供的工作线程，则它们会导致任务丢失。要了解如何适当地关闭工作线程，请参阅[适当地关闭活动和工作流工作线程](swf-graceful-shutdown.md)。
+ 如需了解有关 Amazon SWF 的更多信息，请访问 [Amazon SWF](https://www.amazonaws.cn/swf/) 主页或查看《[Amazon SWF Developer Guide](https://docs.amazonaws.cn/amazonswf/latest/developerguide/)》。
+ 您可以使用适用于 Java 的 Amazon Flow Framework，使用注释以更讲究的 Java 样式编写更复杂的工作流。如需了解更多信息，请参阅《[Amazon Flow Framework for Java Developer Guide](https://docs.amazonaws.cn/amazonswf/latest/awsflowguide/)》。