Search in sources :

Example 1 with DefaultExecutableBinder

use of io.micronaut.core.bind.DefaultExecutableBinder in project micronaut-kafka by micronaut-projects.

the class KafkaConsumerProcessor method processConsumerRecords.

private boolean processConsumerRecords(final ConsumerState consumerState, final ExecutableMethod<?, ?> method, final Map<Argument<?>, Object> boundArguments, final boolean trackPartitions, final Optional<Argument<?>> ackArg, final ConsumerRecords<?, ?> consumerRecords) {
    final ExecutableBinder<ConsumerRecord<?, ?>> executableBinder = new DefaultExecutableBinder<>(boundArguments);
    final Map<TopicPartition, OffsetAndMetadata> currentOffsets = trackPartitions ? new HashMap<>() : null;
    for (final ConsumerRecord<?, ?> consumerRecord : consumerRecords) {
        LOG.trace("Kafka consumer [{}] received record: {}", method, consumerRecord);
        if (trackPartitions) {
            final TopicPartition topicPartition = new TopicPartition(consumerRecord.topic(), consumerRecord.partition());
            final OffsetAndMetadata offsetAndMetadata = new OffsetAndMetadata(consumerRecord.offset() + 1, null);
            currentOffsets.put(topicPartition, offsetAndMetadata);
        }
        Consumer<?, ?> kafkaConsumer = consumerState.kafkaConsumer;
        ackArg.ifPresent(argument -> boundArguments.put(argument, (KafkaAcknowledgement) () -> kafkaConsumer.commitSync(currentOffsets)));
        try {
            final BoundExecutable boundExecutable = executableBinder.bind(method, binderRegistry, consumerRecord);
            final Object result = boundExecutable.invoke(consumerState.consumerBean);
            if (result != null) {
                final Flux<?> resultFlowable;
                final boolean isBlocking;
                if (Publishers.isConvertibleToPublisher(result)) {
                    resultFlowable = Flux.from(Publishers.convertPublisher(result, Publisher.class));
                    isBlocking = method.hasAnnotation(Blocking.class);
                } else {
                    resultFlowable = Flux.just(result);
                    isBlocking = true;
                }
                handleResultFlux(consumerState, method, consumerRecord, resultFlowable, isBlocking, consumerRecords);
            }
        } catch (Throwable e) {
            if (resolveWithErrorStrategy(consumerState, consumerRecord, e)) {
                return false;
            }
        }
        if (consumerState.offsetStrategy == OffsetStrategy.SYNC_PER_RECORD) {
            try {
                kafkaConsumer.commitSync(currentOffsets);
            } catch (CommitFailedException e) {
                handleException(consumerState, consumerRecord, e);
            }
        } else if (consumerState.offsetStrategy == OffsetStrategy.ASYNC_PER_RECORD) {
            kafkaConsumer.commitAsync(currentOffsets, resolveCommitCallback(consumerState.consumerBean));
        }
    }
    return true;
}
Also used : DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) Blocking(io.micronaut.core.annotation.Blocking) ConsumerRecord(org.apache.kafka.clients.consumer.ConsumerRecord) TopicPartition(org.apache.kafka.common.TopicPartition) BoundExecutable(io.micronaut.core.bind.BoundExecutable) OffsetAndMetadata(org.apache.kafka.clients.consumer.OffsetAndMetadata) KafkaAcknowledgement(io.micronaut.configuration.kafka.KafkaAcknowledgement) CommitFailedException(org.apache.kafka.clients.consumer.CommitFailedException)

Example 2 with DefaultExecutableBinder

use of io.micronaut.core.bind.DefaultExecutableBinder in project micronaut-kafka by micronaut-projects.

the class KafkaConsumerProcessor method processConsumerRecordsAsBatch.

private boolean processConsumerRecordsAsBatch(final ConsumerState consumerState, final ExecutableMethod<?, ?> method, final Map<Argument<?>, Object> boundArguments, final ConsumerRecords<?, ?> consumerRecords) {
    final ExecutableBinder<ConsumerRecords<?, ?>> batchBinder = new DefaultExecutableBinder<>(boundArguments);
    final BoundExecutable boundExecutable = batchBinder.bind(method, batchBinderRegistry, consumerRecords);
    Object result = boundExecutable.invoke(consumerState.consumerBean);
    if (result != null) {
        if (result.getClass().isArray()) {
            result = Arrays.asList((Object[]) result);
        }
        final boolean isPublisher = Publishers.isConvertibleToPublisher(result);
        final Flux<?> resultFlux;
        if (result instanceof Iterable) {
            resultFlux = Flux.fromIterable((Iterable) result);
        } else if (isPublisher) {
            resultFlux = Flux.from(Publishers.convertPublisher(result, Publisher.class));
        } else {
            resultFlux = Flux.just(result);
        }
        final Iterator<? extends ConsumerRecord<?, ?>> iterator = consumerRecords.iterator();
        final boolean isBlocking = !isPublisher || method.hasAnnotation(Blocking.class);
        if (isBlocking) {
            List<?> objects = resultFlux.collectList().block();
            for (Object object : objects) {
                if (iterator.hasNext()) {
                    final ConsumerRecord<?, ?> consumerRecord = iterator.next();
                    handleResultFlux(consumerState, method, consumerRecord, Flux.just(object), isBlocking, consumerRecords);
                }
            }
        } else {
            resultFlux.subscribe(o -> {
                if (iterator.hasNext()) {
                    final ConsumerRecord<?, ?> consumerRecord = iterator.next();
                    handleResultFlux(consumerState, method, consumerRecord, Flux.just(o), isBlocking, consumerRecords);
                }
            });
        }
    }
    return true;
}
Also used : DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) Blocking(io.micronaut.core.annotation.Blocking) Publisher(org.reactivestreams.Publisher) ConsumerRecords(org.apache.kafka.clients.consumer.ConsumerRecords) BoundExecutable(io.micronaut.core.bind.BoundExecutable)

Example 3 with DefaultExecutableBinder

use of io.micronaut.core.bind.DefaultExecutableBinder 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 4 with DefaultExecutableBinder

use of io.micronaut.core.bind.DefaultExecutableBinder in project micronaut-gcp by micronaut-projects.

the class PubSubConsumerAdvice method process.

@Override
public void process(BeanDefinition<?> beanDefinition, ExecutableMethod<?, ?> method) {
    if (beanDefinition.hasDeclaredAnnotation(PubSubListener.class)) {
        AnnotationValue<Subscription> subscriptionAnnotation = method.getAnnotation(Subscription.class);
        io.micronaut.context.Qualifier<Object> qualifier = beanDefinition.getAnnotationTypeByStereotype(Qualifier.class).map(type -> Qualifiers.byAnnotation(beanDefinition, type)).orElse(null);
        boolean hasAckArg = Arrays.stream(method.getArguments()).anyMatch(arg -> Acknowledgement.class.isAssignableFrom(arg.getType()));
        Class<Object> beanType = (Class<Object>) beanDefinition.getBeanType();
        Object bean = beanContext.findBean(beanType, qualifier).orElseThrow(() -> new MessageListenerException("Could not find the bean to execute the method " + method));
        DefaultExecutableBinder<PubSubConsumerState> binder = new DefaultExecutableBinder<>();
        if (subscriptionAnnotation != null) {
            String subscriptionName = subscriptionAnnotation.getRequiredValue(String.class);
            ProjectSubscriptionName projectSubscriptionName = PubSubSubscriptionUtils.toProjectSubscriptionName(subscriptionName, googleCloudConfiguration.getProjectId());
            String defaultContentType = subscriptionAnnotation.stringValue("contentType").orElse(MediaType.APPLICATION_JSON);
            String configuration = subscriptionAnnotation.stringValue("configuration").orElse("");
            MessageReceiver receiver = (PubsubMessage message, AckReplyConsumer ackReplyConsumer) -> {
                String messageContentType = message.getAttributesMap().getOrDefault("Content-Type", "");
                String contentType = Optional.of(messageContentType).filter(StringUtils::isNotEmpty).orElse(defaultContentType);
                DefaultPubSubAcknowledgement pubSubAcknowledgement = new DefaultPubSubAcknowledgement(ackReplyConsumer);
                PubSubConsumerState consumerState = new PubSubConsumerState(message, ackReplyConsumer, projectSubscriptionName, contentType);
                try {
                    BoundExecutable executable = null;
                    try {
                        executable = binder.bind(method, binderRegistry, consumerState);
                    } catch (Exception ex) {
                        handleException(new PubSubMessageReceiverException("Error binding message to the method", ex, bean, consumerState));
                    }
                    // Discard result
                    executable.invoke(bean);
                    if (!hasAckArg) {
                        // if manual ack is not specified we auto ack message after method execution
                        pubSubAcknowledgement.ack();
                    } else {
                        Optional<Object> boundAck = Arrays.stream(executable.getBoundArguments()).filter(o -> (o instanceof DefaultPubSubAcknowledgement)).findFirst();
                        if (boundAck.isPresent()) {
                            DefaultPubSubAcknowledgement manualAck = (DefaultPubSubAcknowledgement) boundAck.get();
                            if (!manualAck.isClientAck()) {
                                logger.warn("Method {} was executed and no message acknowledge detected. Did you forget to invoke ack()/nack()?", method.getName());
                            }
                        }
                    }
                } catch (Exception e) {
                    handleException(new PubSubMessageReceiverException("Error handling message", e, bean, consumerState));
                }
            };
            try {
                this.subscriberFactory.createSubscriber(new SubscriberFactoryConfig(projectSubscriptionName, receiver, configuration, pubSubConfigurationProperties.getSubscribingExecutor()));
            } catch (Exception e) {
                throw new PubSubListenerException("Failed to create subscriber", e);
            }
        }
    }
}
Also used : java.util(java.util) Subscription(io.micronaut.gcp.pubsub.annotation.Subscription) BeanContext(io.micronaut.context.BeanContext) MessageListenerException(io.micronaut.messaging.exceptions.MessageListenerException) LoggerFactory(org.slf4j.LoggerFactory) PubSubSubscriptionUtils(io.micronaut.gcp.pubsub.support.PubSubSubscriptionUtils) ExecutableMethod(io.micronaut.inject.ExecutableMethod) AckReplyConsumer(com.google.cloud.pubsub.v1.AckReplyConsumer) PubSubMessageSerDesRegistry(io.micronaut.gcp.pubsub.serdes.PubSubMessageSerDesRegistry) PubsubMessage(com.google.pubsub.v1.PubsubMessage) MediaType(io.micronaut.http.MediaType) PubSubConfigurationProperties(io.micronaut.gcp.pubsub.configuration.PubSubConfigurationProperties) BoundExecutable(io.micronaut.core.bind.BoundExecutable) ConversionService(io.micronaut.core.convert.ConversionService) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) PubSubListener(io.micronaut.gcp.pubsub.annotation.PubSubListener) MessageReceiver(com.google.cloud.pubsub.v1.MessageReceiver) Logger(org.slf4j.Logger) GoogleCloudConfiguration(io.micronaut.gcp.GoogleCloudConfiguration) PubSubMessageReceiverException(io.micronaut.gcp.pubsub.exception.PubSubMessageReceiverException) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) Singleton(jakarta.inject.Singleton) Acknowledgement(io.micronaut.messaging.Acknowledgement) ExecutableMethodProcessor(io.micronaut.context.processor.ExecutableMethodProcessor) StringUtils(io.micronaut.core.util.StringUtils) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) ProjectSubscriptionName(com.google.pubsub.v1.ProjectSubscriptionName) BeanDefinition(io.micronaut.inject.BeanDefinition) Qualifier(jakarta.inject.Qualifier) io.micronaut.gcp.pubsub.bind(io.micronaut.gcp.pubsub.bind) PubSubMessageReceiverExceptionHandler(io.micronaut.gcp.pubsub.exception.PubSubMessageReceiverExceptionHandler) PubSubListenerException(io.micronaut.gcp.pubsub.exception.PubSubListenerException) PubsubMessage(com.google.pubsub.v1.PubsubMessage) Acknowledgement(io.micronaut.messaging.Acknowledgement) StringUtils(io.micronaut.core.util.StringUtils) Subscription(io.micronaut.gcp.pubsub.annotation.Subscription) MessageListenerException(io.micronaut.messaging.exceptions.MessageListenerException) AckReplyConsumer(com.google.cloud.pubsub.v1.AckReplyConsumer) ProjectSubscriptionName(com.google.pubsub.v1.ProjectSubscriptionName) PubSubMessageReceiverException(io.micronaut.gcp.pubsub.exception.PubSubMessageReceiverException) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) MessageListenerException(io.micronaut.messaging.exceptions.MessageListenerException) PubSubMessageReceiverException(io.micronaut.gcp.pubsub.exception.PubSubMessageReceiverException) PubSubListenerException(io.micronaut.gcp.pubsub.exception.PubSubListenerException) MessageReceiver(com.google.cloud.pubsub.v1.MessageReceiver) BoundExecutable(io.micronaut.core.bind.BoundExecutable) PubSubListenerException(io.micronaut.gcp.pubsub.exception.PubSubListenerException)

Example 5 with DefaultExecutableBinder

use of io.micronaut.core.bind.DefaultExecutableBinder in project micronaut-jms by micronaut-projects.

the class AbstractJMSListenerMethodProcessor method generateAndBindListener.

@SuppressWarnings("unchecked")
private MessageListener generateAndBindListener(Object bean, ExecutableMethod<?, ?> method, ExecutorService executor, boolean acknowledge) {
    return message -> executor.submit(() -> {
        try {
            DefaultExecutableBinder<Message> binder = new DefaultExecutableBinder<>();
            BoundExecutable boundExecutable = binder.bind(method, jmsArgumentBinderRegistry, message);
            boundExecutable.invoke(bean);
            if (acknowledge) {
                try {
                    message.acknowledge();
                } catch (JMSException e) {
                    logger.error("Failed to acknowledge receipt of message with the broker. " + "This message may be falsely retried.", e);
                    throw new MessageAcknowledgementException(e.getMessage(), e);
                }
            }
        } catch (Exception e) {
            logger.error("Failed to process a message: " + message + " " + e.getMessage(), e);
        }
    });
}
Also used : JMSListener(io.micronaut.jms.annotations.JMSListener) BeanContext(io.micronaut.context.BeanContext) JMSConnectionPool(io.micronaut.jms.pool.JMSConnectionPool) LoggerFactory(org.slf4j.LoggerFactory) MessageListener(javax.jms.MessageListener) ExecutableMethod(io.micronaut.inject.ExecutableMethod) JMSDestinationType(io.micronaut.jms.model.JMSDestinationType) JMSListenerContainerFactory(io.micronaut.jms.listener.JMSListenerContainerFactory) Argument(io.micronaut.core.type.Argument) BoundExecutable(io.micronaut.core.bind.BoundExecutable) Message(javax.jms.Message) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) ExecutorService(java.util.concurrent.ExecutorService) MessageAcknowledgementException(io.micronaut.messaging.exceptions.MessageAcknowledgementException) Logger(org.slf4j.Logger) Qualifiers(io.micronaut.inject.qualifiers.Qualifiers) ExecutableMethodProcessor(io.micronaut.context.processor.ExecutableMethodProcessor) JMSException(javax.jms.JMSException) Assert(io.micronaut.jms.util.Assert) Stream(java.util.stream.Stream) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) Annotation(java.lang.annotation.Annotation) BeanDefinition(io.micronaut.inject.BeanDefinition) JMSArgumentBinderRegistry(io.micronaut.jms.bind.JMSArgumentBinderRegistry) MessageBody(io.micronaut.messaging.annotation.MessageBody) CLIENT_ACKNOWLEDGE(javax.jms.Session.CLIENT_ACKNOWLEDGE) Message(javax.jms.Message) DefaultExecutableBinder(io.micronaut.core.bind.DefaultExecutableBinder) BoundExecutable(io.micronaut.core.bind.BoundExecutable) JMSException(javax.jms.JMSException) MessageAcknowledgementException(io.micronaut.messaging.exceptions.MessageAcknowledgementException) MessageAcknowledgementException(io.micronaut.messaging.exceptions.MessageAcknowledgementException) JMSException(javax.jms.JMSException)

Aggregations

BoundExecutable (io.micronaut.core.bind.BoundExecutable)5 DefaultExecutableBinder (io.micronaut.core.bind.DefaultExecutableBinder)5 BeanContext (io.micronaut.context.BeanContext)3 ExecutableMethodProcessor (io.micronaut.context.processor.ExecutableMethodProcessor)3 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)3 BeanDefinition (io.micronaut.inject.BeanDefinition)3 ExecutableMethod (io.micronaut.inject.ExecutableMethod)3 Qualifiers (io.micronaut.inject.qualifiers.Qualifiers)3 Blocking (io.micronaut.core.annotation.Blocking)2 StringUtils (io.micronaut.core.util.StringUtils)2 MessageListenerException (io.micronaut.messaging.exceptions.MessageListenerException)2 Singleton (jakarta.inject.Singleton)2 AckReplyConsumer (com.google.cloud.pubsub.v1.AckReplyConsumer)1 MessageReceiver (com.google.cloud.pubsub.v1.MessageReceiver)1 ProjectSubscriptionName (com.google.pubsub.v1.ProjectSubscriptionName)1 PubsubMessage (com.google.pubsub.v1.PubsubMessage)1 KafkaAcknowledgement (io.micronaut.configuration.kafka.KafkaAcknowledgement)1 ConversionService (io.micronaut.core.convert.ConversionService)1 Argument (io.micronaut.core.type.Argument)1 GoogleCloudConfiguration (io.micronaut.gcp.GoogleCloudConfiguration)1