Skip to content

Commit 15fe5b3

Browse files
Merge branch 'master' into actor_ttl
Signed-off-by: artur-ciocanu <[email protected]>
2 parents 4ff319c + efce229 commit 15fe5b3

File tree

9 files changed

+331
-68
lines changed

9 files changed

+331
-68
lines changed

dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/client/DaprClientAutoConfiguration.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package io.dapr.spring.boot.autoconfigure.client;
1515

16+
import io.dapr.actors.client.ActorClient;
17+
import io.dapr.actors.runtime.ActorRuntime;
1618
import io.dapr.client.DaprClient;
1719
import io.dapr.client.DaprClientBuilder;
1820
import io.dapr.config.Properties;
@@ -70,6 +72,20 @@ DaprWorkflowClient daprWorkflowClient(DaprConnectionDetails daprConnectionDetail
7072
return new DaprWorkflowClient(properties);
7173
}
7274

75+
@Bean
76+
@ConditionalOnMissingBean
77+
ActorClient daprActorClient(DaprConnectionDetails daprConnectionDetails) {
78+
Properties properties = createPropertiesFromConnectionDetails(daprConnectionDetails);
79+
return new ActorClient(properties);
80+
}
81+
82+
@Bean
83+
@ConditionalOnMissingBean
84+
ActorRuntime daprActorRuntime(DaprConnectionDetails daprConnectionDetails) {
85+
Properties properties = createPropertiesFromConnectionDetails(daprConnectionDetails);
86+
return ActorRuntime.getInstance(properties);
87+
}
88+
7389
@Bean
7490
@ConditionalOnMissingBean
7591
WorkflowRuntimeBuilder daprWorkflowRuntimeBuilder(DaprConnectionDetails daprConnectionDetails) {

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

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import io.dapr.client.resiliency.ResiliencyOptions;
1717
import io.dapr.config.Properties;
18+
import io.dapr.utils.NetworkUtils;
1819
import io.dapr.utils.Version;
1920
import io.dapr.v1.DaprGrpc;
2021
import io.grpc.Channel;
@@ -83,7 +84,7 @@ public ActorClient(Properties overrideProperties, ResiliencyOptions resiliencyOp
8384
* @param resiliencyOptions Client resiliency options.
8485
*/
8586
public ActorClient(Properties overrideProperties, Map<String, String> metadata, ResiliencyOptions resiliencyOptions) {
86-
this(buildManagedChannel(overrideProperties),
87+
this(NetworkUtils.buildGrpcManagedChannel(overrideProperties),
8788
metadata,
8889
resiliencyOptions,
8990
overrideProperties.getValue(Properties.API_TOKEN));
@@ -129,25 +130,6 @@ public void close() {
129130
}
130131
}
131132

132-
/**
133-
* Creates a GRPC managed channel (or null, if not applicable).
134-
*
135-
* @param overrideProperties Overrides
136-
* @return GRPC managed channel or null.
137-
*/
138-
private static ManagedChannel buildManagedChannel(Properties overrideProperties) {
139-
int port = overrideProperties.getValue(Properties.GRPC_PORT);
140-
if (port <= 0) {
141-
throw new IllegalArgumentException("Invalid port.");
142-
}
143-
144-
var sidecarHost = overrideProperties.getValue(Properties.SIDECAR_IP);
145-
146-
return ManagedChannelBuilder.forAddress(sidecarHost, port)
147-
.usePlaintext()
148-
.userAgent(Version.getSdkVersion())
149-
.build();
150-
}
151133

152134
/**
153135
* Build an instance of the Client based on the provided setup.

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

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
import io.dapr.config.Properties;
1919
import io.dapr.serializer.DaprObjectSerializer;
2020
import io.dapr.serializer.DefaultObjectSerializer;
21-
import io.dapr.utils.Version;
21+
import io.dapr.utils.NetworkUtils;
2222
import io.grpc.ManagedChannel;
23-
import io.grpc.ManagedChannelBuilder;
2423
import reactor.core.publisher.Mono;
2524

2625
import java.io.Closeable;
@@ -80,23 +79,32 @@ public class ActorRuntime implements Closeable {
8079
* @throws IllegalStateException If cannot instantiate Runtime.
8180
*/
8281
private ActorRuntime() throws IllegalStateException {
83-
this(buildManagedChannel());
82+
this(new Properties());
83+
}
84+
85+
/**
86+
* The default constructor. This should not be called directly.
87+
*
88+
* @throws IllegalStateException If cannot instantiate Runtime.
89+
*/
90+
private ActorRuntime(Properties properties) throws IllegalStateException {
91+
this(NetworkUtils.buildGrpcManagedChannel(properties));
8492
}
8593

8694
/**
8795
* Constructor once channel is available. This should not be called directly.
8896
*
8997
* @param channel GRPC managed channel to be closed (or null).
90-
* @throws IllegalStateException If cannot instantiate Runtime.
98+
* @throws IllegalStateException If you cannot instantiate Runtime.
9199
*/
92100
private ActorRuntime(ManagedChannel channel) throws IllegalStateException {
93-
this(channel, buildDaprClient(channel));
101+
this(channel, new DaprClientImpl(channel));
94102
}
95103

96104
/**
97105
* Constructor with dependency injection, useful for testing. This should not be called directly.
98106
*
99-
* @param channel GRPC managed channel to be closed (or null).
107+
* @param channel GRPC managed channel to be closed (or null).
100108
* @param daprClient Client to communicate with Dapr.
101109
* @throws IllegalStateException If class has one instance already.
102110
*/
@@ -128,6 +136,24 @@ public static ActorRuntime getInstance() {
128136
return instance;
129137
}
130138

139+
/**
140+
* Returns an ActorRuntime object.
141+
*
142+
* @param properties Properties to be used for the runtime.
143+
* @return An ActorRuntime object.
144+
*/
145+
public static ActorRuntime getInstance(Properties properties) {
146+
if (instance == null) {
147+
synchronized (ActorRuntime.class) {
148+
if (instance == null) {
149+
instance = new ActorRuntime(properties);
150+
}
151+
}
152+
}
153+
154+
return instance;
155+
}
156+
131157
/**
132158
* Gets the Actor configuration for this runtime.
133159
*
@@ -149,24 +175,22 @@ public byte[] serializeConfig() throws IOException {
149175

150176
/**
151177
* Registers an actor with the runtime, using {@link DefaultObjectSerializer} and {@link DefaultActorFactory}.
152-
*
153178
* {@link DefaultObjectSerializer} is not recommended for production scenarios.
154179
*
155-
* @param clazz The type of actor.
156-
* @param <T> Actor class type.
180+
* @param clazz The type of actor.
181+
* @param <T> Actor class type.
157182
*/
158183
public <T extends AbstractActor> void registerActor(Class<T> clazz) {
159184
registerActor(clazz, new DefaultObjectSerializer(), new DefaultObjectSerializer());
160185
}
161186

162187
/**
163188
* Registers an actor with the runtime, using {@link DefaultObjectSerializer}.
164-
*
165189
* {@link DefaultObjectSerializer} is not recommended for production scenarios.
166190
*
167-
* @param clazz The type of actor.
168-
* @param actorFactory An optional factory to create actors. This can be used for dependency injection.
169-
* @param <T> Actor class type.
191+
* @param clazz The type of actor.
192+
* @param actorFactory An optional factory to create actors. This can be used for dependency injection.
193+
* @param <T> Actor class type.
170194
*/
171195
public <T extends AbstractActor> void registerActor(Class<T> clazz, ActorFactory<T> actorFactory) {
172196
registerActor(clazz, actorFactory, new DefaultObjectSerializer(), new DefaultObjectSerializer());
@@ -181,8 +205,8 @@ public <T extends AbstractActor> void registerActor(Class<T> clazz, ActorFactory
181205
* @param <T> Actor class type.
182206
*/
183207
public <T extends AbstractActor> void registerActor(
184-
Class<T> clazz, DaprObjectSerializer objectSerializer, DaprObjectSerializer stateSerializer) {
185-
registerActor(clazz, new DefaultActorFactory<T>(), objectSerializer, stateSerializer);
208+
Class<T> clazz, DaprObjectSerializer objectSerializer, DaprObjectSerializer stateSerializer) {
209+
registerActor(clazz, new DefaultActorFactory<T>(), objectSerializer, stateSerializer);
186210
}
187211

188212
/**
@@ -195,9 +219,9 @@ public <T extends AbstractActor> void registerActor(
195219
* @param <T> Actor class type.
196220
*/
197221
public <T extends AbstractActor> void registerActor(
198-
Class<T> clazz, ActorFactory<T> actorFactory,
199-
DaprObjectSerializer objectSerializer,
200-
DaprObjectSerializer stateSerializer) {
222+
Class<T> clazz, ActorFactory<T> actorFactory,
223+
DaprObjectSerializer objectSerializer,
224+
DaprObjectSerializer stateSerializer) {
201225
if (clazz == null) {
202226
throw new IllegalArgumentException("Class is required.");
203227
}
@@ -216,12 +240,12 @@ public <T extends AbstractActor> void registerActor(
216240
// Create ActorManager, if not yet registered.
217241
this.actorManagers.computeIfAbsent(actorTypeInfo.getName(), (k) -> {
218242
ActorRuntimeContext<T> context = new ActorRuntimeContext<>(
219-
this,
220-
objectSerializer,
221-
actorFactory,
222-
actorTypeInfo,
223-
this.daprClient,
224-
new DaprStateAsyncProvider(this.daprClient, stateSerializer));
243+
this,
244+
objectSerializer,
245+
actorFactory,
246+
actorTypeInfo,
247+
this.daprClient,
248+
new DaprStateAsyncProvider(this.daprClient, stateSerializer));
225249
this.config.addRegisteredActorType(actorTypeInfo.getName());
226250
return new ActorManager<T>(context);
227251
});
@@ -236,7 +260,7 @@ public <T extends AbstractActor> void registerActor(
236260
*/
237261
public Mono<Void> deactivate(String actorTypeName, String actorId) {
238262
return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
239-
.flatMap(m -> m.deactivateActor(new ActorId(actorId)));
263+
.flatMap(m -> m.deactivateActor(new ActorId(actorId)));
240264
}
241265

242266
/**
@@ -252,8 +276,8 @@ public Mono<Void> deactivate(String actorTypeName, String actorId) {
252276
public Mono<byte[]> invoke(String actorTypeName, String actorId, String actorMethodName, byte[] payload) {
253277
ActorId id = new ActorId(actorId);
254278
return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
255-
.flatMap(m -> m.activateActor(id).thenReturn(m))
256-
.flatMap(m -> ((ActorManager)m).invokeMethod(id, actorMethodName, payload));
279+
.flatMap(m -> m.activateActor(id).thenReturn(m))
280+
.flatMap(m -> ((ActorManager) m).invokeMethod(id, actorMethodName, payload));
257281
}
258282

259283
/**
@@ -268,8 +292,8 @@ public Mono<byte[]> invoke(String actorTypeName, String actorId, String actorMet
268292
public Mono<Void> invokeReminder(String actorTypeName, String actorId, String reminderName, byte[] params) {
269293
ActorId id = new ActorId(actorId);
270294
return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
271-
.flatMap(m -> m.activateActor(id).thenReturn(m))
272-
.flatMap(m -> ((ActorManager)m).invokeReminder(new ActorId(actorId), reminderName, params));
295+
.flatMap(m -> m.activateActor(id).thenReturn(m))
296+
.flatMap(m -> ((ActorManager) m).invokeReminder(new ActorId(actorId), reminderName, params));
273297
}
274298

275299
/**
@@ -284,8 +308,8 @@ public Mono<Void> invokeReminder(String actorTypeName, String actorId, String re
284308
public Mono<Void> invokeTimer(String actorTypeName, String actorId, String timerName, byte[] params) {
285309
ActorId id = new ActorId(actorId);
286310
return Mono.fromSupplier(() -> this.getActorManager(actorTypeName))
287-
.flatMap(m -> m.activateActor(id).thenReturn(m))
288-
.flatMap(m -> ((ActorManager)m).invokeTimer(new ActorId(actorId), timerName, params));
311+
.flatMap(m -> m.activateActor(id).thenReturn(m))
312+
.flatMap(m -> ((ActorManager) m).invokeTimer(new ActorId(actorId), timerName, params));
289313
}
290314

291315
/**
@@ -318,23 +342,6 @@ private static DaprClient buildDaprClient(ManagedChannel channel) {
318342
return new DaprClientImpl(channel);
319343
}
320344

321-
/**
322-
* Creates a GRPC managed channel (or null, if not applicable).
323-
*
324-
* @return GRPC managed channel or null.
325-
*/
326-
private static ManagedChannel buildManagedChannel() {
327-
int port = Properties.GRPC_PORT.get();
328-
if (port <= 0) {
329-
throw new IllegalStateException("Invalid port.");
330-
}
331-
332-
return ManagedChannelBuilder.forAddress(Properties.SIDECAR_IP.get(), port)
333-
.usePlaintext()
334-
.userAgent(Version.getSdkVersion())
335-
.build();
336-
}
337-
338345
/**
339346
* {@inheritDoc}
340347
*/
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2025 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.it.testcontainers;
15+
16+
import io.dapr.actors.ActorId;
17+
import io.dapr.actors.client.ActorClient;
18+
import io.dapr.actors.client.ActorProxyBuilder;
19+
import io.dapr.actors.runtime.ActorRuntime;
20+
import io.dapr.testcontainers.Component;
21+
import io.dapr.testcontainers.DaprContainer;
22+
import io.dapr.testcontainers.DaprLogLevel;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.Tag;
25+
import org.junit.jupiter.api.Test;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.context.SpringBootTest;
28+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
29+
import org.springframework.test.context.DynamicPropertyRegistry;
30+
import org.springframework.test.context.DynamicPropertySource;
31+
import org.testcontainers.containers.Network;
32+
import org.testcontainers.containers.wait.strategy.Wait;
33+
import org.testcontainers.junit.jupiter.Container;
34+
import org.testcontainers.junit.jupiter.Testcontainers;
35+
36+
import java.util.Map;
37+
import java.util.Random;
38+
import java.util.UUID;
39+
40+
import static org.junit.jupiter.api.Assertions.assertEquals;
41+
42+
@SpringBootTest(
43+
webEnvironment = WebEnvironment.RANDOM_PORT,
44+
classes = {
45+
TestActorsApplication.class,
46+
TestDaprActorsConfiguration.class
47+
}
48+
)
49+
@Testcontainers
50+
@Tag("testcontainers")
51+
public class DaprActorsIT {
52+
private static final Network DAPR_NETWORK = Network.newNetwork();
53+
private static final Random RANDOM = new Random();
54+
private static final int PORT = RANDOM.nextInt(1000) + 8000;
55+
56+
private static final String ACTORS_MESSAGE_PATTERN = ".*Actor API level in the cluster has been updated to 10.*";
57+
58+
@Container
59+
private static final DaprContainer DAPR_CONTAINER = new DaprContainer("daprio/daprd:1.14.4")
60+
.withAppName("actor-dapr-app")
61+
.withNetwork(DAPR_NETWORK)
62+
.withComponent(new Component("kvstore", "state.in-memory", "v1",
63+
Map.of("actorStateStore", "true")))
64+
.withDaprLogLevel(DaprLogLevel.DEBUG)
65+
.withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String()))
66+
.withAppChannelAddress("host.testcontainers.internal")
67+
.withAppPort(PORT);
68+
69+
/**
70+
* Expose the Dapr ports to the host.
71+
*
72+
* @param registry the dynamic property registry
73+
*/
74+
@DynamicPropertySource
75+
static void daprProperties(DynamicPropertyRegistry registry) {
76+
registry.add("dapr.http.endpoint", DAPR_CONTAINER::getHttpEndpoint);
77+
registry.add("dapr.grpc.endpoint", DAPR_CONTAINER::getGrpcEndpoint);
78+
registry.add("server.port", () -> PORT);
79+
}
80+
81+
@Autowired
82+
private ActorClient daprActorClient;
83+
84+
@Autowired
85+
private ActorRuntime daprActorRuntime;
86+
87+
@BeforeEach
88+
public void setUp(){
89+
org.testcontainers.Testcontainers.exposeHostPorts(PORT);
90+
daprActorRuntime.registerActor(TestActorImpl.class);
91+
// Ensure the subscriptions are registered
92+
Wait.forLogMessage(ACTORS_MESSAGE_PATTERN, 1).waitUntilReady(DAPR_CONTAINER);
93+
}
94+
95+
@Test
96+
public void testActors() {
97+
ActorProxyBuilder<TestActor> builder = new ActorProxyBuilder<>(TestActor.class, daprActorClient);
98+
ActorId actorId = ActorId.createRandom();
99+
TestActor actor = builder.build(actorId);
100+
101+
String message = UUID.randomUUID().toString();
102+
103+
String echoedMessage = actor.echo(message);
104+
105+
assertEquals(echoedMessage, message);
106+
}
107+
}

0 commit comments

Comments
 (0)