mirror of https://github.com/dapr/java-sdk.git
Implement actor client metadata. (#1165)
Signed-off-by: Artur Souza <asouza.pro@gmail.com> Co-authored-by: Cassie Coyle <cassie@diagrid.io>
This commit is contained in:
parent
bd1667b043
commit
fcdf3c3188
|
@ -22,6 +22,9 @@ import io.grpc.ManagedChannel;
|
||||||
import io.grpc.ManagedChannelBuilder;
|
import io.grpc.ManagedChannelBuilder;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a client for Dapr sidecar communication. ActorClient should be reused.
|
* Holds a client for Dapr sidecar communication. ActorClient should be reused.
|
||||||
*/
|
*/
|
||||||
|
@ -59,7 +62,7 @@ public class ActorClient implements AutoCloseable {
|
||||||
* @param overrideProperties Override properties.
|
* @param overrideProperties Override properties.
|
||||||
*/
|
*/
|
||||||
public ActorClient(Properties overrideProperties) {
|
public ActorClient(Properties overrideProperties) {
|
||||||
this(buildManagedChannel(overrideProperties), null, overrideProperties.getValue(Properties.API_TOKEN));
|
this(overrideProperties, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,21 +72,38 @@ public class ActorClient implements AutoCloseable {
|
||||||
* @param resiliencyOptions Client resiliency options.
|
* @param resiliencyOptions Client resiliency options.
|
||||||
*/
|
*/
|
||||||
public ActorClient(Properties overrideProperties, ResiliencyOptions resiliencyOptions) {
|
public ActorClient(Properties overrideProperties, ResiliencyOptions resiliencyOptions) {
|
||||||
this(buildManagedChannel(overrideProperties), resiliencyOptions, overrideProperties.getValue(Properties.API_TOKEN));
|
this(overrideProperties, null, resiliencyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new channel for Dapr sidecar communication.
|
||||||
|
*
|
||||||
|
* @param overrideProperties Override properties.
|
||||||
|
* @param metadata gRPC metadata or HTTP headers for actor invocation.
|
||||||
|
* @param resiliencyOptions Client resiliency options.
|
||||||
|
*/
|
||||||
|
public ActorClient(Properties overrideProperties, Map<String, String> metadata, ResiliencyOptions resiliencyOptions) {
|
||||||
|
this(buildManagedChannel(overrideProperties),
|
||||||
|
metadata,
|
||||||
|
resiliencyOptions,
|
||||||
|
overrideProperties.getValue(Properties.API_TOKEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new channel for Dapr sidecar communication.
|
* Instantiates a new channel for Dapr sidecar communication.
|
||||||
*
|
*
|
||||||
* @param grpcManagedChannel gRPC channel.
|
* @param grpcManagedChannel gRPC channel.
|
||||||
|
* @param metadata gRPC metadata or HTTP headers for actor invocation.
|
||||||
* @param resiliencyOptions Client resiliency options.
|
* @param resiliencyOptions Client resiliency options.
|
||||||
|
* @param daprApiToken Dapr API token.
|
||||||
*/
|
*/
|
||||||
private ActorClient(
|
private ActorClient(
|
||||||
ManagedChannel grpcManagedChannel,
|
ManagedChannel grpcManagedChannel,
|
||||||
|
Map<String, String> metadata,
|
||||||
ResiliencyOptions resiliencyOptions,
|
ResiliencyOptions resiliencyOptions,
|
||||||
String daprApiToken) {
|
String daprApiToken) {
|
||||||
this.grpcManagedChannel = grpcManagedChannel;
|
this.grpcManagedChannel = grpcManagedChannel;
|
||||||
this.daprClient = buildDaprClient(grpcManagedChannel, resiliencyOptions, daprApiToken);
|
this.daprClient = buildDaprClient(grpcManagedChannel, metadata, resiliencyOptions, daprApiToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,10 +157,12 @@ public class ActorClient implements AutoCloseable {
|
||||||
*/
|
*/
|
||||||
private static DaprClient buildDaprClient(
|
private static DaprClient buildDaprClient(
|
||||||
Channel grpcManagedChannel,
|
Channel grpcManagedChannel,
|
||||||
|
Map<String, String> metadata,
|
||||||
ResiliencyOptions resiliencyOptions,
|
ResiliencyOptions resiliencyOptions,
|
||||||
String daprApiToken) {
|
String daprApiToken) {
|
||||||
return new DaprClientImpl(
|
return new DaprClientImpl(
|
||||||
DaprGrpc.newStub(grpcManagedChannel),
|
DaprGrpc.newStub(grpcManagedChannel),
|
||||||
|
metadata == null ? null : Collections.unmodifiableMap(metadata),
|
||||||
resiliencyOptions,
|
resiliencyOptions,
|
||||||
daprApiToken);
|
daprApiToken);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.MonoSink;
|
import reactor.core.publisher.MonoSink;
|
||||||
import reactor.util.context.ContextView;
|
import reactor.util.context.ContextView;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -57,19 +58,30 @@ class DaprClientImpl implements DaprClient {
|
||||||
*/
|
*/
|
||||||
private final DaprClientGrpcInterceptors grpcInterceptors;
|
private final DaprClientGrpcInterceptors grpcInterceptors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for actor invocation requests.
|
||||||
|
*/
|
||||||
|
private final Map<String, String> metadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal constructor.
|
* Internal constructor.
|
||||||
*
|
*
|
||||||
* @param grpcClient Dapr's GRPC client.
|
* @param grpcClient Dapr's GRPC client.
|
||||||
|
* @param metadata gRPC metadata or HTTP headers for actor server to receive.
|
||||||
* @param resiliencyOptions Client resiliency options (optional).
|
* @param resiliencyOptions Client resiliency options (optional).
|
||||||
* @param daprApiToken Dapr API token (optional).
|
* @param daprApiToken Dapr API token (optional).
|
||||||
*/
|
*/
|
||||||
DaprClientImpl(DaprGrpc.DaprStub grpcClient, ResiliencyOptions resiliencyOptions, String daprApiToken) {
|
DaprClientImpl(
|
||||||
|
DaprGrpc.DaprStub grpcClient,
|
||||||
|
Map<String, String> metadata,
|
||||||
|
ResiliencyOptions resiliencyOptions,
|
||||||
|
String daprApiToken) {
|
||||||
this.client = grpcClient;
|
this.client = grpcClient;
|
||||||
this.grpcInterceptors = new DaprClientGrpcInterceptors(daprApiToken,
|
this.grpcInterceptors = new DaprClientGrpcInterceptors(daprApiToken,
|
||||||
new TimeoutPolicy(resiliencyOptions == null ? null : resiliencyOptions.getTimeout()));
|
new TimeoutPolicy(resiliencyOptions == null ? null : resiliencyOptions.getTimeout()));
|
||||||
this.retryPolicy = new RetryPolicy(
|
this.retryPolicy = new RetryPolicy(
|
||||||
resiliencyOptions == null ? null : resiliencyOptions.getMaxRetries());
|
resiliencyOptions == null ? null : resiliencyOptions.getMaxRetries());
|
||||||
|
this.metadata = metadata == null ? Map.of() : metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,6 +94,7 @@ class DaprClientImpl implements DaprClient {
|
||||||
.setActorType(actorType)
|
.setActorType(actorType)
|
||||||
.setActorId(actorId)
|
.setActorId(actorId)
|
||||||
.setMethod(methodName)
|
.setMethod(methodName)
|
||||||
|
.putAllMetadata(this.metadata)
|
||||||
.setData(jsonPayload == null ? ByteString.EMPTY : ByteString.copyFrom(jsonPayload))
|
.setData(jsonPayload == null ? ByteString.EMPTY : ByteString.copyFrom(jsonPayload))
|
||||||
.build();
|
.build();
|
||||||
return Mono.deferContextual(
|
return Mono.deferContextual(
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class DaprGrpcClientTest {
|
||||||
InProcessChannelBuilder.forName(serverName).directExecutor().build());
|
InProcessChannelBuilder.forName(serverName).directExecutor().build());
|
||||||
|
|
||||||
// Create a HelloWorldClient using the in-process channel;
|
// Create a HelloWorldClient using the in-process channel;
|
||||||
client = new DaprClientImpl(DaprGrpc.newStub(channel), null, null);
|
client = new DaprClientImpl(DaprGrpc.newStub(channel), null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -187,11 +187,19 @@ public class DaprRun implements Stoppable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActorClient newActorClient() {
|
public ActorClient newActorClient() {
|
||||||
return this.newActorClient(null);
|
return this.newActorClient(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorClient newActorClient(Map<String, String> metadata) {
|
||||||
|
return this.newActorClient(metadata, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActorClient newActorClient(ResiliencyOptions resiliencyOptions) {
|
public ActorClient newActorClient(ResiliencyOptions resiliencyOptions) {
|
||||||
return new ActorClient(new Properties(this.getPropertyOverrides()), resiliencyOptions);
|
return this.newActorClient(null, resiliencyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorClient newActorClient(Map<String, String> metadata, ResiliencyOptions resiliencyOptions) {
|
||||||
|
return new ActorClient(new Properties(this.getPropertyOverrides()), metadata, resiliencyOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForAppHealth(int maxWaitMilliseconds) throws InterruptedException {
|
public void waitForAppHealth(int maxWaitMilliseconds) throws InterruptedException {
|
||||||
|
|
|
@ -16,12 +16,17 @@ package io.dapr.it.actors;
|
||||||
import io.dapr.actors.ActorId;
|
import io.dapr.actors.ActorId;
|
||||||
import io.dapr.actors.client.ActorProxyBuilder;
|
import io.dapr.actors.client.ActorProxyBuilder;
|
||||||
import io.dapr.it.BaseIT;
|
import io.dapr.it.BaseIT;
|
||||||
|
import io.dapr.it.DaprRun;
|
||||||
import io.dapr.it.actors.app.MyActor;
|
import io.dapr.it.actors.app.MyActor;
|
||||||
import io.dapr.it.actors.app.MyActorService;
|
import io.dapr.it.actors.app.MyActorService;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static io.dapr.it.Retry.callWithRetry;
|
import static io.dapr.it.Retry.callWithRetry;
|
||||||
import static io.dapr.it.TestUtils.assertThrowsDaprExceptionSubstring;
|
import static io.dapr.it.TestUtils.assertThrowsDaprExceptionSubstring;
|
||||||
|
|
||||||
|
@ -30,23 +35,24 @@ public class ActorExceptionIT extends BaseIT {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(ActorExceptionIT.class);
|
private static Logger logger = LoggerFactory.getLogger(ActorExceptionIT.class);
|
||||||
|
|
||||||
@Test
|
private static DaprRun run;
|
||||||
public void exceptionTest() throws Exception {
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void start() throws Exception {
|
||||||
// The call below will fail if service cannot start successfully.
|
// The call below will fail if service cannot start successfully.
|
||||||
var run = startDaprApp(
|
run = startDaprApp(
|
||||||
ActorExceptionIT.class.getSimpleName(),
|
ActorExceptionIT.class.getSimpleName(),
|
||||||
MyActorService.SUCCESS_MESSAGE,
|
MyActorService.SUCCESS_MESSAGE,
|
||||||
MyActorService.class,
|
MyActorService.class,
|
||||||
true,
|
true,
|
||||||
60000);
|
60000);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug("Creating proxy builder");
|
@Test
|
||||||
|
public void exceptionTest() throws Exception {
|
||||||
ActorProxyBuilder<MyActor> proxyBuilder =
|
ActorProxyBuilder<MyActor> proxyBuilder =
|
||||||
new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient()));
|
new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient()));
|
||||||
logger.debug("Creating actorId");
|
MyActor proxy = proxyBuilder.build(new ActorId("1"));
|
||||||
ActorId actorId1 = new ActorId("1");
|
|
||||||
logger.debug("Building proxy");
|
|
||||||
MyActor proxy = proxyBuilder.build(actorId1);
|
|
||||||
|
|
||||||
callWithRetry(() -> {
|
callWithRetry(() -> {
|
||||||
assertThrowsDaprExceptionSubstring(
|
assertThrowsDaprExceptionSubstring(
|
||||||
|
@ -55,4 +61,20 @@ public class ActorExceptionIT extends BaseIT {
|
||||||
() -> proxy.throwException());
|
() -> proxy.throwException());
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exceptionDueToMetadataTest() throws Exception {
|
||||||
|
// Setting this HTTP header via actor metadata will cause the Actor HTTP server to error.
|
||||||
|
Map<String, String> metadata = Map.of("Content-Length", "9999");
|
||||||
|
ActorProxyBuilder<MyActor> proxyBuilderMetadataOverride =
|
||||||
|
new ActorProxyBuilder("MyActorTest", MyActor.class, deferClose(run.newActorClient(metadata)));
|
||||||
|
|
||||||
|
MyActor proxyWithMetadata = proxyBuilderMetadataOverride.build(new ActorId("2"));
|
||||||
|
callWithRetry(() -> {
|
||||||
|
assertThrowsDaprExceptionSubstring(
|
||||||
|
"INTERNAL",
|
||||||
|
"ContentLength=9999 with Body length 13",
|
||||||
|
() -> proxyWithMetadata.say("hello world"));
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ package io.dapr.internal.grpc;
|
||||||
|
|
||||||
import io.dapr.internal.grpc.interceptors.DaprApiTokenInterceptor;
|
import io.dapr.internal.grpc.interceptors.DaprApiTokenInterceptor;
|
||||||
import io.dapr.internal.grpc.interceptors.DaprAppIdInterceptor;
|
import io.dapr.internal.grpc.interceptors.DaprAppIdInterceptor;
|
||||||
import io.dapr.internal.grpc.interceptors.DaprMetadataInterceptor;
|
import io.dapr.internal.grpc.interceptors.DaprMetadataReceiverInterceptor;
|
||||||
import io.dapr.internal.grpc.interceptors.DaprTimeoutInterceptor;
|
import io.dapr.internal.grpc.interceptors.DaprTimeoutInterceptor;
|
||||||
import io.dapr.internal.grpc.interceptors.DaprTracingInterceptor;
|
import io.dapr.internal.grpc.interceptors.DaprTracingInterceptor;
|
||||||
import io.dapr.internal.resiliency.TimeoutPolicy;
|
import io.dapr.internal.resiliency.TimeoutPolicy;
|
||||||
|
@ -35,10 +35,18 @@ public class DaprClientGrpcInterceptors {
|
||||||
|
|
||||||
private final TimeoutPolicy timeoutPolicy;
|
private final TimeoutPolicy timeoutPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a holder of all gRPC interceptors.
|
||||||
|
*/
|
||||||
public DaprClientGrpcInterceptors() {
|
public DaprClientGrpcInterceptors() {
|
||||||
this(null, null);
|
this(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a holder of all gRPC interceptors.
|
||||||
|
* @param daprApiToken Dapr API token.
|
||||||
|
* @param timeoutPolicy Timeout Policy.
|
||||||
|
*/
|
||||||
public DaprClientGrpcInterceptors(String daprApiToken, TimeoutPolicy timeoutPolicy) {
|
public DaprClientGrpcInterceptors(String daprApiToken, TimeoutPolicy timeoutPolicy) {
|
||||||
this.daprApiToken = daprApiToken;
|
this.daprApiToken = daprApiToken;
|
||||||
this.timeoutPolicy = timeoutPolicy;
|
this.timeoutPolicy = timeoutPolicy;
|
||||||
|
@ -118,7 +126,7 @@ public class DaprClientGrpcInterceptors {
|
||||||
new DaprApiTokenInterceptor(this.daprApiToken),
|
new DaprApiTokenInterceptor(this.daprApiToken),
|
||||||
new DaprTimeoutInterceptor(this.timeoutPolicy),
|
new DaprTimeoutInterceptor(this.timeoutPolicy),
|
||||||
new DaprTracingInterceptor(context),
|
new DaprTracingInterceptor(context),
|
||||||
new DaprMetadataInterceptor(metadataConsumer));
|
new DaprMetadataReceiverInterceptor(metadataConsumer));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ limitations under the License.
|
||||||
package io.dapr.internal.grpc.interceptors;
|
package io.dapr.internal.grpc.interceptors;
|
||||||
|
|
||||||
import io.dapr.client.Headers;
|
import io.dapr.client.Headers;
|
||||||
import io.dapr.config.Properties;
|
|
||||||
import io.grpc.CallOptions;
|
import io.grpc.CallOptions;
|
||||||
import io.grpc.Channel;
|
import io.grpc.Channel;
|
||||||
import io.grpc.ClientCall;
|
import io.grpc.ClientCall;
|
||||||
|
|
|
@ -27,7 +27,7 @@ import java.util.function.Consumer;
|
||||||
/**
|
/**
|
||||||
* Consumes gRPC metadata.
|
* Consumes gRPC metadata.
|
||||||
*/
|
*/
|
||||||
public class DaprMetadataInterceptor implements ClientInterceptor {
|
public class DaprMetadataReceiverInterceptor implements ClientInterceptor {
|
||||||
|
|
||||||
private final Consumer<Metadata> metadataConsumer;
|
private final Consumer<Metadata> metadataConsumer;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class DaprMetadataInterceptor implements ClientInterceptor {
|
||||||
* Creates an instance of the consumer for gRPC metadata.
|
* Creates an instance of the consumer for gRPC metadata.
|
||||||
* @param metadataConsumer gRPC metadata consumer
|
* @param metadataConsumer gRPC metadata consumer
|
||||||
*/
|
*/
|
||||||
public DaprMetadataInterceptor(Consumer<Metadata> metadataConsumer) {
|
public DaprMetadataReceiverInterceptor(Consumer<Metadata> metadataConsumer) {
|
||||||
this.metadataConsumer = metadataConsumer;
|
this.metadataConsumer = metadataConsumer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue