Skip to content

Commit 664cd5f

Browse files
authored
SpringBoot Sample - customize options (#497)
* SpringBoot Sample - optimize options Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * update text Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * updated readme Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> --------- Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io>
1 parent c7ed80c commit 664cd5f

File tree

11 files changed

+442
-1
lines changed

11 files changed

+442
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,5 @@ More info on each sample:
143143
- [**SDK Metrics**](/springboot/src/main/java/io/temporal/samples/springboot/metrics): Learn how to set up SDK Metrics
144144
- [**Synchronous Update**](/springboot/src/main/java/io/temporal/samples/springboot/update): Learn how to use Synchronous Update feature with this purchase sample
145145
- [**Kafka Request / Reply**](/springboot/src/main/java/io/temporal/samples/springboot/kafka): Sample showing possible integration with event streaming platforms such as Kafka
146-
146+
- [**Customize Options**](/springboot/src/main/java/io/temporal/samples/springboot/customize): Sample showing how to customize options such as WorkerOptions, WorkerFactoryOptions, etc (see options config [here](springboot/src/main/java/io/temporal/samples/springboot/customize/TemporalOptionsConfig.java))
147+

springboot/src/main/java/io/temporal/samples/springboot/SamplesController.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.temporal.client.WorkflowOptions;
2525
import io.temporal.client.WorkflowStub;
2626
import io.temporal.client.WorkflowUpdateException;
27+
import io.temporal.samples.springboot.customize.CustomizeWorkflow;
2728
import io.temporal.samples.springboot.hello.HelloWorkflow;
2829
import io.temporal.samples.springboot.hello.model.Person;
2930
import io.temporal.samples.springboot.kafka.MessageWorkflow;
@@ -152,4 +153,27 @@ ResponseEntity sendToKafka(@RequestBody String message) {
152153
// bypass thymeleaf, don't return template name just result
153154
return new ResponseEntity<>("\" Message workflow completed\"", HttpStatus.OK);
154155
}
156+
157+
@GetMapping("/customize")
158+
public String customize(Model model) {
159+
model.addAttribute("sample", "Customizing Options");
160+
return "customize";
161+
}
162+
163+
@PostMapping(
164+
value = "/customize",
165+
consumes = {MediaType.APPLICATION_JSON_VALUE},
166+
produces = {MediaType.TEXT_HTML_VALUE})
167+
ResponseEntity customizeSample() {
168+
CustomizeWorkflow workflow =
169+
client.newWorkflowStub(
170+
CustomizeWorkflow.class,
171+
WorkflowOptions.newBuilder()
172+
.setTaskQueue("CustomizeTaskQueue")
173+
.setWorkflowId("CustomizeSample")
174+
.build());
175+
176+
// bypass thymeleaf, don't return template name just result
177+
return new ResponseEntity<>("\"" + workflow.execute() + "\"", HttpStatus.OK);
178+
}
155179
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.springboot.customize;
21+
22+
import io.temporal.activity.ActivityInterface;
23+
24+
@ActivityInterface
25+
public interface CustomizeActivity {
26+
String run(String input);
27+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.springboot.customize;
21+
22+
import io.temporal.spring.boot.ActivityImpl;
23+
import org.springframework.stereotype.Component;
24+
25+
@Component
26+
@ActivityImpl(taskQueues = "CustomizeTaskQueue")
27+
public class CustomizeActivityImpl implements CustomizeActivity {
28+
@Override
29+
public String run(String input) {
30+
return "Completed as " + input + " activity!";
31+
}
32+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.springboot.customize;
21+
22+
import io.temporal.workflow.WorkflowInterface;
23+
import io.temporal.workflow.WorkflowMethod;
24+
25+
@WorkflowInterface
26+
public interface CustomizeWorkflow {
27+
@WorkflowMethod
28+
String execute();
29+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.springboot.customize;
21+
22+
import io.temporal.activity.ActivityOptions;
23+
import io.temporal.activity.LocalActivityOptions;
24+
import io.temporal.failure.ActivityFailure;
25+
import io.temporal.failure.TimeoutFailure;
26+
import io.temporal.spring.boot.WorkflowImpl;
27+
import io.temporal.workflow.Workflow;
28+
import java.time.Duration;
29+
import org.slf4j.Logger;
30+
31+
/**
32+
* In our custom config we have set that worker polling on CustomizeTaskQueue to be a "local
33+
* activity worker", meaning it would not poll for activity tasks. For this sample we will try to
34+
* start an activity as "normal" activity which should time out, then invoke it again as local which
35+
* should be successful.
36+
*
37+
* @see io.temporal.samples.springboot.customize.TemporalOptionsConfig
38+
*/
39+
@WorkflowImpl(taskQueues = "CustomizeTaskQueue")
40+
public class CustomizeWorkflowImpl implements CustomizeWorkflow {
41+
private CustomizeActivity asNormalActivity =
42+
Workflow.newActivityStub(
43+
CustomizeActivity.class,
44+
ActivityOptions.newBuilder()
45+
.setStartToCloseTimeout(Duration.ofSeconds(2))
46+
.setScheduleToCloseTimeout(Duration.ofSeconds(4))
47+
.build());
48+
49+
private CustomizeActivity asLocalActivity =
50+
Workflow.newLocalActivityStub(
51+
CustomizeActivity.class,
52+
LocalActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());
53+
private Logger logger = Workflow.getLogger(CustomizeActivity.class.getName());
54+
55+
@Override
56+
public String execute() {
57+
try {
58+
return asNormalActivity.run("Normal");
59+
} catch (ActivityFailure e) {
60+
// We should have TimeoutFailure as activity failure cause with StartToClose timeout type
61+
TimeoutFailure tf = (TimeoutFailure) e.getCause();
62+
logger.warn("asNormalActivity failed with timeout type: " + tf.getTimeoutType());
63+
}
64+
return asLocalActivity.run("Local");
65+
}
66+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# SpringBoot Customize Options Sample
2+
3+
This sample shows how to optimize default options such as
4+
* WorkflowServiceStubsOptions
5+
* WorkflowClientOption
6+
* WorkerFactoryOptions
7+
* WorkerOptions
8+
9+
WorkerOptions can be optimized per worker/task queue.
10+
11+
For this sample we set our specific worker to be "local activity worker" via custom options meaning
12+
it would not poll for activity tasks. Click on "Run Workflow" button to start instance of
13+
our sample workflow. This workflow will try to invoke our activity as "normal"
14+
activity which should time out on ScheduleToClose timeout, then we invoke this activity
15+
as local activity which should succeed.
16+
17+
## How to run
18+
1. Start SpringBoot from main samples repo directory:
19+
20+
./gradlew bootRun
21+
22+
2. In your browser navigate to:
23+
24+
http://localhost:3030/customize
25+
26+
3. Press the "Run Workflow" button to start execution. You will see result show on page in 4 seconds
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.springboot.customize;
21+
22+
import io.temporal.client.WorkflowClientOptions;
23+
import io.temporal.serviceclient.WorkflowServiceStubsOptions;
24+
import io.temporal.spring.boot.TemporalOptionsCustomizer;
25+
import io.temporal.spring.boot.WorkerOptionsCustomizer;
26+
import io.temporal.worker.WorkerFactoryOptions;
27+
import io.temporal.worker.WorkerOptions;
28+
import javax.annotation.Nonnull;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Configuration;
31+
32+
@Configuration
33+
public class TemporalOptionsConfig {
34+
35+
// Worker specific options customization
36+
@Bean
37+
public WorkerOptionsCustomizer customWorkerOptions() {
38+
return new WorkerOptionsCustomizer() {
39+
@Nonnull
40+
@Override
41+
public WorkerOptions.Builder customize(
42+
@Nonnull WorkerOptions.Builder optionsBuilder,
43+
@Nonnull String workerName,
44+
@Nonnull String taskQueue) {
45+
46+
// For CustomizeTaskQueue (also name of worker) we set worker
47+
// to only handle workflow tasks and local activities
48+
if (taskQueue.equals("CustomizeTaskQueue")) {
49+
optionsBuilder.setLocalActivityWorkerOnly(true);
50+
}
51+
return optionsBuilder;
52+
}
53+
};
54+
}
55+
56+
// WorkflowServiceStubsOptions customization
57+
@Bean
58+
public TemporalOptionsCustomizer<WorkflowServiceStubsOptions.Builder>
59+
customServiceStubsOptions() {
60+
return new TemporalOptionsCustomizer<WorkflowServiceStubsOptions.Builder>() {
61+
@Nonnull
62+
@Override
63+
public WorkflowServiceStubsOptions.Builder customize(
64+
@Nonnull WorkflowServiceStubsOptions.Builder optionsBuilder) {
65+
// set options on optionsBuilder as needed
66+
// ...
67+
return optionsBuilder;
68+
}
69+
};
70+
}
71+
72+
// WorkflowClientOption customization
73+
@Bean
74+
public TemporalOptionsCustomizer<WorkflowClientOptions.Builder> customClientOptions() {
75+
return new TemporalOptionsCustomizer<WorkflowClientOptions.Builder>() {
76+
@Nonnull
77+
@Override
78+
public WorkflowClientOptions.Builder customize(
79+
@Nonnull WorkflowClientOptions.Builder optionsBuilder) {
80+
// set options on optionsBuilder as needed
81+
// ...
82+
return optionsBuilder;
83+
}
84+
};
85+
}
86+
87+
// WorkerFactoryOptions customization
88+
@Bean
89+
public TemporalOptionsCustomizer<WorkerFactoryOptions.Builder> customWorkerFactoryOptions() {
90+
return new TemporalOptionsCustomizer<WorkerFactoryOptions.Builder>() {
91+
@Nonnull
92+
@Override
93+
public WorkerFactoryOptions.Builder customize(
94+
@Nonnull WorkerFactoryOptions.Builder optionsBuilder) {
95+
// set options on optionsBuilder as needed
96+
// ...
97+
return optionsBuilder;
98+
}
99+
};
100+
}
101+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<!DOCTYPE html>
2+
<html lang="en" xmlns:th="http://www.thymeleaf.org">
3+
<head th:replace="fragments :: samples-header"></head>
4+
<body>
5+
<div class="container">
6+
<div class="card">
7+
<div class="card-body">
8+
<h4 class="card-title" th:text="'Temporal Java SDK Samples: ' + ${sample}">Temporal Java SDK Samples</h4>
9+
<h6>This sample shows how to optimize default options such as
10+
<ul>
11+
<li>WorkflowServiceStubsOptions</li>
12+
<li>WorkflowClientOption</li>
13+
<li>WorkerFactoryOptions</li>
14+
<li>WorkerOptions</li>
15+
</ul>
16+
<br/>WorkerOptions can be optimized per worker/task queue.
17+
For this sample we set our specific worker to be "local activity worker" via custom options meaning
18+
it would not poll for activity tasks. Click on "Run Workflow" button to start instance of
19+
our sample workflow. This workflow will try to invoke activity as "normal" activity which should
20+
timeout on the set ScheduleToClose timeout, we handle this activity failure
21+
and then invoke this activity as local activity which should succeed and update the
22+
workflow execution result on the page.
23+
</h6>
24+
<div class="form-group">
25+
<br/><br/><br/>
26+
<form action="/customize", id="sampleform">
27+
<p><input type="submit" value="Run Workflow" class="btn btn-primary" />
28+
</form>
29+
</div>
30+
</div>
31+
<div style="width: 18rem;">
32+
<div>
33+
<h5 class="card-title">Workflow result:</h5>
34+
<div id="result"></div>
35+
</div>
36+
</div>
37+
</div>
38+
</div>
39+
<script>
40+
$("#sampleform").submit(function( event ) {
41+
event.preventDefault();
42+
$( "#result" ).empty().append( "" );
43+
44+
var $form = $( this ),
45+
url = $form.attr( "action" );
46+
47+
$.ajax({
48+
'url': url,
49+
'method':'POST',
50+
'contentType': 'application/json',
51+
success: function(response) {
52+
$( "#result" ).empty().append( response );
53+
}
54+
});
55+
});
56+
</script>
57+
<footer th:replace="fragments :: samples-footer"></footer>
58+
</body>
59+
</html>

0 commit comments

Comments
 (0)