Skip to content

auto validate actors #863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ jobs:
working-directory: ./examples
run: |
mm.py ./src/main/java/io/dapr/examples/configuration/http/README.md
- name: Validate actors example
working-directory: ./examples
run: |
mm.py ./src/main/java/io/dapr/examples/actors/README.md
- name: Validate query state HTTP example
working-directory: ./examples
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
@ActorType(name = "DemoActor")
public interface DemoActor {

void registerReminder();
void registerTimer(String state);

void registerReminder(int index);

@ActorMethod(name = "echo_message")
String say(String something);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public static void main(String[] args) throws InterruptedException {
DemoActor actor = builder.build(actorId);

// Start a thread per actor.
Thread thread = new Thread(() -> callActorForever(actorId.toString(), actor));
int finalI = i;
Thread thread = new Thread(() -> callActorForever(finalI, actorId.toString(), actor));
thread.start();
threads.add(thread);
}
Expand All @@ -68,19 +69,23 @@ public static void main(String[] args) throws InterruptedException {
* @param actorId Actor's identifier.
* @param actor Actor to be invoked.
*/
private static final void callActorForever(String actorId, DemoActor actor) {
private static final void callActorForever(int index, String actorId, DemoActor actor) {
// First, register reminder.
actor.registerReminder();
actor.registerReminder(index);
// Second register timer.
actor.registerTimer("ping! {" + index + "} ");

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

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

try {
// Waits for up to 1 second.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,18 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
*/
public DemoActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
super(runtimeContext, id);
}

/**
* Register a timer.
*/
@Override
public void registerTimer(String state) {
// For example, the state will be formatted as `ping! {INDEX}` where INDEX is the index of the actor related to ID.
super.registerActorTimer(
null,
"clock",
"ping!",
state,
Duration.ofSeconds(2),
Duration.ofSeconds(1)).block();
}
Expand All @@ -56,10 +63,11 @@ public DemoActorImpl(ActorRuntimeContext runtimeContext, ActorId id) {
* Registers a reminder.
*/
@Override
public void registerReminder() {
public void registerReminder(int index) {
// For this example, the state reminded by the reminder is deterministic to be the index(not ID) of the actor.
super.registerReminder(
"myremind",
(int) (Integer.MAX_VALUE * Math.random()),
index,
Duration.ofSeconds(5),
Duration.ofSeconds(2)).block();
}
Expand Down Expand Up @@ -120,9 +128,9 @@ public void clock(String message) {
String utcNowAsString = DATE_FORMAT.format(utcNow.getTime());

// Handles the request by printing message.
System.out.println("Server timer for actor "
+ super.getId() + ": "
+ (message == null ? "" : message + " @ " + utcNowAsString));
System.out.println("Server timer triggered with state "
+ (message == null ? "" : message) + " for actor "
+ super.getId() + "@ " + utcNowAsString);
}

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

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

// Handles the request by printing message.
System.out.println(message);
Expand Down
105 changes: 77 additions & 28 deletions examples/src/main/java/io/dapr/examples/actors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
}

@Override
public void registerReminder() {
public void registerTimer(String state) {
//...
}

@Override
public void registerReminder(int index) {
//...
}

Expand Down Expand Up @@ -115,8 +120,10 @@ import io.dapr.actors.ActorMethod;
*/
@ActorType(name = "DemoActor")
public interface DemoActor {

void registerReminder();

void registerTimer(String state);

void registerReminder(int index);

@ActorMethod(name = "echo_message")
String say(String something);
Expand All @@ -134,12 +141,35 @@ The `@ActorType` annotation indicates the Dapr Java SDK that this interface is a
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.
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).

<!-- STEP
name: Run Demo Actor Service
match_order: none
output_match_mode: substring
expected_stdout_lines:
- 'Message #2 received from actor at index 1 with ID'
- 'Message #2 received from actor at index 2 with ID'
- 'Message #2 received from actor at index 0 with ID'
- 'Message #1 received from actor at index 1 with ID'
- 'Message #1 received from actor at index 0 with ID'
- 'Message #3 received from actor at index 2 with ID'
- 'Server timer triggered with state ping! {2} for actor'
- 'Server timer triggered with state ping! {1} for actor'
- 'Server timer triggered with state ping! {0} for actor'
- 'Reminder myremind with state {2} triggered for actor'
- 'Reminder myremind with state {0} triggered for actor'
- 'Reminder myremind with state {1} triggered for actor'
background: true
sleep: 10
timeout_seconds: 90
-->
<!-- Timeout for above service must be more than sleep + timeout for the client-->


Now, execute the following script in order to run DemoActorService:
```sh
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
```

<!-- END_STEP -->
### Running the Actor client

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.
Expand Down Expand Up @@ -167,19 +197,23 @@ public class DemoActorClient {
}
}

private static final void callActorForever(String actorId, DemoActor actor) {
private static final void callActorForever(int index, String actorId, DemoActor actor) {
// First, register reminder.
actor.registerReminder();
actor.registerReminder(index);
// Second register timer.
actor.registerTimer("ping! {" + index + "} ");

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

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

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

Use the follow command to execute the DemoActorClient:

<!-- STEP
name: Run Demo Actor Client
match_order: none
output_match_mode: substring
expected_stdout_lines:
- 'received from actor at index 2 with ID'
- 'received from actor at index 1 with ID'
- 'received from actor at index 0 with ID '
background: true
sleep: 20
timeout_seconds: 45
-->


```sh
dapr run --components-path ./components/actors --app-id demoactorclient -- java -jar target/dapr-java-sdk-examples-exec.jar io.dapr.examples.actors.DemoActorClient
```

<!-- END_STEP -->

Once running, the `demoactorservice` logs will start displaying the different steps:
First, we can see actors being activated and the `say` method being invoked:
```text
== 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 ...
== 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 ...

== 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 ...
== 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 ...

== 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 ...
== 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 ...

== 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
== 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

== 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
== 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

== 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
== 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

== 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
== 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

== 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
== 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

== 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
== 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
```

Then we can see reminders and timers in action:
```text
== APP == Server timer for actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c: ping! @ 2021-03-10 21:08:32.945
== APP == Server timer triggered with state ping! {0} for actor a855706e-f477-4530-9bff-d7b1cd2988f8@ 2023-05-23 11:04:49.021

== APP == Server timer for actor d0455670-557b-4ff5-ab4c-8743aca9a423: ping! @ 2021-03-10 21:08:32.945
== APP == Server timer triggered with state ping! {1} for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf@ 2023-05-23 11:04:49.021

== APP == Server timer for actor 56d741b6-b685-45df-974b-9e94efb3e7b4: ping! @ 2021-03-10 21:08:32.945
== APP == Reminder myremind with state {2} triggered for actor 4720f646-baaa-4fae-86dd-aec2fc2ead6e @ 2023-05-23 11:04:52.012

== APP == Server reminded actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c of: myremind for 1251123938 @ 2021-03-10 21:08:33.007
== APP == Reminder myremind with state {1} triggered for actor d54592a5-5b5b-4925-8974-6cf309fbdbbf @ 2023-05-23 11:04:52.012

== APP == Reminder myremind with state {0} triggered for actor a855706e-f477-4530-9bff-d7b1cd2988f8 @ 2023-05-23 11:04:52.012
```

Finally, the console for `demoactorclient` got the service responses:
```text
== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.170

== APP == Actor b7b8e745-bc1b-44ff-a0d3-c9a71f68956c got a reply: 2021-03-10 21:08:29.170
== APP == Reply 2023-05-23 11:04:49.288 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8

== APP == Actor d0455670-557b-4ff5-ab4c-8743aca9a423 got a reply: 2021-03-10 21:08:29.170
== APP == Reply 2023-05-23 11:04:49.408 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8

== APP == Actor d0455670-557b-4ff5-ab4c-8743aca9a423 got a reply: 2021-03-10 21:08:29.292
== APP == Reply 2023-05-23 11:04:49.515 received from actor at index 1 with ID d54592a5-5b5b-4925-8974-6cf309fbdbbf

== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.752
== APP == Reply 2023-05-23 11:04:49.740 received from actor at index 0 with ID a855706e-f477-4530-9bff-d7b1cd2988f8

== APP == Actor 56d741b6-b685-45df-974b-9e94efb3e7b4 got a reply: 2021-03-10 21:08:29.804
== APP == Reply 2023-05-23 11:04:49.863 received from actor at index 2 with ID 4720f646-baaa-4fae-86dd-aec2fc2ead6e
```

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