Skip to content

Commit 4117801

Browse files
mukundansundarmacromania
authored andcommitted
auto validate actors (dapr#863)
Signed-off-by: Mukundan Sundararajan <[email protected]> Signed-off-by: Mahmut Canga <[email protected]>
1 parent 64b3ad8 commit 4117801

File tree

5 files changed

+110
-42
lines changed

5 files changed

+110
-42
lines changed

.github/workflows/validate.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ jobs:
154154
working-directory: ./examples
155155
run: |
156156
mm.py ./src/main/java/io/dapr/examples/configuration/http/README.md
157+
- name: Validate actors example
158+
working-directory: ./examples
159+
run: |
160+
mm.py ./src/main/java/io/dapr/examples/actors/README.md
157161
- name: Validate query state HTTP example
158162
working-directory: ./examples
159163
run: |

examples/src/main/java/io/dapr/examples/actors/DemoActor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
@ActorType(name = "DemoActor")
2424
public interface DemoActor {
2525

26-
void registerReminder();
26+
void registerTimer(String state);
27+
28+
void registerReminder(int index);
2729

2830
@ActorMethod(name = "echo_message")
2931
String say(String something);

examples/src/main/java/io/dapr/examples/actors/DemoActorClient.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public static void main(String[] args) throws InterruptedException {
4949
DemoActor actor = builder.build(actorId);
5050

5151
// Start a thread per actor.
52-
Thread thread = new Thread(() -> callActorForever(actorId.toString(), actor));
52+
int finalI = i;
53+
Thread thread = new Thread(() -> callActorForever(finalI, actorId.toString(), actor));
5354
thread.start();
5455
threads.add(thread);
5556
}
@@ -68,19 +69,23 @@ public static void main(String[] args) throws InterruptedException {
6869
* @param actorId Actor's identifier.
6970
* @param actor Actor to be invoked.
7071
*/
71-
private static final void callActorForever(String actorId, DemoActor actor) {
72+
private static final void callActorForever(int index, String actorId, DemoActor actor) {
7273
// First, register reminder.
73-
actor.registerReminder();
74+
actor.registerReminder(index);
75+
// Second register timer.
76+
actor.registerTimer("ping! {" + index + "} ");
7477

7578
// Now, we run until thread is interrupted.
7679
while (!Thread.currentThread().isInterrupted()) {
7780
// Invoke actor method to increment counter by 1, then build message.
7881
int messageNumber = actor.incrementAndGet(1).block();
79-
String message = String.format("Actor %s said message #%d", actorId, messageNumber);
82+
String message = String.format("Message #%d received from actor at index %d with ID %s", messageNumber,
83+
index, actorId);
8084

8185
// Invoke the 'say' method in actor.
8286
String result = actor.say(message);
83-
System.out.println(String.format("Actor %s got a reply: %s", actorId, result));
87+
System.out.println(String.format("Reply %s received from actor at index %d with ID %s ", result,
88+
index, actorId));
8489

8590
try {
8691
// Waits for up to 1 second.

examples/src/main/java/io/dapr/examples/actors/DemoActorImpl.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,18 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
4343
*/
4444
public DemoActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
4545
super(runtimeContext, id);
46+
}
4647

48+
/**
49+
* Register a timer.
50+
*/
51+
@Override
52+
public void registerTimer(String state) {
53+
// For example, the state will be formatted as `ping! {INDEX}` where INDEX is the index of the actor related to ID.
4754
super.registerActorTimer(
4855
null,
4956
"clock",
50-
"ping!",
57+
state,
5158
Duration.ofSeconds(2),
5259
Duration.ofSeconds(1)).block();
5360
}
@@ -56,10 +63,11 @@ public DemoActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
5663
* Registers a reminder.
5764
*/
5865
@Override
59-
public void registerReminder() {
66+
public void registerReminder(int index) {
67+
// For this example, the state reminded by the reminder is deterministic to be the index(not ID) of the actor.
6068
super.registerReminder(
6169
"myremind",
62-
(int) (Integer.MAX_VALUE * Math.random()),
70+
index,
6371
Duration.ofSeconds(5),
6472
Duration.ofSeconds(2)).block();
6573
}
@@ -120,9 +128,9 @@ public void clock(String message) {
120128
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
121129

122130
// Handles the request by printing message.
123-
System.out.println("Server timer for actor "
124-
+ super.getId() + ": "
125-
+ (message == null ? "" : message + " @ " + utcNowAsString));
131+
System.out.println("Server timer triggered with state "
132+
+ (message == null ? "" : message) + " for actor "
133+
+ super.getId() + "@ " + utcNowAsString);
126134
}
127135

128136
/**
@@ -148,8 +156,8 @@ public Mono<Void> receiveReminder(String reminderName, Integer state, Duration d
148156
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
149157
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());
150158

151-
String message = String.format("Server reminded actor %s of: %s for %d @ %s",
152-
this.getId(), reminderName, state, utcNowAsString);
159+
String message = String.format("Reminder %s with state {%d} triggered for actor %s @ %s",
160+
reminderName, state, this.getId(), utcNowAsString);
153161

154162
// Handles the request by printing message.
155163
System.out.println(message);

examples/src/main/java/io/dapr/examples/actors/README.md

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
7575
}
7676

7777
@Override
78-
public void registerReminder() {
78+
public void registerTimer(String state) {
79+
//...
80+
}
81+
82+
@Override
83+
public void registerReminder(int index) {
7984
//...
8085
}
8186

@@ -115,8 +120,10 @@ import io.dapr.actors.ActorMethod;
115120
*/
116121
@ActorType(name = "DemoActor")
117122
public interface DemoActor {
118-
119-
void registerReminder();
123+
124+
void registerTimer(String state);
125+
126+
void registerReminder(int index);
120127

121128
@ActorMethod(name = "echo_message")
122129
String say(String something);
@@ -134,12 +141,35 @@ The `@ActorType` annotation indicates the Dapr Java SDK that this interface is a
134141
The `@ActorMethod` annotation can be applied to an interface method to specify configuration for that method. In this example, the `say` method, is renamed to `echo_message` - this can be used when invoking an actor method implemented in a different programming language (like C# or Python) and the method name does not match Java's naming conventions.
135142
Some methods can return a `Mono` object. In these cases, the `@ActorMethod` annotation is used to hint the Dapr Java SDK of the type encapsulated in the `Mono` object. You can read more about Java generic type erasure [here](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html).
136143

144+
<!-- STEP
145+
name: Run Demo Actor Service
146+
match_order: none
147+
output_match_mode: substring
148+
expected_stdout_lines:
149+
- 'Message #2 received from actor at index 1 with ID'
150+
- 'Message #2 received from actor at index 2 with ID'
151+
- 'Message #2 received from actor at index 0 with ID'
152+
- 'Message #1 received from actor at index 1 with ID'
153+
- 'Message #1 received from actor at index 0 with ID'
154+
- 'Message #3 received from actor at index 2 with ID'
155+
- 'Server timer triggered with state ping! {2} for actor'
156+
- 'Server timer triggered with state ping! {1} for actor'
157+
- 'Server timer triggered with state ping! {0} for actor'
158+
- 'Reminder myremind with state {2} triggered for actor'
159+
- 'Reminder myremind with state {0} triggered for actor'
160+
- 'Reminder myremind with state {1} triggered for actor'
161+
background: true
162+
sleep: 10
163+
timeout_seconds: 90
164+
-->
165+
<!-- Timeout for above service must be more than sleep + timeout for the client-->
166+
137167

138168
Now, execute the following script in order to run DemoActorService:
139169
```sh
140170
dapr run --components-path ./components/actors --app-id demoactorservice --app-port 3000 -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.actors.DemoActorService -p 3000
141171
```
142-
172+
<!-- END_STEP -->
143173
### Running the Actor client
144174

145175
The actor client is a simple java class with a main method that uses the Dapr Actor capabilities in order to create the actors and execute the different methods based on the Actor pattern.
@@ -167,19 +197,23 @@ public class DemoActorClient {
167197
}
168198
}
169199

170-
private static final void callActorForever(String actorId, DemoActor actor) {
200+
private static final void callActorForever(int index, String actorId, DemoActor actor) {
171201
// First, register reminder.
172-
actor.registerReminder();
202+
actor.registerReminder(index);
203+
// Second register timer.
204+
actor.registerTimer("ping! {" + index + "} ");
173205

174206
// Now, we run until thread is interrupted.
175207
while (!Thread.currentThread().isInterrupted()) {
176208
// Invoke actor method to increment counter by 1, then build message.
177209
int messageNumber = actor.incrementAndGet(1).block();
178-
String message = String.format("Actor %s said message #%d", actorId, messageNumber);
210+
String message = String.format("Message #%d received from actor at index %d with ID %s", messageNumber,
211+
index, actorId);
179212

180213
// Invoke the 'say' method in actor.
181214
String result = actor.say(message);
182-
System.out.println(String.format("Actor %s got a reply: %s", actorId, result));
215+
System.out.println(String.format("Reply %s received from actor at index %d with ID %s ", result,
216+
index, actorId));
183217

184218
try {
185219
// Waits for up to 1 second.
@@ -199,57 +233,72 @@ Then, the code executes the `callActorForever` private method once per actor. In
199233

200234
Use the follow command to execute the DemoActorClient:
201235

236+
<!-- STEP
237+
name: Run Demo Actor Client
238+
match_order: none
239+
output_match_mode: substring
240+
expected_stdout_lines:
241+
- 'received from actor at index 2 with ID'
242+
- 'received from actor at index 1 with ID'
243+
- 'received from actor at index 0 with ID '
244+
background: true
245+
sleep: 20
246+
timeout_seconds: 45
247+
-->
248+
249+
202250
```sh
203251
dapr run --components-path ./components/actors --app-id demoactorclient -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.actors.DemoActorClient
204252
```
205253

254+
<!-- END_STEP -->
255+
206256
Once running, the `demoactorservice` logs will start displaying the different steps:
207257
First, we can see actors being activated and the `say` method being invoked:
208258
```text
209-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-1] INFO io.dapr.actors.ActorTrace - Actor:b7b8e745-bc1b-44ff-a0d3-c9a71f68956c Activating ...
259+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-5] INFO io.dapr.actors.ActorTrace - Actor:a855706e-f477-4530-9bff-d7b1cd2988f8 Activating ...
210260
211-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-2] INFO io.dapr.actors.ActorTrace - Actor:d0455670-557b-4ff5-ab4c-8743aca9a423 Activating ...
261+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-6] INFO io.dapr.actors.ActorTrace - Actor:4720f646-baaa-4fae-86dd-aec2fc2ead6e Activating ...
212262
213-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-10] INFO io.dapr.actors.ActorTrace - Actor:56d741b6-b685-45df-974b-9e94efb3e7b4 Activating ...
263+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-7] INFO io.dapr.actors.ActorTrace - Actor:d54592a5-5b5b-4925-8974-6cf309fbdbbf Activating ...
214264
215-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-10] INFO io.dapr.actors.ActorTrace - Actor:56d741b6-b685-45df-974b-9e94efb3e7b4 Activated
265+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-5] INFO io.dapr.actors.ActorTrace - Actor:a855706e-f477-4530-9bff-d7b1cd2988f8 Activated
216266
217-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-1] INFO io.dapr.actors.ActorTrace - Actor:b7b8e745-bc1b-44ff-a0d3-c9a71f68956c Activated
267+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-7] INFO io.dapr.actors.ActorTrace - Actor:d54592a5-5b5b-4925-8974-6cf309fbdbbf Activated
218268
219-
== APP == 2021-03-10 21:08:28,941 {HH:mm:ss.SSS} [http-nio-3000-exec-2] INFO io.dapr.actors.ActorTrace - Actor:d0455670-557b-4ff5-ab4c-8743aca9a423 Activated
269+
== APP == 2023-05-23 11:04:47,348 {HH:mm:ss.SSS} [http-nio-3000-exec-6] INFO io.dapr.actors.ActorTrace - Actor:4720f646-baaa-4fae-86dd-aec2fc2ead6e Activated
220270
221-
== APP == Server say method for actor 56d741b6-b685-45df-974b-9e94efb3e7b4: Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 said message #1 @ 2021-03-10 21:08:29.170
271+
== APP == Server say method for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf: Message #2 received from actor at index 1 with ID d54592a5-5b5b-4925-8974-6cf309fbdbbf @ 2023-05-23 11:04:48.459
222272
223-
== APP == Server say method for actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c: Actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c said message #1 @ 2021-03-10 21:08:29.170
273+
== APP == Server say method for actor 4720f646-baaa-4fae-86dd-aec2fc2ead6e: Message #4 received from actor at index 2 with ID 4720f646-baaa-4fae-86dd-aec2fc2ead6e @ 2023-05-23 11:04:48.695
224274
225-
== APP == Server say method for actor d0455670-557b-4ff5-ab4c-8743aca9a423: Actor d0455670-557b-4ff5-ab4c-8743aca9a423 said message #1 @ 2021-03-10 21:08:29.170
275+
== APP == Server say method for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf: Message #3 received from actor at index 1 with ID d54592a5-5b5b-4925-8974-6cf309fbdbbf @ 2023-05-23 11:04:48.708
226276
```
227277

228278
Then we can see reminders and timers in action:
229279
```text
230-
== APP == Server timer for actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c: ping! @ 2021-03-10 21:08:32.945
280+
== APP == Server timer triggered with state ping! {0} for actor a855706e-f477-4530-9bff-d7b1cd2988f8@ 2023-05-23 11:04:49.021
231281
232-
== APP == Server timer for actor d0455670-557b-4ff5-ab4c-8743aca9a423: ping! @ 2021-03-10 21:08:32.945
282+
== APP == Server timer triggered with state ping! {1} for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf@ 2023-05-23 11:04:49.021
233283
234-
== APP == Server timer for actor 56d741b6-b685-45df-974b-9e94efb3e7b4: ping! @ 2021-03-10 21:08:32.945
284+
== APP == Reminder myremind with state {2} triggered for actor 4720f646-baaa-4fae-86dd-aec2fc2ead6e @ 2023-05-23 11:04:52.012
235285
236-
== APP == Server reminded actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c of: myremind for 1251123938 @ 2021-03-10 21:08:33.007
286+
== APP == Reminder myremind with state {1} triggered for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf @ 2023-05-23 11:04:52.012
237287
288+
== APP == Reminder myremind with state {0} triggered for actor a855706e-f477-4530-9bff-d7b1cd2988f8 @ 2023-05-23 11:04:52.012
238289
```
239290

240291
Finally, the console for `demoactorclient` got the service responses:
241292
```text
242-
== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.170
243-
244-
== APP == Actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c got a reply: 2021-03-10 21:08:29.170
293+
== APP == Reply 2023-05-23 11:04:49.288 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8
245294
246-
== APP == Actor d0455670-557b-4ff5-ab4c-8743aca9a423 got a reply: 2021-03-10 21:08:29.170
295+
== APP == Reply 2023-05-23 11:04:49.408 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8
247296
248-
== APP == Actor d0455670-557b-4ff5-ab4c-8743aca9a423 got a reply: 2021-03-10 21:08:29.292
297+
== APP == Reply 2023-05-23 11:04:49.515 received from actor at index 1 with ID d54592a5-5b5b-4925-8974-6cf309fbdbbf
249298
250-
== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.752
299+
== APP == Reply 2023-05-23 11:04:49.740 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8
251300
252-
== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.804
301+
== APP == Reply 2023-05-23 11:04:49.863 received from actor at index 2 with ID 4720f646-baaa-4fae-86dd-aec2fc2ead6e
253302
```
254303

255304
For more details on Dapr SpringBoot integration, please refer to [Dapr Spring Boot](../../../springboot/DaprApplication.java) Application implementation.

0 commit comments

Comments
 (0)