Search in sources :

Example 6 with MethodInvocationContext

use of io.micronaut.aop.MethodInvocationContext 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)

Example 7 with MethodInvocationContext

use of io.micronaut.aop.MethodInvocationContext in project micronaut-jms by micronaut-projects.

the class JMSProducerMethodInterceptor method intercept.

@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
    if (!context.hasAnnotation(JMSProducer.class)) {
        return context.proceed();
    }
    ExecutableMethod<?, ?> method = context.getExecutableMethod();
    Class<? extends Annotation> annotationType;
    JMSDestinationType destinationType;
    if (method.hasAnnotation(Queue.class)) {
        annotationType = Queue.class;
        destinationType = QUEUE;
    } else if (method.hasAnnotation(Topic.class)) {
        annotationType = Topic.class;
        destinationType = TOPIC;
    } else {
        return context.proceed();
    }
    String connectionFactory = method.stringValue(JMSProducer.class).orElseThrow(() -> new ConfigurationException("@JMSProducer must specify a connection factory."));
    String destinationName = method.stringValue(annotationType).orElseThrow(() -> new ConfigurationException("@" + annotationType.getSimpleName() + " must specify a destination."));
    Map<String, Object> parameterValueMap = context.getParameterValueMap();
    Object body = Arrays.stream(method.getArguments()).filter(arg -> arg.isDeclaredAnnotationPresent(MessageBody.class)).map(arg -> parameterValueMap.get(arg.getName())).findFirst().orElseThrow(() -> new ConfigurationException("One method argument must be annotated with @Body"));
    String serializerName = method.stringValue(annotationType, "serializer").orElse(null);
    Serializer serializer = serializerName == null ? beanContext.getBean(DefaultSerializerDeserializer.class) : beanContext.getBean(Serializer.class, Qualifiers.byName(serializerName));
    MessageHeader[] headers = Arrays.stream(method.getArguments()).filter(arg -> arg.isDeclaredAnnotationPresent(io.micronaut.messaging.annotation.MessageHeader.class)).map(arg -> {
        String argName = arg.getName();
        String headerName = arg.getAnnotationMetadata().stringValue(io.micronaut.messaging.annotation.MessageHeader.class).orElseThrow(() -> new IllegalArgumentException("@Header annotation on argument '" + argName + "' must have a name"));
        return new MessageHeader(headerName, parameterValueMap.get(argName));
    }).toArray(MessageHeader[]::new);
    JMSConnectionPool pool = beanContext.getBean(JMSConnectionPool.class, Qualifiers.byName(connectionFactory));
    JmsProducer producer = new JmsProducer(destinationType, pool, serializer);
    producer.send(destinationName, body, headers);
    return null;
}
Also used : ConfigurationException(io.micronaut.context.exceptions.ConfigurationException) Arrays(java.util.Arrays) TOPIC(io.micronaut.jms.model.JMSDestinationType.TOPIC) BeanContext(io.micronaut.context.BeanContext) JMSConnectionPool(io.micronaut.jms.pool.JMSConnectionPool) QUEUE(io.micronaut.jms.model.JMSDestinationType.QUEUE) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) Singleton(jakarta.inject.Singleton) Queue(io.micronaut.jms.annotations.Queue) DefaultSerializerDeserializer(io.micronaut.jms.serdes.DefaultSerializerDeserializer) Topic(io.micronaut.jms.annotations.Topic) ExecutableMethod(io.micronaut.inject.ExecutableMethod) JmsProducer(io.micronaut.jms.templates.JmsProducer) JMSDestinationType(io.micronaut.jms.model.JMSDestinationType) JMSProducer(io.micronaut.jms.annotations.JMSProducer) MethodInterceptor(io.micronaut.aop.MethodInterceptor) MethodInvocationContext(io.micronaut.aop.MethodInvocationContext) Map(java.util.Map) Annotation(java.lang.annotation.Annotation) MessageHeader(io.micronaut.jms.model.MessageHeader) Serializer(io.micronaut.jms.serdes.Serializer) MessageBody(io.micronaut.messaging.annotation.MessageBody) DefaultSerializerDeserializer(io.micronaut.jms.serdes.DefaultSerializerDeserializer) JmsProducer(io.micronaut.jms.templates.JmsProducer) JMSProducer(io.micronaut.jms.annotations.JMSProducer) JMSDestinationType(io.micronaut.jms.model.JMSDestinationType) JMSConnectionPool(io.micronaut.jms.pool.JMSConnectionPool) ConfigurationException(io.micronaut.context.exceptions.ConfigurationException) MessageHeader(io.micronaut.jms.model.MessageHeader) Topic(io.micronaut.jms.annotations.Topic) Serializer(io.micronaut.jms.serdes.Serializer)

Aggregations

MethodInterceptor (io.micronaut.aop.MethodInterceptor)7 MethodInvocationContext (io.micronaut.aop.MethodInvocationContext)7 StringUtils (io.micronaut.core.util.StringUtils)6 ExecutableMethod (io.micronaut.inject.ExecutableMethod)6 MessageBody (io.micronaut.messaging.annotation.MessageBody)6 Arrays (java.util.Arrays)6 Map (java.util.Map)6 Mono (reactor.core.publisher.Mono)6 InterceptedMethod (io.micronaut.aop.InterceptedMethod)5 InterceptorBean (io.micronaut.aop.InterceptorBean)5 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)5 Publishers (io.micronaut.core.async.publisher.Publishers)5 ConversionService (io.micronaut.core.convert.ConversionService)5 Argument (io.micronaut.core.type.Argument)5 MessageHeader (io.micronaut.messaging.annotation.MessageHeader)5 List (java.util.List)5 Optional (java.util.Optional)5 Logger (org.slf4j.Logger)5 LoggerFactory (org.slf4j.LoggerFactory)5 BeanContext (io.micronaut.context.BeanContext)4