Search in sources :

Example 1 with NatsMessageSerDes

use of io.micronaut.nats.serdes.NatsMessageSerDes 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);
        }
    }
}
Also used : NatsBinderRegistry(io.micronaut.nats.bind.NatsBinderRegistry) BeanContext(io.micronaut.context.BeanContext) MessageListenerException(io.micronaut.messaging.exceptions.MessageListenerException) NatsConnection(io.micronaut.nats.annotation.NatsConnection) Dispatcher(io.nats.client.Dispatcher) NatsMessageSerDes(io.micronaut.nats.serdes.NatsMessageSerDes) ExecutableMethod(io.micronaut.inject.ExecutableMethod) NatsListenerExceptionHandler(io.micronaut.nats.exception.NatsListenerExceptionHandler) Message(io.nats.client.Message) NatsListenerException(io.micronaut.nats.exception.NatsListenerException) NatsMessageSerDesRegistry(io.micronaut.nats.serdes.NatsMessageSerDesRegistry) Map(java.util.Map) Connection(io.nats.client.Connection) BoundExecutable(io.micronaut.core.bind.BoundExecutable) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) Iterator(java.util.Iterator) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) Singleton(jakarta.inject.Singleton) ExecutableMethodProcessor(io.micronaut.context.processor.ExecutableMethodProcessor) StringUtils(io.micronaut.core.util.StringUtils) NatsListener(io.micronaut.nats.annotation.NatsListener) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) Subject(io.micronaut.nats.annotation.Subject) Optional(java.util.Optional) BeanDefinition(io.micronaut.inject.BeanDefinition) Message(io.nats.client.Message) Dispatcher(io.nats.client.Dispatcher) NatsMessageSerDes(io.micronaut.nats.serdes.NatsMessageSerDes) MessageListenerException(io.micronaut.messaging.exceptions.MessageListenerException) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) NatsConnection(io.micronaut.nats.annotation.NatsConnection) Connection(io.nats.client.Connection) Subject(io.micronaut.nats.annotation.Subject) BoundExecutable(io.micronaut.core.bind.BoundExecutable) NatsListenerException(io.micronaut.nats.exception.NatsListenerException)

Example 2 with NatsMessageSerDes

use of io.micronaut.nats.serdes.NatsMessageSerDes 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();
    }
}
Also used : Arrays(java.util.Arrays) BeanContext(io.micronaut.context.BeanContext) NatsConnection(io.micronaut.nats.annotation.NatsConnection) LoggerFactory(org.slf4j.LoggerFactory) MessageHeader(io.micronaut.messaging.annotation.MessageHeader) CompletableFuture(java.util.concurrent.CompletableFuture) NatsMessageSerDes(io.micronaut.nats.serdes.NatsMessageSerDes) Caffeine(io.micronaut.caffeine.cache.Caffeine) Scheduler(reactor.core.scheduler.Scheduler) InterceptedMethod(io.micronaut.aop.InterceptedMethod) ExecutableMethod(io.micronaut.inject.ExecutableMethod) Message(io.nats.client.Message) InterceptorBean(io.micronaut.aop.InterceptorBean) TaskExecutors(io.micronaut.scheduling.TaskExecutors) NatsMessageSerDesRegistry(io.micronaut.nats.serdes.NatsMessageSerDesRegistry) MethodInterceptor(io.micronaut.aop.MethodInterceptor) NatsClientException(io.micronaut.nats.exception.NatsClientException) Map(java.util.Map) Schedulers(reactor.core.scheduler.Schedulers) Argument(io.micronaut.core.type.Argument) ConversionService(io.micronaut.core.convert.ConversionService) Subscriber(org.reactivestreams.Subscriber) ExecutorService(java.util.concurrent.ExecutorService) Logger(org.slf4j.Logger) Headers(io.nats.client.impl.Headers) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) NatsMessage(io.nats.client.impl.NatsMessage) Singleton(jakarta.inject.Singleton) Mono(reactor.core.publisher.Mono) Objects(java.util.Objects) StringUtils(io.micronaut.core.util.StringUtils) AbstractMap(java.util.AbstractMap) List(java.util.List) MethodInvocationContext(io.micronaut.aop.MethodInvocationContext) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) Subscription(org.reactivestreams.Subscription) Subject(io.micronaut.nats.annotation.Subject) Optional(java.util.Optional) Cache(io.micronaut.caffeine.cache.Cache) NatsClient(io.micronaut.nats.annotation.NatsClient) Collections(java.util.Collections) MessageBody(io.micronaut.messaging.annotation.MessageBody) ReactivePublisher(io.micronaut.nats.reactive.ReactivePublisher) Named(jakarta.inject.Named) Argument(io.micronaut.core.type.Argument) Message(io.nats.client.Message) NatsMessage(io.nats.client.impl.NatsMessage) Headers(io.nats.client.impl.Headers) CompletableFuture(java.util.concurrent.CompletableFuture) ReactivePublisher(io.micronaut.nats.reactive.ReactivePublisher) NatsMessage(io.nats.client.impl.NatsMessage) List(java.util.List) Subscription(org.reactivestreams.Subscription) NatsClientException(io.micronaut.nats.exception.NatsClientException) InterceptedMethod(io.micronaut.aop.InterceptedMethod) Subject(io.micronaut.nats.annotation.Subject) NatsClientException(io.micronaut.nats.exception.NatsClientException) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) MessageHeader(io.micronaut.messaging.annotation.MessageHeader) Map(java.util.Map) AbstractMap(java.util.AbstractMap)

Aggregations

BeanContext (io.micronaut.context.BeanContext)2 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)2 StringUtils (io.micronaut.core.util.StringUtils)2 ExecutableMethod (io.micronaut.inject.ExecutableMethod)2 Qualifiers (io.micronaut.inject.qualifiers.Qualifiers)2 NatsConnection (io.micronaut.nats.annotation.NatsConnection)2 Subject (io.micronaut.nats.annotation.Subject)2 NatsMessageSerDes (io.micronaut.nats.serdes.NatsMessageSerDes)2 NatsMessageSerDesRegistry (io.micronaut.nats.serdes.NatsMessageSerDesRegistry)2 Message (io.nats.client.Message)2 Singleton (jakarta.inject.Singleton)2 Map (java.util.Map)2 Optional (java.util.Optional)2 InterceptedMethod (io.micronaut.aop.InterceptedMethod)1 InterceptorBean (io.micronaut.aop.InterceptorBean)1 MethodInterceptor (io.micronaut.aop.MethodInterceptor)1 MethodInvocationContext (io.micronaut.aop.MethodInvocationContext)1 Cache (io.micronaut.caffeine.cache.Cache)1 Caffeine (io.micronaut.caffeine.cache.Caffeine)1 ExecutableMethodProcessor (io.micronaut.context.processor.ExecutableMethodProcessor)1