io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor Maven / Gradle / Ivy
/*
* Copyright 2014-2017 the original author or 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.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import io.bitsensor.plugins.shaded.org.apache.commons.logging.Log;
import io.bitsensor.plugins.shaded.org.apache.commons.logging.LogFactory;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.AbstractDeclarable;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.AbstractExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Binding;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Binding.DestinationType;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.DirectExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Exchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.ExchangeTypes;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.FanoutExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.HeadersExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Queue;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.TopicExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.core.RabbitAdmin;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.MultiMethodRabbitListenerEndpoint;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import io.bitsensor.plugins.shaded.org.springframework.aop.framework.Advised;
import io.bitsensor.plugins.shaded.org.springframework.aop.support.AopUtils;
import io.bitsensor.plugins.shaded.org.springframework.beans.BeansException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanClassLoaderAware;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanFactoryAware;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanInitializationException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.ListableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.NoSuchBeanDefinitionException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.SmartInitializingSingleton;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanExpressionContext;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanExpressionResolver;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanPostProcessor;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.ConfigurableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.context.EnvironmentAware;
import io.bitsensor.plugins.shaded.org.springframework.context.expression.StandardBeanExpressionResolver;
import io.bitsensor.plugins.shaded.org.springframework.core.Ordered;
import io.bitsensor.plugins.shaded.org.springframework.core.annotation.AnnotationUtils;
import io.bitsensor.plugins.shaded.org.springframework.core.convert.ConversionService;
import io.bitsensor.plugins.shaded.org.springframework.core.convert.support.DefaultConversionService;
import io.bitsensor.plugins.shaded.org.springframework.core.env.Environment;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import io.bitsensor.plugins.shaded.org.springframework.util.Assert;
import io.bitsensor.plugins.shaded.org.springframework.util.ClassUtils;
import io.bitsensor.plugins.shaded.org.springframework.util.ReflectionUtils;
import io.bitsensor.plugins.shaded.org.springframework.util.StringUtils;
/**
* Bean post-processor that registers methods annotated with {@link RabbitListener}
* to be invoked by a AMQP message listener container created under the cover
* by a {@link io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory}
* according to the parameters of the annotation.
*
* Annotated methods can use flexible arguments as defined by {@link RabbitListener}.
*
*
This post-processor is automatically registered by Spring's
* {@code } XML element, and also by the {@link EnableRabbit}
* annotation.
*
* Auto-detect any {@link RabbitListenerConfigurer} instances in the container,
* allowing for customization of the registry to be used, the default container
* factory or for fine-grained control over endpoints registration. See
* {@link EnableRabbit} Javadoc for complete usage details.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @author Gary Russell
* @since 1.4
* @see RabbitListener
* @see EnableRabbit
* @see RabbitListenerConfigurer
* @see RabbitListenerEndpointRegistrar
* @see RabbitListenerEndpointRegistry
* @see io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpoint
* @see MethodRabbitListenerEndpoint
*/
public class RabbitListenerAnnotationBeanPostProcessor
implements BeanPostProcessor, Ordered, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware,
SmartInitializingSingleton {
/**
* The bean name of the default {@link io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory}.
*/
public static final String DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "rabbitListenerContainerFactory";
public static final String RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY = "spring.rabbitmq.emptyStringArguments";
private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();
private final Log logger = LogFactory.getLog(this.getClass());
private final Set emptyStringArguments = new HashSet();
private RabbitListenerEndpointRegistry endpointRegistry;
private String containerFactoryBeanName = DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME;
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
private final RabbitHandlerMethodFactoryAdapter messageHandlerMethodFactory =
new RabbitHandlerMethodFactoryAdapter();
private final RabbitListenerEndpointRegistrar registrar = new RabbitListenerEndpointRegistrar();
private final AtomicInteger counter = new AtomicInteger();
private final ConcurrentMap, TypeMetadata> typeCache = new ConcurrentHashMap, TypeMetadata>();
private BeanExpressionResolver resolver = new StandardBeanExpressionResolver();
private BeanExpressionContext expressionContext;
private int increment;
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
public RabbitListenerAnnotationBeanPostProcessor() {
this.emptyStringArguments.add("x-dead-letter-exchange");
}
/**
* Set the {@link RabbitListenerEndpointRegistry} that will hold the created
* endpoint and manage the lifecycle of the related listener container.
* @param endpointRegistry the {@link RabbitListenerEndpointRegistry} to set.
*/
public void setEndpointRegistry(RabbitListenerEndpointRegistry endpointRegistry) {
this.endpointRegistry = endpointRegistry;
}
/**
* Set the name of the {@link RabbitListenerContainerFactory} to use by default.
* If none is specified, "rabbitListenerContainerFactory" is assumed to be defined.
* @param containerFactoryBeanName the {@link RabbitListenerContainerFactory} bean name.
*/
public void setContainerFactoryBeanName(String containerFactoryBeanName) {
this.containerFactoryBeanName = containerFactoryBeanName;
}
/**
* Set the {@link MessageHandlerMethodFactory} to use to configure the message
* listener responsible to serve an endpoint detected by this processor.
*
By default, {@link DefaultMessageHandlerMethodFactory} is used and it
* can be configured further to support additional method arguments
* or to customize conversion and validation support. See
* {@link DefaultMessageHandlerMethodFactory} Javadoc for more details.
* @param messageHandlerMethodFactory the {@link MessageHandlerMethodFactory} instance.
*/
public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
}
/**
* Making a {@link BeanFactory} available is optional; if not set,
* {@link RabbitListenerConfigurer} beans won't get autodetected and an
* {@link #setEndpointRegistry endpoint registry} has to be explicitly configured.
* @param beanFactory the {@link BeanFactory} to be used.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
}
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
@Override
public void setEnvironment(Environment environment) {
String property = environment.getProperty(RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY, String.class);
if (property != null) {
this.emptyStringArguments.addAll(StringUtils.commaDelimitedListToSet(property));
}
}
@Override
public void afterSingletonsInstantiated() {
this.registrar.setBeanFactory(this.beanFactory);
if (this.beanFactory instanceof ListableBeanFactory) {
Map instances =
((ListableBeanFactory) this.beanFactory).getBeansOfType(RabbitListenerConfigurer.class);
for (RabbitListenerConfigurer configurer : instances.values()) {
configurer.configureRabbitListeners(this.registrar);
}
}
if (this.registrar.getEndpointRegistry() == null) {
if (this.endpointRegistry == null) {
Assert.state(this.beanFactory != null,
"BeanFactory must be set to find endpoint registry by bean name");
this.endpointRegistry = this.beanFactory.getBean(
RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
RabbitListenerEndpointRegistry.class);
}
this.registrar.setEndpointRegistry(this.endpointRegistry);
}
if (this.containerFactoryBeanName != null) {
this.registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
}
// Set the custom handler method factory once resolved by the configurer
MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
if (handlerMethodFactory != null) {
this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory);
}
// Actually register all listeners
this.registrar.afterPropertiesSet();
// clear the cache - prototype beans will be re-cached.
this.typeCache.clear();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Class targetClass = AopUtils.getTargetClass(bean);
TypeMetadata metadata = this.typeCache.get(targetClass);
if (metadata == null) {
metadata = buildMetadata(targetClass);
this.typeCache.putIfAbsent(targetClass, metadata);
}
for (ListenerMethod lm : metadata.listenerMethods) {
for (RabbitListener rabbitListener : lm.annotations) {
processAmqpListener(rabbitListener, lm.method, bean, beanName);
}
}
if (metadata.handlerMethods.length > 0) {
processMultiMethodListeners(metadata.classAnnotations, metadata.handlerMethods, bean, beanName);
}
return bean;
}
private TypeMetadata buildMetadata(Class targetClass) {
Collection classLevelListeners = findListenerAnnotations(targetClass);
final boolean hasClassLevelListeners = classLevelListeners.size() > 0;
final List methods = new ArrayList();
final List multiMethods = new ArrayList();
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Collection listenerAnnotations = findListenerAnnotations(method);
if (listenerAnnotations.size() > 0) {
methods.add(new ListenerMethod(method,
listenerAnnotations.toArray(new RabbitListener[listenerAnnotations.size()])));
}
if (hasClassLevelListeners) {
RabbitHandler rabbitHandler = AnnotationUtils.findAnnotation(method, RabbitHandler.class);
if (rabbitHandler != null) {
multiMethods.add(method);
}
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.isEmpty() && multiMethods.isEmpty()) {
return TypeMetadata.EMPTY;
}
return new TypeMetadata(
methods.toArray(new ListenerMethod[methods.size()]),
multiMethods.toArray(new Method[multiMethods.size()]),
classLevelListeners.toArray(new RabbitListener[classLevelListeners.size()]));
}
/*
* AnnotationUtils.getRepeatableAnnotations does not look at interfaces
*/
private Collection findListenerAnnotations(Class clazz) {
Set listeners = new HashSet();
RabbitListener ann = AnnotationUtils.findAnnotation(clazz, RabbitListener.class);
if (ann != null) {
listeners.add(ann);
}
RabbitListeners anns = AnnotationUtils.findAnnotation(clazz, RabbitListeners.class);
if (anns != null) {
Collections.addAll(listeners, anns.value());
}
return listeners;
}
/*
* AnnotationUtils.getRepeatableAnnotations does not look at interfaces
*/
private Collection findListenerAnnotations(Method method) {
Set listeners = new HashSet();
RabbitListener ann = AnnotationUtils.findAnnotation(method, RabbitListener.class);
if (ann != null) {
listeners.add(ann);
}
RabbitListeners anns = AnnotationUtils.findAnnotation(method, RabbitListeners.class);
if (anns != null) {
Collections.addAll(listeners, anns.value());
}
return listeners;
}
private void processMultiMethodListeners(RabbitListener[] classLevelListeners, Method[] multiMethods,
Object bean, String beanName) {
List checkedMethods = new ArrayList();
for (Method method : multiMethods) {
checkedMethods.add(checkProxy(method, bean));
}
for (RabbitListener classLevelListener : classLevelListeners) {
MultiMethodRabbitListenerEndpoint endpoint = new MultiMethodRabbitListenerEndpoint(checkedMethods, bean);
endpoint.setBeanFactory(this.beanFactory);
processListener(endpoint, classLevelListener, bean, bean.getClass(), beanName);
}
}
protected void processAmqpListener(RabbitListener rabbitListener, Method method, Object bean, String beanName) {
Method methodToUse = checkProxy(method, bean);
MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint();
endpoint.setMethod(methodToUse);
endpoint.setBeanFactory(this.beanFactory);
processListener(endpoint, rabbitListener, bean, methodToUse, beanName);
}
private Method checkProxy(Method method, Object bean) {
if (AopUtils.isJdkDynamicProxy(bean)) {
try {
// Found a @RabbitListener method on the target class for this JDK proxy ->
// is it also present on the proxy itself?
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
Class[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces();
for (Class iface : proxiedInterfaces) {
try {
method = iface.getMethod(method.getName(), method.getParameterTypes());
break;
}
catch (NoSuchMethodException noMethod) {
}
}
}
catch (SecurityException ex) {
ReflectionUtils.handleReflectionException(ex);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException(String.format(
"@RabbitListener method '%s' found on bean target class '%s', " +
"but not found in any interface(s) for bean JDK proxy. Either " +
"pull the method up to an interface or switch to subclass (CGLIB) " +
"proxies by setting proxy-target-class/proxyTargetClass " +
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
}
}
return method;
}
protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean,
Object adminTarget, String beanName) {
endpoint.setBean(bean);
endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
endpoint.setId(getEndpointId(rabbitListener));
endpoint.setQueueNames(resolveQueues(rabbitListener));
String group = rabbitListener.group();
if (StringUtils.hasText(group)) {
Object resolvedGroup = resolveExpression(group);
if (resolvedGroup instanceof String) {
endpoint.setGroup((String) resolvedGroup);
}
}
endpoint.setExclusive(rabbitListener.exclusive());
String priority = resolve(rabbitListener.priority());
if (StringUtils.hasText(priority)) {
try {
endpoint.setPriority(Integer.valueOf(priority));
}
catch (NumberFormatException ex) {
throw new BeanInitializationException("Invalid priority value for " +
rabbitListener + " (must be an integer)", ex);
}
}
String rabbitAdmin = resolve(rabbitListener.admin());
if (StringUtils.hasText(rabbitAdmin)) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve RabbitAdmin by bean name");
try {
endpoint.setAdmin(this.beanFactory.getBean(rabbitAdmin, RabbitAdmin.class));
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanInitializationException("Could not register rabbit listener endpoint on [" +
adminTarget + "], no " + RabbitAdmin.class.getSimpleName() + " with id '" +
rabbitAdmin + "' was found in the application context", ex);
}
}
RabbitListenerContainerFactory factory = null;
String containerFactoryBeanName = resolve(rabbitListener.containerFactory());
if (StringUtils.hasText(containerFactoryBeanName)) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name");
try {
factory = this.beanFactory.getBean(containerFactoryBeanName, RabbitListenerContainerFactory.class);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanInitializationException("Could not register rabbit listener endpoint on [" +
adminTarget + "] for bean " + beanName + ", no " +
RabbitListenerContainerFactory.class.getSimpleName() + " with id '" +
containerFactoryBeanName + "' was found in the application context", ex);
}
}
this.registrar.registerEndpoint(endpoint, factory);
}
private String getEndpointId(RabbitListener rabbitListener) {
if (StringUtils.hasText(rabbitListener.id())) {
return resolve(rabbitListener.id());
}
else {
return "io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#" + this.counter.getAndIncrement();
}
}
private String[] resolveQueues(RabbitListener rabbitListener) {
String[] queues = rabbitListener.queues();
QueueBinding[] bindings = rabbitListener.bindings();
if (queues.length > 0 && bindings.length > 0) {
throw new BeanInitializationException("@RabbitListener can have 'queues' or 'bindings' but not both");
}
List result = new ArrayList();
if (queues.length > 0) {
for (int i = 0; i < queues.length; i++) {
Object resolvedValue = resolveExpression(queues[i]);
resolveAsString(resolvedValue, result);
}
}
else {
return registerBeansForDeclaration(rabbitListener);
}
return result.toArray(new String[result.size()]);
}
@SuppressWarnings("unchecked")
private void resolveAsString(Object resolvedValue, List result) {
Object resolvedValueToUse = resolvedValue;
if (resolvedValue instanceof String[]) {
resolvedValueToUse = Arrays.asList((String[]) resolvedValue);
}
if (resolvedValueToUse instanceof Queue) {
result.add(((Queue) resolvedValueToUse).getName());
}
else if (resolvedValueToUse instanceof String) {
result.add((String) resolvedValueToUse);
}
else if (resolvedValueToUse instanceof Iterable) {
for (Object object : (Iterable