use of io.micronaut.nats.annotation.Subject in project micronaut-nats by micronaut-projects.
the class NatsConsumerAdvice method process.
@Override
public void process(BeanDefinition<?> beanDefinition, ExecutableMethod<?, ?> method) {
if (method.hasAnnotation(NatsListener.class)) {
AnnotationValue<Subject> subjectAnn = method.getAnnotation(Subject.class);
if (subjectAnn != null) {
String subject = subjectAnn.getRequiredValue(String.class);
String connectionName = method.findAnnotation(NatsConnection.class).flatMap(conn -> conn.get("connection", String.class)).orElse(NatsConnection.DEFAULT_CONNECTION);
io.micronaut.context.Qualifier<Object> qualifer = beanDefinition.getAnnotationTypeByStereotype("javax.inject.Qualifier").map(type -> Qualifiers.byAnnotation(beanDefinition, type)).orElse(null);
Class<Object> beanType = (Class<Object>) beanDefinition.getBeanType();
Class<?> returnTypeClass = method.getReturnType().getType();
boolean isVoid = returnTypeClass == Void.class || returnTypeClass == void.class;
Object bean = beanContext.findBean(beanType, qualifer).orElseThrow(() -> new MessageListenerException("Could not find the bean to execute the method " + method));
Connection connection = beanContext.getBean(Connection.class, Qualifiers.byName(connectionName));
DefaultExecutableBinder<Message> binder = new DefaultExecutableBinder<>();
Dispatcher ds = connection.createDispatcher(msg -> {
BoundExecutable boundExecutable = null;
try {
boundExecutable = binder.bind(method, binderRegistry, msg);
} catch (Throwable e) {
handleException(new NatsListenerException("An error occurred binding the message to the method", e, bean, msg));
}
if (boundExecutable != null) {
Object returnedValue = boundExecutable.invoke(bean);
if (!isVoid && StringUtils.isNotEmpty(msg.getReplyTo())) {
byte[] converted = null;
if (returnedValue != null) {
NatsMessageSerDes serDes = serDesRegistry.findSerdes(method.getReturnType().asArgument()).map(NatsMessageSerDes.class::cast).orElseThrow(() -> new NatsListenerException(String.format("Could not find a serializer for the body argument of type [%s]", returnedValue.getClass().getName()), bean, msg));
converted = serDes.serialize(returnedValue);
}
connection.publish(msg.getReplyTo(), converted);
}
}
});
Optional<String> queueOptional = subjectAnn.get("queue", String.class);
if (queueOptional.isPresent() && !queueOptional.get().isEmpty()) {
ds.subscribe(subject, queueOptional.get());
} else {
ds.subscribe(subject);
}
consumerDispatchers.put(ds, subject);
}
}
}
use of io.micronaut.nats.annotation.Subject 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