Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package io.dapr.examples.actors;

import io.dapr.actors.ActorId;
import io.dapr.actors.client.ActorClient;
import io.dapr.actors.client.ActorProxyBuilder;

import java.util.ArrayList;
Expand All @@ -30,7 +31,8 @@ public class DemoActorClient {
* @throws InterruptedException If program has been interrupted.
*/
public static void main(String[] args) throws InterruptedException {
try (ActorProxyBuilder<DemoActor> builder = new ActorProxyBuilder(DemoActor.class)) {
try (ActorClient client = new ActorClient()) {
ActorProxyBuilder<DemoActor> builder = new ActorProxyBuilder(DemoActor.class, client);
List<Thread> threads = new ArrayList<>(NUM_ACTORS);

// Creates multiple actors.
Expand Down
5 changes: 3 additions & 2 deletions examples/src/main/java/io/dapr/examples/actors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ public class DemoActorClient {
private static final int NUM_ACTORS = 3;

public static void main(String[] args) throws InterruptedException {
try (ActorProxyBuilder<DemoActor> builder = new ActorProxyBuilder(DemoActor.class)) {
try (ActorClient client = new ActorClient()) {
ActorProxyBuilder<DemoActor> builder = new ActorProxyBuilder(DemoActor.class, client);
///...
for (int i = 0; i < NUM_ACTORS; i++) {
DemoActor actor = builder.build(ActorId.createRandom());
Expand Down Expand Up @@ -189,7 +190,7 @@ public class DemoActorClient {
}
```

First, the client defines how many actors it is going to create. The main method declares a `ActorProxyBuilder` to create instances of the `DemoActor` interface, which are implemented automatically by the SDK and make remote calls to the equivalent methods in Actor runtime. `ActorProxyBuilder` implements `Closeable`, which means it holds resources that need to be closed. In this example, we use the "try-resource" feature in Java.
First, the client defines how many actors it is going to create. The main method declares a `ActorClient` and `ActorProxyBuilder` to create instances of the `DemoActor` interface, which are implemented automatically by the SDK and make remote calls to the equivalent methods in Actor runtime. `ActorClient` is reusable for different actor types and should be instantiated only once in your code. `ActorClient` also implements `AutoCloseable`, which means it holds resources that need to be closed. In this example, we use the "try-resource" feature in Java.

Then, the code executes the `callActorForever` private method once per actor. Initially, it will invoke `registerReminder()`, which sets the due time and period for the reminder. Then, `incrementAndGet()` increments a counter, persists it and sends it back as response. Finally `say` method which will print a message containing the received string along with the formatted server time.

Expand Down
113 changes: 113 additions & 0 deletions sdk-actors/src/main/java/io/dapr/actors/client/ActorClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/

package io.dapr.actors.client;

import io.dapr.client.DaprApiProtocol;
import io.dapr.client.DaprHttpBuilder;
import io.dapr.config.Properties;
import io.dapr.v1.DaprGrpc;
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import reactor.core.publisher.Mono;

/**
* Holds a client for Dapr sidecar communication. ActorClient should be reused.
*/
public class ActorClient implements AutoCloseable {
Comment thread
artursouza marked this conversation as resolved.

/**
* gRPC channel for communication with Dapr sidecar.
*/
private final ManagedChannel grpcManagedChannel;

/**
* Dapr's client.
*/
private final DaprClient daprClient;

/**
* Instantiates a new channel for Dapr sidecar communication.
*/
public ActorClient() {
this(Properties.API_PROTOCOL.get());
}

/**
* Instantiates a new channel for Dapr sidecar communication.
*
* @param apiProtocol Dapr's API protocol.
*/
private ActorClient(DaprApiProtocol apiProtocol) {
this(apiProtocol, buildManagedChannel(apiProtocol));
}

/**
* Instantiates a new channel for Dapr sidecar communication.
*
* @param apiProtocol Dapr's API protocol.
*/
private ActorClient(DaprApiProtocol apiProtocol, ManagedChannel grpcManagedChannel) {
this.grpcManagedChannel = grpcManagedChannel;
this.daprClient = buildDaprClient(apiProtocol, grpcManagedChannel);
}

/**
* Invokes an Actor method on Dapr.
*
* @param actorType Type of actor.
* @param actorId Actor Identifier.
* @param methodName Method name to invoke.
* @param jsonPayload Serialized body.
* @return Asynchronous result with the Actor's response.
*/
Mono<byte[]> invoke(String actorType, String actorId, String methodName, byte[] jsonPayload) {
return daprClient.invoke(actorType, actorId, methodName, jsonPayload);
}

/**
* {@inheritDoc}
*/
@Override
public void close() {
if (grpcManagedChannel != null && !grpcManagedChannel.isShutdown()) {
grpcManagedChannel.shutdown();
}
}

/**
* Creates a GRPC managed channel (or null, if not applicable).
*
* @param apiProtocol Dapr's API protocol.
* @return GRPC managed channel or null.
*/
private static ManagedChannel buildManagedChannel(DaprApiProtocol apiProtocol) {
if (apiProtocol != DaprApiProtocol.GRPC) {
return null;
}

int port = Properties.GRPC_PORT.get();
if (port <= 0) {
throw new IllegalArgumentException("Invalid port.");
}

return ManagedChannelBuilder.forAddress(Properties.SIDECAR_IP.get(), port).usePlaintext().build();
}

/**
* Build an instance of the Client based on the provided setup.
*
* @return an instance of the setup Client
* @throws java.lang.IllegalStateException if any required field is missing
*/
private static DaprClient buildDaprClient(DaprApiProtocol apiProtocol, Channel grpcManagedChannel) {
switch (apiProtocol) {
case GRPC: return new DaprGrpcClient(DaprGrpc.newFutureStub(grpcManagedChannel));
case HTTP: return new DaprHttpClient(new DaprHttpBuilder().build());
default: throw new IllegalStateException("Unsupported protocol: " + apiProtocol.name());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,15 @@

import io.dapr.actors.ActorId;
import io.dapr.actors.ActorUtils;
import io.dapr.client.DaprApiProtocol;
import io.dapr.client.DaprHttpBuilder;
import io.dapr.config.Properties;
import io.dapr.serializer.DaprObjectSerializer;
import io.dapr.serializer.DefaultObjectSerializer;
import io.dapr.v1.DaprGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.io.Closeable;
import java.lang.reflect.Proxy;

/**
* Builder to generate an ActorProxy instance. Builder can be reused for multiple instances.
*/
public class ActorProxyBuilder<T> implements Closeable {

/**
* Determine if this builder will create GRPC clients instead of HTTP clients.
*/
private final DaprApiProtocol apiProtocol;
public class ActorProxyBuilder<T> {

/**
* Actor's type.
Expand All @@ -45,36 +33,20 @@ public class ActorProxyBuilder<T> implements Closeable {
private DaprObjectSerializer objectSerializer;

/**
* Builds Dapr HTTP client.
*/
private DaprHttpBuilder daprHttpBuilder;

/**
* Channel for communication with Dapr.
*/
private final ManagedChannel channel;

/**
* Instantiates a new builder for a given Actor type, using {@link DefaultObjectSerializer} by default.
*
* {@link DefaultObjectSerializer} is not recommended for production scenarios.
*
* @param actorTypeClass Actor's type class.
* Client for communication with Dapr's Actor APIs.
*/
public ActorProxyBuilder(Class<T> actorTypeClass) {
this(ActorUtils.findActorTypeName(actorTypeClass), actorTypeClass);
}
private final ActorClient actorClient;

/**
* Instantiates a new builder for a given Actor type, using {@link DefaultObjectSerializer} by default.
*
* {@link DefaultObjectSerializer} is not recommended for production scenarios.
*
* @param actorType Actor's type.
* @param actorTypeClass Actor's type class.
* @param actorClient Dapr's sidecar client for Actor APIs.
*/
public ActorProxyBuilder(String actorType, Class<T> actorTypeClass) {
this(actorType, actorTypeClass, Properties.API_PROTOCOL.get());
public ActorProxyBuilder(Class<T> actorTypeClass, ActorClient actorClient) {
this(ActorUtils.findActorTypeName(actorTypeClass), actorTypeClass, actorClient);
}

/**
Expand All @@ -84,22 +56,23 @@ public ActorProxyBuilder(String actorType, Class<T> actorTypeClass) {
*
* @param actorType Actor's type.
* @param actorTypeClass Actor's type class.
* @param apiProtocol Dapr's API protocol.
* @param actorClient Dapr's sidecar client for Actor APIs.
*/
private ActorProxyBuilder(String actorType, Class<T> actorTypeClass, DaprApiProtocol apiProtocol) {
public ActorProxyBuilder(String actorType, Class<T> actorTypeClass, ActorClient actorClient) {
if ((actorType == null) || actorType.isEmpty()) {
throw new IllegalArgumentException("ActorType is required.");
}
if (actorTypeClass == null) {
throw new IllegalArgumentException("ActorTypeClass is required.");
}
if (actorClient == null) {
throw new IllegalArgumentException("ActorClient is required.");
}

this.apiProtocol = apiProtocol;
this.actorType = actorType;
this.objectSerializer = new DefaultObjectSerializer();
this.clazz = actorTypeClass;
this.daprHttpBuilder = new DaprHttpBuilder();
this.channel = buildManagedChannel(apiProtocol);
this.actorClient = actorClient;
}

/**
Expand Down Expand Up @@ -132,7 +105,7 @@ public T build(ActorId actorId) {
this.actorType,
actorId,
this.objectSerializer,
buildDaprClient());
this.actorClient);

if (this.clazz.equals(ActorProxy.class)) {
// If users want to use the not strongly typed API, we respect that here.
Expand All @@ -145,46 +118,4 @@ public T build(ActorId actorId) {
proxy);
}

/**
* Build an instance of the Client based on the provided setup.
*
* @return an instance of the setup Client
* @throws java.lang.IllegalStateException if any required field is missing
*/
private DaprClient buildDaprClient() {
switch (this.apiProtocol) {
case GRPC: return new DaprGrpcClient(DaprGrpc.newFutureStub(this.channel));
case HTTP: return new DaprHttpClient(daprHttpBuilder.build());
default: throw new IllegalStateException("Unsupported protocol: " + this.apiProtocol.name());
}
}

/**
* {@inheritDoc}
*/
@Override
public void close() {
if (channel != null && !channel.isShutdown()) {
channel.shutdown();
}
}

/**
* Creates a GRPC managed channel (or null, if not applicable).
*
* @param apiProtocol Dapr's API protocol.
* @return GRPC managed channel or null.
*/
private static ManagedChannel buildManagedChannel(DaprApiProtocol apiProtocol) {
if (apiProtocol != DaprApiProtocol.GRPC) {
return null;
}

int port = Properties.GRPC_PORT.get();
if (port <= 0) {
throw new IllegalArgumentException("Invalid port.");
}

return ManagedChannelBuilder.forAddress(Properties.SIDECAR_IP.get(), port).usePlaintext().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,20 @@ class ActorProxyImpl implements ActorProxy, InvocationHandler {
/**
* Client to talk to the Dapr's API.
*/
private final DaprClient daprClient;
private final ActorClient actorClient;

/**
* Creates a new instance of {@link ActorProxyImpl}.
*
* @param actorType actor implementation type of the actor associated with the proxy object.
* @param actorId The actorId associated with the proxy
* @param serializer Serializer and deserializer for method calls.
* @param daprClient Dapr client.
* @param actorClient Dapr client for Actor APIs.
*/
ActorProxyImpl(String actorType, ActorId actorId, DaprObjectSerializer serializer, DaprClient daprClient) {
ActorProxyImpl(String actorType, ActorId actorId, DaprObjectSerializer serializer, ActorClient actorClient) {
this.actorType = actorType;
this.actorId = actorId;
this.daprClient = daprClient;
this.actorClient = actorClient;
this.serializer = serializer;
}

Expand All @@ -77,7 +77,7 @@ public String getActorType() {
*/
@Override
public <T> Mono<T> invokeMethod(String methodName, Object data, TypeRef<T> type) {
return this.daprClient.invoke(actorType, actorId.toString(), methodName, this.serialize(data))
return this.actorClient.invoke(actorType, actorId.toString(), methodName, this.serialize(data))
.filter(s -> s.length > 0)
.map(s -> deserialize(s, type));
}
Expand All @@ -95,7 +95,7 @@ public <T> Mono<T> invokeMethod(String methodName, Object data, Class<T> clazz)
*/
@Override
public <T> Mono<T> invokeMethod(String methodName, TypeRef<T> type) {
return this.daprClient.invoke(actorType, actorId.toString(), methodName, null)
return this.actorClient.invoke(actorType, actorId.toString(), methodName, null)
.filter(s -> s.length > 0)
.map(s -> deserialize(s, type));
}
Expand All @@ -113,15 +113,15 @@ public <T> Mono<T> invokeMethod(String methodName, Class<T> clazz) {
*/
@Override
public Mono<Void> invokeMethod(String methodName) {
return this.daprClient.invoke(actorType, actorId.toString(), methodName, null).then();
return this.actorClient.invoke(actorType, actorId.toString(), methodName, null).then();
}

/**
* {@inheritDoc}
*/
@Override
public Mono<Void> invokeMethod(String methodName, Object data) {
return this.daprClient.invoke(actorType, actorId.toString(), methodName, this.serialize(data)).then();
return this.actorClient.invoke(actorType, actorId.toString(), methodName, this.serialize(data)).then();
}

/**
Expand Down
Loading