|
| 1 | +# Exercise 1: Implement a Custom Data Converter |
| 2 | + |
| 3 | +During this exercise, you will: |
| 4 | + |
| 5 | +- Output typical payloads from a Temporal Workflow using the default Data Converter |
| 6 | +- Implement a Custom Data Converter that encrypts Workflow output |
| 7 | +- Implement a Failure Converter and demonstrate parsing its output |
| 8 | + |
| 9 | +Make your changes to the code in the `practice` subdirectory (look for |
| 10 | +`TODO` comments that will guide you to where you should make changes to |
| 11 | +the code). If you need a hint or want to verify your changes, look at |
| 12 | +the complete version in the `solution` subdirectory. |
| 13 | + |
| 14 | +### GitPod Environment Shortcuts |
| 15 | + |
| 16 | +If you are executing the exercises in the provided GitPod environment, you |
| 17 | +can take advantage of certain aliases to aid in navigation and execution of |
| 18 | +the code. |
| 19 | + |
| 20 | +| Command | Action | |
| 21 | +| :------ | :------------------------------------------------------------------------------------------------------------------------------ | |
| 22 | +| `ex1` | Change to Exercise 1 Practice Directory | |
| 23 | +| `ex1s` | Change to Exercise 1 Solution Directory | |
| 24 | +| `ex1w` | Execute the Exercise 1 Worker. Must be within the appropriate directory for this to succeed. (either `practice` or `solution`) | |
| 25 | +| `ex1st` | Execute the Exercise 1 Starter. Must be within the appropriate directory for this to succeed. (either `practice` or `solution`) | |
| 26 | + |
| 27 | +## Part A: Implement a Custom Data Converter |
| 28 | + |
| 29 | +1. Defining a Custom Data Converter is a straightforward change to your existing |
| 30 | + Worker and Starter code. The example in the `practice` subdirectory of this |
| 31 | + exercise is missing the necessary change to use a Custom Data Converter, |
| 32 | + meaning that you can run it out of the box, and produce JSON output using the |
| 33 | + Default Data Converter. You'll do this first, so you have an idea of the |
| 34 | + expected output. First, start the Worker: |
| 35 | + |
| 36 | + ```shell |
| 37 | + mvn exec:java -Dexec.mainClass="customconverter.ConverterWorker" |
| 38 | + ``` |
| 39 | + |
| 40 | +2. Next, run the Workflow starter: |
| 41 | + |
| 42 | + ```shell |
| 43 | + mvn exec:java -Dexec.mainClass="customconverter.Starter" |
| 44 | + ``` |
| 45 | + |
| 46 | +3. After that, you can use the `temporal` CLI to show the Workflow result: |
| 47 | + |
| 48 | + ```shell |
| 49 | + temporal workflow show -w converters_workflowID |
| 50 | + ``` |
| 51 | + |
| 52 | + ``` |
| 53 | + Progress: |
| 54 | + ID Time Type |
| 55 | + 1 2024-03-14T16:01:21Z WorkflowExecutionStarted |
| 56 | + 2 2024-03-14T16:01:21Z WorkflowTaskScheduled |
| 57 | + 3 2024-03-14T16:01:21Z WorkflowTaskStarted |
| 58 | + 4 2024-03-14T16:01:21Z WorkflowTaskCompleted |
| 59 | + 5 2024-03-14T16:01:21Z ActivityTaskScheduled |
| 60 | + 6 2024-03-14T16:01:21Z ActivityTaskStarted |
| 61 | + 7 2024-03-14T16:01:21Z ActivityTaskCompleted |
| 62 | + 8 2024-03-14T16:01:21Z WorkflowTaskScheduled |
| 63 | + 9 2024-03-14T16:01:21Z WorkflowTaskStarted |
| 64 | + 10 2024-03-14T16:01:21Z WorkflowTaskCompleted |
| 65 | + 11 2024-03-14T16:01:21Z WorkflowExecutionCompleted |
| 66 | +
|
| 67 | + Result: |
| 68 | + Status: COMPLETED |
| 69 | + Output: ["Received Plain text input"] |
| 70 | + ``` |
| 71 | + |
| 72 | + You should now have an idea of how this Workflow runs ordinarily — it outputs |
| 73 | + the string "Received Plain text input". In the next step, you'll add a Custom |
| 74 | + Data Converter. |
| 75 | + |
| 76 | +4. To add a Custom Data Converter, you don't need to change anything in your |
| 77 | + Workflow code. You only need to add a `CodecDataConverter` parameter to |
| 78 | + `WorkflowClient client = WorkflowClient.newInstance(service);` where it is used |
| 79 | + in both `ConverterWorker.java` and `Starter.java`. |
| 80 | + 1. Replace `WorkflowClient client = WorkflowClient.newInstance(service);` in |
| 81 | + both files with |
| 82 | + ```java |
| 83 | + WorkflowClient client = WorkflowClient.newInstance(service, WorkflowClientOptions.newBuilder() |
| 84 | + .setDataConverter( |
| 85 | + new CodecDataConverter( |
| 86 | + DefaultDataConverter.newDefaultInstance(), |
| 87 | + Collections.singletonList(new CustomPayloadCodec()))) |
| 88 | + .build()); |
| 89 | + ``` |
| 90 | +5. Next, review `CustomPayloadCodec.java`. This contains the Custom Converter |
| 91 | + code you'll be using. The `encode()` method applies the `encodePayload()` method |
| 92 | + to each element in the payload. The `encodePayload()` method compresses the payload |
| 93 | + using Java's [snappy](https://github.com/google/snappy) codec, and sets the |
| 94 | + file metadata. The `decode()` and `decodePayload()` methods do the same thing, |
| 95 | + but in reverse. Add the missing calls to the `encode` method (you can use the |
| 96 | + `decode()` function as a hint). |
| 97 | +6. Now you can re-run the Workflow with your Custom Converter. |
| 98 | + |
| 99 | + 1. Stop your Worker (with `Ctrl+C` in a blocking terminal) |
| 100 | + 1. Recompile your code |
| 101 | + |
| 102 | + ```shell |
| 103 | + mvn clean compile |
| 104 | + ``` |
| 105 | + |
| 106 | + 1. Restart the worker using |
| 107 | + |
| 108 | + ```shell |
| 109 | + mvn exec:java -Dexec.mainClass="customconverter.ConverterWorker" |
| 110 | + ``` |
| 111 | + |
| 112 | + 1. Rerun the workflow with |
| 113 | + |
| 114 | + ```shell |
| 115 | + mvn exec:java -Dexec.mainClass="customconverter.Starter" |
| 116 | + ``` |
| 117 | + |
| 118 | + 1. Finally, get the result again with |
| 119 | + |
| 120 | + ```shell |
| 121 | + temporal workflow show -w converter-workflow |
| 122 | + ``` |
| 123 | + |
| 124 | + This time, your output will be encoded: |
| 125 | + |
| 126 | + ```shell |
| 127 | + ... |
| 128 | + Result: |
| 129 | + Status: COMPLETED |
| 130 | + Output: [encoding binary/snappy: payload encoding is not supported] |
| 131 | + ``` |
| 132 | + |
| 133 | +The `payload encoding is not supported` message is normal — the Temporal |
| 134 | +Cluster itself can't use the `decode` method directly without a Codec |
| 135 | +Server, which you'll create in the next exercise. In the meantime, you have |
| 136 | +successfully implemented a Custom Data Converter, and in the next step, you'll |
| 137 | +add more features to it. |
| 138 | + |
| 139 | +## Part B: Implement a Failure Converter |
| 140 | + |
| 141 | +1. The next feature you may add is a Failure Converter. Failure messages and |
| 142 | + stack traces are not encoded as codec-capable Payloads by default; you must |
| 143 | + explicitly enable encoding these common attributes on failures. If your |
| 144 | + errors might contain sensitive information, you can encrypt the message and |
| 145 | + stack trace by configuring the default Failure Converter to use your encoded |
| 146 | + attributes, in which case it moves your `message` and `stack_trace` fields to a |
| 147 | + Payload that's run through your codec. To do this, you can override the |
| 148 | + default Failure Converter with a single additional parameter, `true`. |
| 149 | + 1. Locate the `WorkflowClient.newInstance...` code you added in the previous exercise. |
| 150 | + 1. Add the value `true` as the last value in the `new DataCodecConverter()` constructor. |
| 151 | + 1. Do this in both `ConverterWorker.java` and `Starter.java` |
| 152 | +2. To test your Failure Converter, change your Workflow to return an artificial |
| 153 | + error. |
| 154 | + |
| 155 | + 1. Add the following code after executing your Activity. |
| 156 | + |
| 157 | + ```java |
| 158 | + throw ApplicationFailure.newFailure("Artificial Error", "Artificial Error"); |
| 159 | + ``` |
| 160 | + |
| 161 | + 1. Comment out all code after the `throw` statement as that code will |
| 162 | + become unreachable. |
| 163 | + 1. Stop your worker using `Ctrl-C` |
| 164 | + 1. Recompile your code |
| 165 | + |
| 166 | + ```shell |
| 167 | + mvn clean compile |
| 168 | + ``` |
| 169 | + |
| 170 | + 1. Restart the worker using |
| 171 | + |
| 172 | + ```shell |
| 173 | + mvn exec:java -Dexec.mainClass="customconverter.ConverterWorker" |
| 174 | + ``` |
| 175 | + |
| 176 | + 1. Rerun the workflow with |
| 177 | + |
| 178 | + ```shell |
| 179 | + mvn exec:java -Dexec.mainClass="customconverter.Starter" |
| 180 | + ``` |
| 181 | + |
| 182 | + 1. You should see a stack trace appear. This was expected. Stop the execution |
| 183 | + using `Ctrl-C`. |
| 184 | + |
| 185 | + 1. Finally, get the result again with to get the status of your failed Workflow. |
| 186 | + |
| 187 | + ```shell |
| 188 | + temporal workflow show -w converter-workflow |
| 189 | + ``` |
| 190 | + |
| 191 | + Notice that the `Failure:` field should now display an encoded |
| 192 | + result, rather than a plain text error: |
| 193 | + |
| 194 | + ``` |
| 195 | + Progress: |
| 196 | + ID Time Type |
| 197 | + 1 2024-03-14T17:08:20Z WorkflowExecutionStarted |
| 198 | + 2 2024-03-14T17:08:20Z WorkflowTaskScheduled |
| 199 | + 3 2024-03-14T17:08:20Z WorkflowTaskStarted |
| 200 | + 4 2024-03-14T17:08:20Z WorkflowTaskCompleted |
| 201 | + 5 2024-03-14T17:08:20Z ActivityTaskScheduled |
| 202 | + 6 2024-03-14T17:08:20Z ActivityTaskStarted |
| 203 | + 7 2024-03-14T17:08:20Z ActivityTaskCompleted |
| 204 | + 8 2024-03-14T17:08:20Z WorkflowTaskScheduled |
| 205 | + 9 2024-03-14T17:08:20Z WorkflowTaskStarted |
| 206 | + 10 2024-03-14T17:08:20Z WorkflowTaskCompleted |
| 207 | + 11 2024-03-14T17:08:20Z WorkflowExecutionFailed |
| 208 | +
|
| 209 | + Result: |
| 210 | + Status: FAILED |
| 211 | + Failure: &Failure{Message:Encoded failure,Source:JavaSDK,StackTrace:,Cause:nil,FailureType:Failure_ApplicationFailureInfo,} |
| 212 | + ``` |
| 213 | + |
| 214 | +### This is the end of the exercise. |
0 commit comments