io.micronaut.configuration.rabbitmq.intercept.RabbitMQConsumerAdvice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of micronaut-rabbitmq Show documentation
Show all versions of micronaut-rabbitmq Show documentation
Integration between Micronaut and RabbitMQ
The newest version!
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.configuration.rabbitmq.intercept;
import com.rabbitmq.client.*;
import io.micronaut.configuration.rabbitmq.annotation.Queue;
import io.micronaut.configuration.rabbitmq.annotation.RabbitConnection;
import io.micronaut.configuration.rabbitmq.annotation.RabbitListener;
import io.micronaut.configuration.rabbitmq.annotation.RabbitProperty;
import io.micronaut.configuration.rabbitmq.bind.RabbitBinderRegistry;
import io.micronaut.configuration.rabbitmq.bind.RabbitMessageCloseable;
import io.micronaut.configuration.rabbitmq.bind.RabbitConsumerState;
import io.micronaut.configuration.rabbitmq.connect.ChannelPool;
import io.micronaut.configuration.rabbitmq.exception.RabbitListenerException;
import io.micronaut.configuration.rabbitmq.exception.RabbitListenerExceptionHandler;
import io.micronaut.configuration.rabbitmq.serdes.RabbitMessageSerDes;
import io.micronaut.configuration.rabbitmq.serdes.RabbitMessageSerDesRegistry;
import io.micronaut.context.BeanContext;
import io.micronaut.context.processor.ExecutableMethodProcessor;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.bind.BoundExecutable;
import io.micronaut.core.bind.DefaultExecutableBinder;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.messaging.Acknowledgement;
import io.micronaut.messaging.exceptions.MessageAcknowledgementException;
import io.micronaut.messaging.exceptions.MessageListenerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PreDestroy;
import javax.inject.Qualifier;
import javax.inject.Singleton;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
/**
* An {@link ExecutableMethodProcessor} that will process all beans annotated
* with {@link RabbitListener} and create and subscribe the relevant methods
* as consumers to RabbitMQ queues.
*
* @author James Kleeh
* @since 1.1.0
*/
@Singleton
public class RabbitMQConsumerAdvice implements ExecutableMethodProcessor, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(RabbitMQConsumerAdvice.class);
private final BeanContext beanContext;
private final RabbitBinderRegistry binderRegistry;
private final RabbitListenerExceptionHandler exceptionHandler;
private final RabbitMessageSerDesRegistry serDesRegistry;
private final ConversionService conversionService;
private final Map consumerChannels = new ConcurrentHashMap<>();
/**
* Default constructor.
*
* @param beanContext The bean context
* @param binderRegistry The registry to bind arguments to the method
* @param exceptionHandler The exception handler to use if the consumer isn't a handler
* @param serDesRegistry The serialization/deserialization registry
* @param conversionService The service to convert consume argument values
*/
public RabbitMQConsumerAdvice(BeanContext beanContext,
RabbitBinderRegistry binderRegistry,
RabbitListenerExceptionHandler exceptionHandler,
RabbitMessageSerDesRegistry serDesRegistry,
ConversionService conversionService) {
this.beanContext = beanContext;
this.binderRegistry = binderRegistry;
this.exceptionHandler = exceptionHandler;
this.serDesRegistry = serDesRegistry;
this.conversionService = conversionService;
}
@Override
public void process(BeanDefinition> beanDefinition, ExecutableMethod, ?> method) {
AnnotationValue queueAnn = method.getAnnotation(Queue.class);
if (queueAnn != null) {
String queue = queueAnn.getRequiredValue(String.class);
String clientTag = method.getDeclaringType().getSimpleName() + '#' + method.toString();
boolean reQueue = queueAnn.getRequiredValue("reQueue", boolean.class);
boolean exclusive = queueAnn.getRequiredValue("exclusive", boolean.class);
boolean hasAckArg = Arrays.stream(method.getArguments())
.anyMatch(arg -> Acknowledgement.class.isAssignableFrom(arg.getType()));
String connection = method.findAnnotation(RabbitConnection.class)
.flatMap(conn -> conn.get("connection", String.class))
.orElse(RabbitConnection.DEFAULT_CONNECTION);
ChannelPool channelPool;
try {
channelPool = beanContext.getBean(ChannelPool.class, Qualifiers.byName(connection));
} catch (Throwable e) {
throw new MessageListenerException(String.format("Failed to retrieve a channel pool named [%s] to register a listener", connection), e);
}
Channel channel = getChannel(channelPool);
Integer prefetch = queueAnn.get("prefetch", Integer.class).orElse(null);
try {
if (prefetch != null) {
channel.basicQos(prefetch);
}
} catch (IOException e) {
throw new MessageListenerException(String.format("Failed to set a prefetch count of [%s] on the channel", prefetch), e);
}
ConsumerState state = new ConsumerState();
state.channelPool = channelPool;
state.consumerTag = clientTag;
consumerChannels.put(channel, state);
Map arguments = new HashMap<>();
List> propertyAnnotations = method.getAnnotationValuesByType(RabbitProperty.class);
Collections.reverse(propertyAnnotations); //set the values in the class first so methods can override
propertyAnnotations.forEach((prop) -> {
String name = prop.getRequiredValue("name", String.class);
String value = prop.getValue(String.class).orElse(null);
Class type = prop.get("type", Class.class).orElse(null);
if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
if (type != null && type != Void.class) {
Optional
© 2015 - 2025 Weber Informatics LLC | Privacy Policy