mirror of https://github.com/dapr/java-sdk.git
Support ActorMethod to change method's name. (#448)
This commit is contained in:
parent
d43272fdc6
commit
446cd17e18
|
|
@ -17,6 +17,7 @@ public interface DemoActor {
|
|||
|
||||
void registerReminder();
|
||||
|
||||
@ActorMethod(name = "echo_message")
|
||||
String say(String something);
|
||||
|
||||
void clock(String message);
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@ public class DemoActorImpl extends AbstractActor implements DemoActor, Remindabl
|
|||
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.
|
||||
|
||||
```java
|
||||
import io.dapr.actors.ActorMethod;
|
||||
|
||||
/**
|
||||
* Example of implementation of an Actor.
|
||||
*/
|
||||
|
|
@ -113,6 +115,7 @@ public interface DemoActor {
|
|||
|
||||
void registerReminder();
|
||||
|
||||
@ActorMethod(name = "echo_message")
|
||||
String say(String something);
|
||||
|
||||
void clock(String message);
|
||||
|
|
@ -123,7 +126,10 @@ public interface DemoActor {
|
|||
|
||||
```
|
||||
|
||||
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).
|
||||
The `@ActorType` annotation indicates the Dapr Java SDK that this interface is an Actor Type, allowing a name for the type to be defined.
|
||||
|
||||
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).
|
||||
|
||||
|
||||
Now, execute the following script in order to run DemoActorService:
|
||||
|
|
|
|||
|
|
@ -21,5 +21,13 @@ public @interface ActorMethod {
|
|||
*
|
||||
* @return Actor's method return type.
|
||||
*/
|
||||
Class returns();
|
||||
Class returns() default Undefined.class;
|
||||
|
||||
/**
|
||||
* Actor's method name. This is optional and will override the method's default name for actor invocation.
|
||||
*
|
||||
* @return Actor's method name.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package io.dapr.actors;
|
||||
|
||||
/**
|
||||
* Internal class to represent the undefined value for an optional Class attribute.
|
||||
*/
|
||||
final class Undefined {
|
||||
|
||||
private Undefined() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ import java.lang.reflect.Method;
|
|||
*/
|
||||
class ActorProxyImpl implements ActorProxy, InvocationHandler {
|
||||
|
||||
private static final String UNDEFINED_CLASS_NAME = "io.dapr.actors.Undefined";
|
||||
|
||||
/**
|
||||
* Actor's identifier for this Actor instance.
|
||||
*/
|
||||
|
|
@ -136,29 +138,33 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
|
|||
throw new UnsupportedOperationException("Actor methods can only have zero or one arguments.");
|
||||
}
|
||||
|
||||
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
|
||||
String methodName = method.getName();
|
||||
if ((actorMethodAnnotation != null) && !actorMethodAnnotation.name().isEmpty()) {
|
||||
methodName = actorMethodAnnotation.name();
|
||||
}
|
||||
|
||||
if (method.getParameterCount() == 0) {
|
||||
if (method.getReturnType().equals(Mono.class)) {
|
||||
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
|
||||
if (actorMethodAnnotation == null) {
|
||||
return invokeMethod(method.getName());
|
||||
if ((actorMethodAnnotation == null) || UNDEFINED_CLASS_NAME.equals(actorMethodAnnotation.returns().getName())) {
|
||||
return invokeMethod(methodName);
|
||||
}
|
||||
|
||||
return invokeMethod(method.getName(), actorMethodAnnotation.returns());
|
||||
return invokeMethod(methodName, actorMethodAnnotation.returns());
|
||||
}
|
||||
|
||||
return invokeMethod(method.getName(), method.getReturnType()).block();
|
||||
return invokeMethod(methodName, method.getReturnType()).block();
|
||||
}
|
||||
|
||||
if (method.getReturnType().equals(Mono.class)) {
|
||||
ActorMethod actorMethodAnnotation = method.getDeclaredAnnotation(ActorMethod.class);
|
||||
if (actorMethodAnnotation == null) {
|
||||
return invokeMethod(method.getName(), args[0]);
|
||||
if ((actorMethodAnnotation == null) || UNDEFINED_CLASS_NAME.equals(actorMethodAnnotation.returns().getName())) {
|
||||
return invokeMethod(methodName, args[0]);
|
||||
}
|
||||
|
||||
return invokeMethod(method.getName(), args[0], actorMethodAnnotation.returns());
|
||||
return invokeMethod(methodName, args[0], actorMethodAnnotation.returns());
|
||||
}
|
||||
|
||||
return invokeMethod(method.getName(), args[0], method.getReturnType()).block();
|
||||
return invokeMethod(methodName, args[0], method.getReturnType()).block();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
package io.dapr.actors.runtime;
|
||||
|
||||
import io.dapr.actors.ActorMethod;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
|
@ -35,7 +37,12 @@ class ActorMethodInfoMap {
|
|||
if (methodInfo.getParameterCount() <= 1) {
|
||||
// If Actor class uses overloading, then one will win.
|
||||
// Document this behavior, so users know how to write their code.
|
||||
methods.put(methodInfo.getName(), methodInfo);
|
||||
String methodName = methodInfo.getName();
|
||||
ActorMethod actorMethodAnnotation = methodInfo.getAnnotation(ActorMethod.class);
|
||||
if ((actorMethodAnnotation != null) && !actorMethodAnnotation.name().isEmpty()) {
|
||||
methodName = actorMethodAnnotation.name();
|
||||
}
|
||||
methods.put(methodName, methodInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
package io.dapr.actors.runtime;
|
||||
|
||||
import io.dapr.actors.ActorId;
|
||||
import io.dapr.actors.ActorMethod;
|
||||
import io.dapr.actors.ActorType;
|
||||
import io.dapr.actors.client.ActorProxy;
|
||||
import io.dapr.actors.client.ActorProxyForTestsImpl;
|
||||
|
|
@ -43,6 +44,8 @@ public class ActorNoStateTest {
|
|||
Mono<MyData> classInClassOut(MyData input);
|
||||
Mono<String> registerBadCallbackName();
|
||||
String registerTimerAutoName();
|
||||
@ActorMethod(name = "DotNetMethodASync")
|
||||
Mono<Void> dotNetMethod();
|
||||
}
|
||||
|
||||
@ActorType(name = "MyActor")
|
||||
|
|
@ -108,6 +111,11 @@ public class ActorNoStateTest {
|
|||
public String registerTimerAutoName() {
|
||||
return super.registerActorTimer("", "anything", "state", Duration.ofSeconds(1), Duration.ofSeconds(1)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> dotNetMethod() {
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static class MyData {
|
||||
|
|
@ -174,6 +182,12 @@ public class ActorNoStateTest {
|
|||
actorProxy.invokeMethod("stringInVoidOutIntentionallyThrows", "hello world").block();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodNameChange() {
|
||||
MyActor actor = createActorProxy(MyActor.class);
|
||||
actor.dotNetMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classInClassOut() {
|
||||
ActorProxy actorProxy = createActorProxy();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package io.dapr.it.actors;
|
||||
|
||||
import io.dapr.actors.ActorId;
|
||||
import io.dapr.actors.client.ActorProxy;
|
||||
import io.dapr.actors.client.ActorProxyBuilder;
|
||||
import io.dapr.it.BaseIT;
|
||||
import io.dapr.it.actors.app.MyActor;
|
||||
import io.dapr.it.actors.app.MyActorService;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.dapr.it.Retry.callWithRetry;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ActorMethodNameIT extends BaseIT {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ActorMethodNameIT.class);
|
||||
|
||||
@Test
|
||||
public void actorMethodNameChange() throws Exception {
|
||||
// The call below will fail if service cannot start successfully.
|
||||
startDaprApp(
|
||||
ActorMethodNameIT.class.getSimpleName(),
|
||||
MyActorService.SUCCESS_MESSAGE,
|
||||
MyActorService.class,
|
||||
true,
|
||||
60000);
|
||||
|
||||
logger.debug("Creating proxy builder");
|
||||
ActorProxyBuilder<MyActor> proxyBuilder = deferClose(new ActorProxyBuilder("MyActorTest", MyActor.class));
|
||||
logger.debug("Creating actorId");
|
||||
ActorId actorId1 = new ActorId("1");
|
||||
logger.debug("Building proxy");
|
||||
MyActor proxy = proxyBuilder.build(actorId1);
|
||||
|
||||
callWithRetry(() -> {
|
||||
logger.debug("Invoking dotNetMethod from Proxy");
|
||||
boolean response = proxy.dotNetMethod();
|
||||
logger.debug("asserting true response: [" + response + "]");
|
||||
assertTrue(response);
|
||||
}, 60000);
|
||||
|
||||
logger.debug("Creating proxy builder 2");
|
||||
ActorProxyBuilder<ActorProxy> proxyBuilder2 = deferClose(new ActorProxyBuilder("MyActorTest", ActorProxy.class));
|
||||
logger.debug("Building proxy 2");
|
||||
ActorProxy proxy2 = proxyBuilder2.build(actorId1);
|
||||
|
||||
callWithRetry(() -> {
|
||||
logger.debug("Invoking DotNetMethodAsync from Proxy 2");
|
||||
boolean response = proxy2.invokeMethod("DotNetMethodAsync", boolean.class).block();
|
||||
logger.debug("asserting true response 2: [" + response + "]");
|
||||
assertTrue(response);
|
||||
}, 60000);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
package io.dapr.it.actors.app;
|
||||
|
||||
import io.dapr.actors.ActorMethod;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -26,4 +28,7 @@ public interface MyActor {
|
|||
ArrayList<String> getCallLog();
|
||||
|
||||
String getIdentifier();
|
||||
|
||||
@ActorMethod(name = "DotNetMethodAsync")
|
||||
boolean dotNetMethod();
|
||||
}
|
||||
|
|
@ -199,6 +199,11 @@ public class MyActorImpl extends AbstractActor implements MyActor, Remindable<St
|
|||
return System.getenv("DAPR_HTTP_PORT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dotNetMethod() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void formatAndLog(boolean isEnter, String methodName) {
|
||||
Calendar utcNow = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue