Skip to content

Commit 3da6e61

Browse files
authored
Merge 651c778 into d43272f
2 parents d43272f + 651c778 commit 3da6e61

10 files changed

Lines changed: 143 additions & 13 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public interface DemoActor {
1717

1818
void registerReminder();
1919

20+
@ActorMethod(name = "echo_message")
2021
String say(String something);
2122

2223
void clock(String message);

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
105105
An actor inherits from `AbstractActor` and implements the constructor to pass through `ActorRuntimeContext` and `ActorId`. By default, the actor's name will be the same as the class' name. Optionally, it can be annotated with `ActorType` and override the actor's name. The actor's methods can be synchronously or use [Project Reactor's Mono](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html) return type. Finally, state management is done via methods in `super.getActorStateManager()`. The `DemoActor` interface is used by the Actor runtime and also client. See how `DemoActor` interface can be annotated as Dapr Actor.
106106

107107
```java
108+
import io.dapr.actors.ActorMethod;
109+
108110
/**
109111
* Example of implementation of an Actor.
110112
*/
@@ -113,6 +115,7 @@ public interface DemoActor {
113115

114116
void registerReminder();
115117

118+
@ActorMethod(name = "echo_message")
116119
String say(String something);
117120

118121
void clock(String message);
@@ -123,7 +126,10 @@ public interface DemoActor {
123126

124127
```
125128

126-
The `@ActorType` annotation indicates the Dapr Java SDK that this interface is an Actor Type, allowing a name for the type to be defined. 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).
129+
The `@ActorType` annotation indicates the Dapr Java SDK that this interface is an Actor Type, allowing a name for the type to be defined.
130+
131+
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.
132+
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).
127133

128134

129135
Now, execute the following script in order to run DemoActorService:

sdk-actors/src/main/java/io/dapr/actors/ActorMethod.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,13 @@
2121
*
2222
* @return Actor's method return type.
2323
*/
24-
Class returns();
24+
Class returns() default Undefined.class;
25+
26+
/**
27+
* Actor's method name. This is optional and will override the method's default name for actor invocation.
28+
*
29+
* @return Actor's method name.
30+
*/
31+
String name() default "";
32+
2533
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
package io.dapr.actors;
7+
8+
/**
9+
* Internal class to represent the undefined value for an optional Class attribute.
10+
*/
11+
final class Undefined {
12+
13+
private Undefined() {
14+
}
15+
16+
}

sdk-actors/src/main/java/io/dapr/actors/client/ActorProxyImpl.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
*/
2222
class ActorProxyImpl implements ActorProxy, InvocationHandler {
2323

24+
private static final String UNDEFINED_CLASS_NAME = "io.dapr.actors.Undefined";
25+
2426
/**
2527
* Actor's identifier for this Actor instance.
2628
*/
@@ -136,29 +138,33 @@ public Object invoke(Object proxy, Method method, Object[] args) {
136138
throw new UnsupportedOperationException("Actor methods can only have zero or one arguments.");
137139
}
138140

141+
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
142+
String methodName = method.getName();
143+
if ((actorMethodAnnotation != null) && !actorMethodAnnotation.name().isEmpty()) {
144+
methodName = actorMethodAnnotation.name();
145+
}
146+
139147
if (method.getParameterCount() == 0) {
140148
if (method.getReturnType().equals(Mono.class)) {
141-
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
142-
if (actorMethodAnnotation == null) {
143-
return invokeMethod(method.getName());
149+
if ((actorMethodAnnotation == null) || UNDEFINED_CLASS_NAME.equals(actorMethodAnnotation.returns().getName())) {
150+
return invokeMethod(methodName);
144151
}
145152

146-
return invokeMethod(method.getName(), actorMethodAnnotation.returns());
153+
return invokeMethod(methodName, actorMethodAnnotation.returns());
147154
}
148155

149-
return invokeMethod(method.getName(), method.getReturnType()).block();
156+
return invokeMethod(methodName, method.getReturnType()).block();
150157
}
151158

152159
if (method.getReturnType().equals(Mono.class)) {
153-
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
154-
if (actorMethodAnnotation == null) {
155-
return invokeMethod(method.getName(), args[0]);
160+
if ((actorMethodAnnotation == null) || UNDEFINED_CLASS_NAME.equals(actorMethodAnnotation.returns().getName())) {
161+
return invokeMethod(methodName, args[0]);
156162
}
157163

158-
return invokeMethod(method.getName(), args[0], actorMethodAnnotation.returns());
164+
return invokeMethod(methodName, args[0], actorMethodAnnotation.returns());
159165
}
160166

161-
return invokeMethod(method.getName(), args[0], method.getReturnType()).block();
167+
return invokeMethod(methodName, args[0], method.getReturnType()).block();
162168
}
163169

164170
/**

sdk-actors/src/main/java/io/dapr/actors/runtime/ActorMethodInfoMap.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.dapr.actors.runtime;
77

8+
import io.dapr.actors.ActorMethod;
9+
810
import java.lang.reflect.Method;
911
import java.util.Collection;
1012
import java.util.Collections;
@@ -35,7 +37,12 @@ class ActorMethodInfoMap {
3537
if (methodInfo.getParameterCount() <= 1) {
3638
// If Actor class uses overloading, then one will win.
3739
// Document this behavior, so users know how to write their code.
38-
methods.put(methodInfo.getName(), methodInfo);
40+
String methodName = methodInfo.getName();
41+
ActorMethod actorMethodAnnotation = methodInfo.getAnnotation(ActorMethod.class);
42+
if ((actorMethodAnnotation != null) && !actorMethodAnnotation.name().isEmpty()) {
43+
methodName = actorMethodAnnotation.name();
44+
}
45+
methods.put(methodName, methodInfo);
3946
}
4047
}
4148
}

sdk-actors/src/test/java/io/dapr/actors/runtime/ActorNoStateTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.dapr.actors.runtime;
77

88
import io.dapr.actors.ActorId;
9+
import io.dapr.actors.ActorMethod;
910
import io.dapr.actors.ActorType;
1011
import io.dapr.actors.client.ActorProxy;
1112
import io.dapr.actors.client.ActorProxyForTestsImpl;
@@ -43,6 +44,8 @@ public interface MyActor {
4344
Mono<MyData> classInClassOut(MyData input);
4445
Mono<String> registerBadCallbackName();
4546
String registerTimerAutoName();
47+
@ActorMethod(name = "DotNetMethodASync")
48+
Mono<Void> dotNetMethod();
4649
}
4750

4851
@ActorType(name = "MyActor")
@@ -108,6 +111,11 @@ public Mono<String> registerBadCallbackName() {
108111
public String registerTimerAutoName() {
109112
return super.registerActorTimer("", "anything", "state", Duration.ofSeconds(1), Duration.ofSeconds(1)).block();
110113
}
114+
115+
@Override
116+
public Mono<Void> dotNetMethod() {
117+
return Mono.empty();
118+
}
111119
}
112120

113121
static class MyData {
@@ -174,6 +182,12 @@ public void stringInVoidOutIntentionallyThrows() {
174182
actorProxy.invokeMethod("stringInVoidOutIntentionallyThrows", "hello world").block();
175183
}
176184

185+
@Test
186+
public void testMethodNameChange() {
187+
MyActor actor = createActorProxy(MyActor.class);
188+
actor.dotNetMethod();
189+
}
190+
177191
@Test
178192
public void classInClassOut() {
179193
ActorProxy actorProxy = createActorProxy();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
package io.dapr.it.actors;
7+
8+
import io.dapr.actors.ActorId;
9+
import io.dapr.actors.client.ActorProxy;
10+
import io.dapr.actors.client.ActorProxyBuilder;
11+
import io.dapr.it.BaseIT;
12+
import io.dapr.it.actors.app.MyActor;
13+
import io.dapr.it.actors.app.MyActorService;
14+
import org.junit.Test;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
import static io.dapr.it.Retry.callWithRetry;
19+
import static org.junit.Assert.assertTrue;
20+
21+
public class ActorMethodNameIT extends BaseIT {
22+
23+
private static Logger logger = LoggerFactory.getLogger(ActorMethodNameIT.class);
24+
25+
@Test
26+
public void actorMethodNameChange() throws Exception {
27+
// The call below will fail if service cannot start successfully.
28+
startDaprApp(
29+
ActorMethodNameIT.class.getSimpleName(),
30+
MyActorService.SUCCESS_MESSAGE,
31+
MyActorService.class,
32+
true,
33+
60000);
34+
35+
logger.debug("Creating proxy builder");
36+
ActorProxyBuilder<MyActor> proxyBuilder = deferClose(new ActorProxyBuilder("MyActorTest", MyActor.class));
37+
logger.debug("Creating actorId");
38+
ActorId actorId1 = new ActorId("1");
39+
logger.debug("Building proxy");
40+
MyActor proxy = proxyBuilder.build(actorId1);
41+
42+
callWithRetry(() -> {
43+
logger.debug("Invoking dotNetMethod from Proxy");
44+
boolean response = proxy.dotNetMethod();
45+
logger.debug("asserting true response: [" + response + "]");
46+
assertTrue(response);
47+
}, 60000);
48+
49+
logger.debug("Creating proxy builder 2");
50+
ActorProxyBuilder<ActorProxy> proxyBuilder2 = deferClose(new ActorProxyBuilder("MyActorTest", ActorProxy.class));
51+
logger.debug("Building proxy 2");
52+
ActorProxy proxy2 = proxyBuilder2.build(actorId1);
53+
54+
callWithRetry(() -> {
55+
logger.debug("Invoking DotNetMethodAsync from Proxy 2");
56+
boolean response = proxy2.invokeMethod("DotNetMethodAsync", boolean.class).block();
57+
logger.debug("asserting true response 2: [" + response + "]");
58+
assertTrue(response);
59+
}, 60000);
60+
61+
}
62+
}

sdk-tests/src/test/java/io/dapr/it/actors/app/MyActor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.dapr.it.actors.app;
77

8+
import io.dapr.actors.ActorMethod;
9+
810
import java.util.ArrayList;
911
import java.util.List;
1012

@@ -26,4 +28,7 @@ public interface MyActor {
2628
ArrayList<String> getCallLog();
2729

2830
String getIdentifier();
31+
32+
@ActorMethod(name = "DotNetMethodAsync")
33+
boolean dotNetMethod();
2934
}

sdk-tests/src/test/java/io/dapr/it/actors/app/MyActorImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ public String getIdentifier() {
199199
return System.getenv("DAPR_HTTP_PORT");
200200
}
201201

202+
@Override
203+
public boolean dotNetMethod() {
204+
return true;
205+
}
206+
202207
private void formatAndLog(boolean isEnter, String methodName) {
203208
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
204209

0 commit comments

Comments
 (0)