Skip to content

Commit c126e31

Browse files
tsurdiloantmendoza
andauthored
HelloSignalWithStartAndWorkflowInit sample (temporalio#748)
* HelloSignalWithStartAndWorkflowInit sample Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * update readme Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * update workflowid Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * adding test Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> * Update core/src/test/java/io/temporal/samples/hello/HelloSignalWithStartAndWorkflowInitTest.java Co-authored-by: Antonio Mendoza Pérez <antonio.perez@temporal.io> --------- Signed-off-by: Tihomir Surdilovic <tihomir@temporal.io> Co-authored-by: Antonio Mendoza Pérez <antonio.perez@temporal.io>
1 parent 11bbbbe commit c126e31

File tree

4 files changed

+286
-1
lines changed

4 files changed

+286
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ See the README.md file in each main sample directory for cut/paste Gradle comman
8080
- [**HelloSignalWithTimer**](/core/src/main/java/io/temporal/samples/hello/HelloSignalWithTimer.java): Demonstrates how to use collect signals for certain amount of time and then process last one.
8181
- [**HelloWorkflowTimer**](/core/src/main/java/io/temporal/samples/hello/HelloWorkflowTimer.java): Demonstrates how we can use workflow timer to restrict duration of workflow execution instead of workflow run/execution timeouts.
8282
- [**Auto-Heartbeating**](/core/src/main/java/io/temporal/samples/autoheartbeat/): Demonstrates use of Auto-heartbeating utility via activity interceptor.
83-
83+
- [**HelloSignalWithStartAndWorkflowInit**](/core/src/main/java/io/temporal/samples/hello/HelloSignalWithStartAndWorkflowInit.java): Demonstrates how WorkflowInit can be useful with SignalWithStart to initialize workflow variables.
8484

8585
#### Scenario-based samples
8686

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package io.temporal.samples.hello;
2+
3+
import io.temporal.activity.ActivityInterface;
4+
import io.temporal.activity.ActivityOptions;
5+
import io.temporal.client.WorkflowClient;
6+
import io.temporal.client.WorkflowFailedException;
7+
import io.temporal.client.WorkflowOptions;
8+
import io.temporal.client.WorkflowStub;
9+
import io.temporal.serviceclient.WorkflowServiceStubs;
10+
import io.temporal.worker.Worker;
11+
import io.temporal.worker.WorkerFactory;
12+
import io.temporal.worker.WorkflowImplementationOptions;
13+
import io.temporal.workflow.*;
14+
import java.time.Duration;
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import org.apache.commons.lang.StringUtils;
18+
19+
/**
20+
* Sample Temporal workflow that demonstrates how to use WorkflowInit with clients starting
21+
* execution using SignalWithStart
22+
*/
23+
public class HelloSignalWithStartAndWorkflowInit {
24+
static final String TASK_QUEUE = "HelloWithInitTaskQueue";
25+
26+
public interface MyWorkflow {
27+
@WorkflowMethod
28+
String greet(Person person);
29+
30+
@SignalMethod
31+
void addGreeting(Person person);
32+
}
33+
34+
@WorkflowInterface
35+
public interface MyWorkflowWithInit extends MyWorkflow {}
36+
37+
@WorkflowInterface
38+
public interface MyWorkflowNoInit extends MyWorkflow {}
39+
40+
public static class WithInitMyWorkflowImpl implements MyWorkflowWithInit {
41+
// We dont initialize peopleToGreet on purpose
42+
private List<Person> peopleToGreet;
43+
private MyGreetingActivities activities =
44+
Workflow.newActivityStub(
45+
MyGreetingActivities.class,
46+
ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());
47+
48+
@WorkflowInit
49+
public WithInitMyWorkflowImpl(Person person) {
50+
peopleToGreet = new ArrayList<>();
51+
}
52+
53+
@Override
54+
public String greet(Person person) {
55+
peopleToGreet.add(person);
56+
List<String> greetings = new ArrayList<>();
57+
58+
while (!peopleToGreet.isEmpty()) {
59+
// run activity...
60+
greetings.add(activities.greet(peopleToGreet.get(0)));
61+
peopleToGreet.remove(0);
62+
}
63+
return StringUtils.join(greetings, ",");
64+
}
65+
66+
@Override
67+
public void addGreeting(Person person) {
68+
peopleToGreet.add(person);
69+
}
70+
}
71+
72+
public static class WithoutInitMyWorkflowImpl implements MyWorkflowNoInit {
73+
// We dont initialize peopleToGreet on purpose
74+
private List<Person> peopleToGreet;
75+
private MyGreetingActivities activities =
76+
Workflow.newActivityStub(
77+
MyGreetingActivities.class,
78+
ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());
79+
80+
@Override
81+
public String greet(Person person) {
82+
peopleToGreet.add(person);
83+
List<String> greetings = new ArrayList<>();
84+
85+
while (!peopleToGreet.isEmpty()) {
86+
// run activity...
87+
greetings.add(activities.greet(peopleToGreet.get(0)));
88+
peopleToGreet.remove(0);
89+
}
90+
return StringUtils.join(greetings, ",");
91+
}
92+
93+
@Override
94+
public void addGreeting(Person person) {
95+
peopleToGreet.add(person);
96+
}
97+
}
98+
99+
@ActivityInterface
100+
public interface MyGreetingActivities {
101+
public String greet(Person person);
102+
}
103+
104+
public static class MyGreetingActivitiesImpl implements MyGreetingActivities {
105+
@Override
106+
public String greet(Person person) {
107+
return "Hello " + person.firstName + " " + person.lastName;
108+
}
109+
}
110+
111+
public static class Person {
112+
String firstName;
113+
String lastName;
114+
int age;
115+
116+
public Person() {}
117+
118+
public Person(String firstName, String lastName, int age) {
119+
this.firstName = firstName;
120+
this.lastName = lastName;
121+
this.age = age;
122+
}
123+
124+
public String getFirstName() {
125+
return firstName;
126+
}
127+
128+
public void setFirstName(String firstName) {
129+
this.firstName = firstName;
130+
}
131+
132+
public String getLastName() {
133+
return lastName;
134+
}
135+
136+
public void setLastName(String lastName) {
137+
this.lastName = lastName;
138+
}
139+
140+
public int getAge() {
141+
return age;
142+
}
143+
144+
public void setAge(int age) {
145+
this.age = age;
146+
}
147+
}
148+
149+
public static void main(String[] args) {
150+
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
151+
WorkflowClient client = WorkflowClient.newInstance(service);
152+
WorkerFactory factory = WorkerFactory.newInstance(client);
153+
Worker worker = factory.newWorker(TASK_QUEUE);
154+
155+
worker.registerWorkflowImplementationTypes(WithInitMyWorkflowImpl.class);
156+
// We explicitly want to fail this workflow on NPE as thats what we expect without WorkflowInit
157+
// As we didnt initialize peopleToGreet on purpose
158+
worker.registerWorkflowImplementationTypes(
159+
WorkflowImplementationOptions.newBuilder()
160+
.setFailWorkflowExceptionTypes(NullPointerException.class)
161+
.build(),
162+
WithoutInitMyWorkflowImpl.class);
163+
worker.registerActivitiesImplementations(new MyGreetingActivitiesImpl());
164+
165+
factory.start();
166+
167+
MyWorkflowWithInit withInitStub =
168+
client.newWorkflowStub(
169+
MyWorkflowWithInit.class,
170+
WorkflowOptions.newBuilder()
171+
.setWorkflowId("with-init")
172+
.setTaskQueue(TASK_QUEUE)
173+
.build());
174+
// Start with init workflow which is expected to succeed
175+
// As WorkflowInit will initialize peopleToGreet before signal handler is invoked
176+
WorkflowStub.fromTyped(withInitStub)
177+
.signalWithStart(
178+
"addGreeting",
179+
new Object[] {new Person("Michael", "Jordan", 55)},
180+
new Object[] {new Person("John", "Stockton", 57)});
181+
182+
String result = WorkflowStub.fromTyped(withInitStub).getResult(String.class);
183+
System.out.println("Result: " + result);
184+
185+
// Start without init, this execution is expected to fail as we set
186+
// NullPointerException as a workflow failure type
187+
// NPE is caused because we did not initialize peopleToGreet array
188+
MyWorkflowNoInit noInitStub =
189+
client.newWorkflowStub(
190+
MyWorkflowNoInit.class,
191+
WorkflowOptions.newBuilder()
192+
.setWorkflowId("without-init")
193+
.setTaskQueue(TASK_QUEUE)
194+
.build());
195+
WorkflowStub.fromTyped(noInitStub)
196+
.signalWithStart(
197+
"addGreeting",
198+
new Object[] {new Person("Michael", "Jordan", 55)},
199+
new Object[] {new Person("John", "Stockton", 57)});
200+
try {
201+
WorkflowStub.fromTyped(noInitStub).getResult(String.class);
202+
} catch (WorkflowFailedException e) {
203+
System.out.println("Expected workflow failure: " + e.getMessage());
204+
}
205+
206+
System.exit(0);
207+
}
208+
}

core/src/main/java/io/temporal/samples/hello/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ To run each hello world sample, use one of the following commands:
3434
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSideEffect
3535
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloUpdate
3636
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSignalWithTimer
37+
./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSignalWithStartAndWorkflowInit
3738
```
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.temporal.samples.hello;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.fail;
5+
6+
import io.temporal.client.WorkflowFailedException;
7+
import io.temporal.client.WorkflowOptions;
8+
import io.temporal.client.WorkflowStub;
9+
import io.temporal.testing.TestWorkflowEnvironment;
10+
import io.temporal.testing.TestWorkflowExtension;
11+
import io.temporal.worker.Worker;
12+
import io.temporal.worker.WorkflowImplementationOptions;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.extension.RegisterExtension;
15+
16+
public class HelloSignalWithStartAndWorkflowInitTest {
17+
@RegisterExtension
18+
public static final TestWorkflowExtension testWorkflowExtension =
19+
TestWorkflowExtension.newBuilder()
20+
.registerWorkflowImplementationTypes(
21+
HelloSignalWithStartAndWorkflowInit.WithInitMyWorkflowImpl.class)
22+
.registerWorkflowImplementationTypes(
23+
WorkflowImplementationOptions.newBuilder()
24+
.setFailWorkflowExceptionTypes(NullPointerException.class)
25+
.build(),
26+
HelloSignalWithStartAndWorkflowInit.WithoutInitMyWorkflowImpl.class)
27+
.setActivityImplementations(
28+
new HelloSignalWithStartAndWorkflowInit.MyGreetingActivitiesImpl())
29+
.build();
30+
31+
@Test
32+
public void testWithInit(TestWorkflowEnvironment testEnv, Worker worker) {
33+
HelloSignalWithStartAndWorkflowInit.MyWorkflowWithInit withInitStub =
34+
testEnv
35+
.getWorkflowClient()
36+
.newWorkflowStub(
37+
HelloSignalWithStartAndWorkflowInit.MyWorkflowWithInit.class,
38+
WorkflowOptions.newBuilder()
39+
.setWorkflowId("with-init")
40+
.setTaskQueue(worker.getTaskQueue())
41+
.build());
42+
WorkflowStub.fromTyped(withInitStub)
43+
.signalWithStart(
44+
"addGreeting",
45+
new Object[] {new HelloSignalWithStartAndWorkflowInit.Person("Michael", "Jordan", 55)},
46+
new Object[] {new HelloSignalWithStartAndWorkflowInit.Person("John", "Stockton", 57)});
47+
String result = WorkflowStub.fromTyped(withInitStub).getResult(String.class);
48+
assertEquals("Hello Michael Jordan,Hello John Stockton", result);
49+
}
50+
51+
@Test
52+
public void testWithoutInit(TestWorkflowEnvironment testEnv, Worker worker) {
53+
HelloSignalWithStartAndWorkflowInit.MyWorkflowNoInit noInitStub =
54+
testEnv
55+
.getWorkflowClient()
56+
.newWorkflowStub(
57+
HelloSignalWithStartAndWorkflowInit.MyWorkflowNoInit.class,
58+
WorkflowOptions.newBuilder()
59+
.setWorkflowId("without-init")
60+
.setTaskQueue(worker.getTaskQueue())
61+
.build());
62+
WorkflowStub.fromTyped(noInitStub)
63+
.signalWithStart(
64+
"addGreeting",
65+
new Object[] {new HelloSignalWithStartAndWorkflowInit.Person("Michael", "Jordan", 55)},
66+
new Object[] {new HelloSignalWithStartAndWorkflowInit.Person("John", "Stockton", 57)});
67+
try {
68+
WorkflowStub.fromTyped(noInitStub).getResult(String.class);
69+
fail("Workflow execution should have failed");
70+
} catch (Exception e) {
71+
if (!(e instanceof WorkflowFailedException)) {
72+
fail("Workflow execution should have failed with WorkflowFailedException");
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)