use of io.micronaut.nats.annotation.NatsClient in project micronaut-nats by micronaut-projects.
the class NatsIntroductionAdvice method intercept.
@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
if (context.hasAnnotation(NatsClient.class)) {
StaticPublisherState publisherState = publisherCache.get(context.getExecutableMethod(), method -> {
if (!method.findAnnotation(NatsClient.class).isPresent()) {
throw new IllegalStateException("No @NatsClient annotation present on method: " + method);
}
Optional<String> subject = method.findAnnotation(Subject.class).flatMap(AnnotationValue::stringValue);
String connection = method.findAnnotation(NatsConnection.class).flatMap(conn -> conn.get("connection", String.class)).orElse(NatsConnection.DEFAULT_CONNECTION);
Argument<?> bodyArgument = findBodyArgument(method).orElseThrow(() -> new NatsClientException("No valid message body argument found for method: " + method));
Headers methodHeaders = new Headers();
List<AnnotationValue<MessageHeader>> headerAnnotations = method.getAnnotationValuesByType(MessageHeader.class);
// set the values in the class first so methods can override
Collections.reverse(headerAnnotations);
headerAnnotations.forEach(header -> {
String name = header.stringValue("name").orElse(null);
String value = header.stringValue().orElse(null);
if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
methodHeaders.put(name, value);
}
});
NatsMessageSerDes<?> serDes = serDesRegistry.findSerdes(bodyArgument).orElseThrow(() -> new NatsClientException(String.format("Could not find a serializer for the body argument of type [%s]", bodyArgument.getType().getName())));
ReactivePublisher reactivePublisher;
try {
reactivePublisher = beanContext.getBean(ReactivePublisher.class, Qualifiers.byName(connection));
} catch (Throwable e) {
throw new NatsClientException(String.format("Failed to retrieve a publisher named [%s] to publish messages", connection), e);
}
return new StaticPublisherState(subject.orElse(null), bodyArgument, methodHeaders, method.getReturnType(), connection, serDes, reactivePublisher);
});
NatsMessage.Builder builder = NatsMessage.builder();
Headers headers = publisherState.getHeaders();
Argument[] arguments = context.getArguments();
Map<String, Object> parameterValues = context.getParameterValueMap();
for (Argument argument : arguments) {
AnnotationValue<MessageHeader> headerAnn = argument.getAnnotation(MessageHeader.class);
boolean headersObject = argument.getType() == Headers.class;
if (headerAnn != null) {
Map.Entry<String, List<String>> entry = getNameAndValue(argument, headerAnn, parameterValues);
String name = entry.getKey();
List<String> value = entry.getValue();
headers.put(name, value);
} else if (headersObject) {
Headers dynamicHeaders = (Headers) parameterValues.get(argument.getName());
dynamicHeaders.forEach(headers::put);
}
}
if (!headers.isEmpty()) {
builder.headers(headers);
}
Object body = parameterValues.get(publisherState.getBodyArgument().getName());
byte[] converted = publisherState.getSerDes().serialize(body);
builder = builder.data(converted);
String subject = publisherState.getSubject().orElse(findSubjectKey(context).orElse(null));
builder = builder.subject(subject);
if (subject == null) {
throw new IllegalStateException("No @Subject annotation present on method: " + context.getExecutableMethod());
}
Message message = builder.build();
ReactivePublisher reactivePublisher = publisherState.getReactivePublisher();
InterceptedMethod interceptedMethod = InterceptedMethod.of(context);
try {
boolean rpc = !interceptedMethod.returnTypeValue().isVoid();
Mono<?> reactive;
if (rpc) {
reactive = Mono.from(reactivePublisher.publishAndReply(message)).flatMap(response -> {
Object deserialized = deserialize(response, publisherState.getDataType(), publisherState.getDataType());
if (deserialized == null) {
return Mono.empty();
} else {
return Mono.just(deserialized);
}
});
if (interceptedMethod.resultType() == InterceptedMethod.ResultType.SYNCHRONOUS) {
if (LOG.isDebugEnabled()) {
LOG.debug("Publish is an RPC call. Blocking until a response is received.", context);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Publish is an RPC call. Publisher will complete when a response is received.", context);
}
reactive = reactive.subscribeOn(scheduler);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Sending the message.", context);
}
reactive = Mono.from(reactivePublisher.publish(message)).onErrorMap(throwable -> new NatsClientException(String.format("Failed to publish a message with subject: [%s]", subject), throwable, Collections.singletonList(message)));
}
switch(interceptedMethod.resultType()) {
case PUBLISHER:
return interceptedMethod.handleResult(reactive);
case COMPLETION_STAGE:
CompletableFuture<Object> future = new CompletableFuture<>();
reactive.subscribe(new Subscriber<Object>() {
Object value = null;
@Override
public void onSubscribe(Subscription s) {
s.request(1);
}
@Override
public void onNext(Object o) {
value = o;
}
@Override
public void onError(Throwable t) {
future.completeExceptionally(t);
}
@Override
public void onComplete() {
future.complete(value);
}
});
return interceptedMethod.handleResult(future);
case SYNCHRONOUS:
return interceptedMethod.handleResult(reactive.block());
default:
return interceptedMethod.unsupported();
}
} catch (Exception e) {
return interceptedMethod.handleException(e);
}
} else {
return context.proceed();
}
}
Aggregations