Search in sources :

Example 6 with InterceptedMethod

use of io.micronaut.aop.InterceptedMethod in project resilience4j by resilience4j.

the class CircuitBreakerInterceptor method intercept.

@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
    Optional<AnnotationValue<io.github.resilience4j.micronaut.annotation.CircuitBreaker>> opt = context.findAnnotation(io.github.resilience4j.micronaut.annotation.CircuitBreaker.class);
    if (!opt.isPresent()) {
        return context.proceed();
    }
    ExecutableMethod executableMethod = context.getExecutableMethod();
    final String name = executableMethod.stringValue(io.github.resilience4j.micronaut.annotation.CircuitBreaker.class, "name").orElse("default");
    CircuitBreaker circuitBreaker = this.circuitBreakerRegistry.circuitBreaker(name);
    InterceptedMethod interceptedMethod = InterceptedMethod.of(context);
    try {
        switch(interceptedMethod.resultType()) {
            case PUBLISHER:
                return interceptedMethod.handleResult(fallbackReactiveTypes(Flowable.fromPublisher(interceptedMethod.interceptResultAsPublisher()).compose(CircuitBreakerOperator.of(circuitBreaker)), context));
            case COMPLETION_STAGE:
                return interceptedMethod.handleResult(fallbackForFuture(circuitBreaker.executeCompletionStage(() -> {
                    try {
                        return interceptedMethod.interceptResultAsCompletionStage();
                    } catch (Exception e) {
                        throw new CompletionException(e);
                    }
                }), context));
            case SYNCHRONOUS:
                try {
                    return circuitBreaker.executeCheckedSupplier(context::proceed);
                } catch (Throwable exception) {
                    return fallback(context, exception);
                }
            default:
                return interceptedMethod.unsupported();
        }
    } catch (Exception e) {
        return interceptedMethod.handleException(e);
    }
}
Also used : CircuitBreaker(io.github.resilience4j.circuitbreaker.CircuitBreaker) ExecutableMethod(io.micronaut.inject.ExecutableMethod) InterceptedMethod(io.micronaut.aop.InterceptedMethod) CompletionException(java.util.concurrent.CompletionException) CompletionException(java.util.concurrent.CompletionException) AnnotationValue(io.micronaut.core.annotation.AnnotationValue)

Example 7 with InterceptedMethod

use of io.micronaut.aop.InterceptedMethod in project resilience4j by resilience4j.

the class TimeLimiterInterceptor method intercept.

@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
    Optional<AnnotationValue<io.github.resilience4j.micronaut.annotation.TimeLimiter>> opt = context.findAnnotation(io.github.resilience4j.micronaut.annotation.TimeLimiter.class);
    if (!opt.isPresent()) {
        return context.proceed();
    }
    ExecutableMethod executableMethod = context.getExecutableMethod();
    final String name = executableMethod.stringValue(io.github.resilience4j.micronaut.annotation.TimeLimiter.class).orElse("default");
    TimeLimiter timeLimiter = this.timeLimiterRegistry.timeLimiter(name);
    InterceptedMethod interceptedMethod = InterceptedMethod.of(context);
    try {
        switch(interceptedMethod.resultType()) {
            case PUBLISHER:
                return interceptedMethod.handleResult(fallbackReactiveTypes(Flowable.fromPublisher(interceptedMethod.interceptResultAsPublisher()).compose(TimeLimiterTransformer.of(timeLimiter)), context));
            case COMPLETION_STAGE:
                return interceptedMethod.handleResult(fallbackForFuture(timeLimiter.executeCompletionStage(executorService, () -> {
                    try {
                        return interceptedMethod.interceptResultAsCompletionStage();
                    } catch (Exception e) {
                        throw new CompletionException(e);
                    }
                }), context));
            case SYNCHRONOUS:
                try {
                    return timeLimiter.executeFutureSupplier(() -> CompletableFuture.supplyAsync(context::proceed));
                } catch (Throwable exception) {
                    return fallback(context, exception);
                }
            default:
                return interceptedMethod.unsupported();
        }
    } catch (Exception e) {
        return interceptedMethod.handleException(e);
    }
}
Also used : TimeLimiter(io.github.resilience4j.timelimiter.TimeLimiter) ExecutableMethod(io.micronaut.inject.ExecutableMethod) InterceptedMethod(io.micronaut.aop.InterceptedMethod) CompletionException(java.util.concurrent.CompletionException) CompletionException(java.util.concurrent.CompletionException) AnnotationValue(io.micronaut.core.annotation.AnnotationValue)

Example 8 with InterceptedMethod

use of io.micronaut.aop.InterceptedMethod in project micronaut-kafka by micronaut-projects.

the class KafkaClientIntroductionAdvice method intercept.

@SuppressWarnings("unchecked")
@Override
public final Object intercept(MethodInvocationContext<Object, Object> context) {
    if (context.hasAnnotation(KafkaClient.class)) {
        if (!context.hasAnnotation(KafkaClient.class)) {
            throw new IllegalStateException("No @KafkaClient annotation present on method: " + context);
        }
        ProducerState producerState = getProducer(context);
        InterceptedMethod interceptedMethod = InterceptedMethod.of(context);
        try {
            Argument<?> returnType = interceptedMethod.returnTypeValue();
            if (Argument.OBJECT_ARGUMENT.equalsType(returnType)) {
                returnType = Argument.of(RecordMetadata.class);
            }
            switch(interceptedMethod.resultType()) {
                case COMPLETION_STAGE:
                    CompletableFuture<Object> completableFuture = returnCompletableFuture(context, producerState, returnType);
                    return interceptedMethod.handleResult(completableFuture);
                case PUBLISHER:
                    Flux<Object> returnFlowable = returnPublisher(context, producerState, returnType);
                    return interceptedMethod.handleResult(returnFlowable);
                case SYNCHRONOUS:
                    return returnSynchronous(context, producerState);
                default:
                    return interceptedMethod.unsupported();
            }
        } catch (Exception e) {
            return interceptedMethod.handleException(e);
        }
    } else {
        // can't be implemented so proceed
        return context.proceed();
    }
}
Also used : RecordMetadata(org.apache.kafka.clients.producer.RecordMetadata) InterceptedMethod(io.micronaut.aop.InterceptedMethod) KafkaClient(io.micronaut.configuration.kafka.annotation.KafkaClient) MessagingClientException(io.micronaut.messaging.exceptions.MessagingClientException)

Example 9 with InterceptedMethod

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

InterceptedMethod (io.micronaut.aop.InterceptedMethod)9 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)6 CompletionException (java.util.concurrent.CompletionException)6 ExecutableMethod (io.micronaut.inject.ExecutableMethod)5 InterceptorBean (io.micronaut.aop.InterceptorBean)2 MethodInterceptor (io.micronaut.aop.MethodInterceptor)2 MethodInvocationContext (io.micronaut.aop.MethodInvocationContext)2 StringUtils (io.micronaut.core.util.StringUtils)2 CompletableFuture (java.util.concurrent.CompletableFuture)2 CircuitBreaker (io.github.resilience4j.circuitbreaker.CircuitBreaker)1 RateLimiter (io.github.resilience4j.ratelimiter.RateLimiter)1 Retry (io.github.resilience4j.retry.Retry)1 TimeLimiter (io.github.resilience4j.timelimiter.TimeLimiter)1 Counted (io.micrometer.core.annotation.Counted)1 EXCEPTION_TAG (io.micrometer.core.aop.TimedAspect.EXCEPTION_TAG)1 Counter (io.micrometer.core.instrument.Counter)1 MeterRegistry (io.micrometer.core.instrument.MeterRegistry)1 Cache (io.micronaut.caffeine.cache.Cache)1 Caffeine (io.micronaut.caffeine.cache.Caffeine)1 KafkaClient (io.micronaut.configuration.kafka.annotation.KafkaClient)1