GH-3844: Rework messaging annotation with @Bean (#3877)
* GH-3844: Rework messaging annotation with @Bean Fixes https://github.com/spring-projects/spring-integration/issues/3844 * Make `MessagingAnnotationPostProcessor` as a `BeanDefinitionRegistryPostProcessor` to process bean definitions as early as possible and register respective messaging components at that early phase * Make bean definitions parsing logic optional for AOT and native mode since beans have bean parsed during AOT building phase * Introduce a `BeanDefinitionPropertiesMapper` for easier mapping of the annotation attributes to the target `BeanDefinition` * Remove `@Bean`-related logic from method parsing process * Change the logic for `@Bean`-based endpoint bean names: since we don't deal with methods on the bean definition phase, then method name does not make sense. It even may mislead if we `@Bean` name is based on a method by default, so we end up with duplicated word in the target endpoint bean name. Now we don't * Fix `configuration.adoc` respectively for a new endpoint bean name logic * In the end the new logic in the `AbstractMethodAnnotationPostProcessor` is similar to XML parsers: we feed annotation attributes to the `AbstractStandardMessageHandlerFactoryBean` impls * * Fix language in docs and exception message
This commit is contained in:
@@ -22,12 +22,9 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
@@ -36,7 +33,7 @@ import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.junit.BrokerRunning;
|
||||
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -59,7 +56,7 @@ import org.springframework.integration.support.AbstractIntegrationMessageBuilder
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
@@ -67,23 +64,24 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
* @since 5.0.1
|
||||
*
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
@RabbitAvailable({
|
||||
AmqpMessageSourceIntegrationTests.DSL_QUEUE,
|
||||
AmqpMessageSourceIntegrationTests.INTERCEPT_QUEUE,
|
||||
AmqpMessageSourceIntegrationTests.DLQ,
|
||||
AmqpMessageSourceIntegrationTests.NOAUTOACK_QUEUE })
|
||||
public class AmqpMessageSourceIntegrationTests {
|
||||
|
||||
private static final String DSL_QUEUE = "AmqpMessageSourceIntegrationTests";
|
||||
static final String DSL_QUEUE = "AmqpMessageSourceIntegrationTests";
|
||||
|
||||
private static final String QUEUE_WITH_DLQ = "AmqpMessageSourceIntegrationTests.withDLQ";
|
||||
static final String QUEUE_WITH_DLQ = "AmqpMessageSourceIntegrationTests.withDLQ";
|
||||
|
||||
private static final String DLQ = QUEUE_WITH_DLQ + ".dlq";
|
||||
static final String DLQ = QUEUE_WITH_DLQ + ".dlq";
|
||||
|
||||
private static final String INTERCEPT_QUEUE = "AmqpMessageSourceIntegrationTests.channel";
|
||||
static final String INTERCEPT_QUEUE = "AmqpMessageSourceIntegrationTests.channel";
|
||||
|
||||
private static final String NOAUTOACK_QUEUE = "AmqpMessageSourceIntegrationTests.noAutoAck";
|
||||
|
||||
@ClassRule
|
||||
public static BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(DSL_QUEUE, INTERCEPT_QUEUE, DLQ,
|
||||
NOAUTOACK_QUEUE);
|
||||
static final String NOAUTOACK_QUEUE = "AmqpMessageSourceIntegrationTests.noAutoAck";
|
||||
|
||||
@Autowired
|
||||
private Config config;
|
||||
@@ -91,7 +89,7 @@ public class AmqpMessageSourceIntegrationTests {
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
RabbitAdmin admin = new RabbitAdmin(this.config.connectionFactory());
|
||||
Queue queue = QueueBuilder.nonDurable(QUEUE_WITH_DLQ)
|
||||
@@ -103,16 +101,11 @@ public class AmqpMessageSourceIntegrationTests {
|
||||
this.context.start();
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void after() {
|
||||
this.context.stop();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
brokerRunning.removeTestQueues(QUEUE_WITH_DLQ);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImplicitNackThenAck() throws Exception {
|
||||
RabbitTemplate template = new RabbitTemplate(this.config.connectionFactory());
|
||||
@@ -256,7 +249,7 @@ public class AmqpMessageSourceIntegrationTests {
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return new CachingConnectionFactory(brokerRunning.getConnectionFactory());
|
||||
return new CachingConnectionFactory("localhost");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -95,8 +95,7 @@ public class IdempotentReceiverAutoProxyCreatorInitializer implements Integratio
|
||||
List<Map<String, String>> idempotentEndpointsMapping, String beanName, BeanDefinition beanDefinition)
|
||||
throws LinkageError {
|
||||
|
||||
if (beanDefinition.getSource() instanceof MethodMetadata) {
|
||||
MethodMetadata beanMethod = (MethodMetadata) beanDefinition.getSource();
|
||||
if (beanDefinition.getSource() instanceof MethodMetadata beanMethod) {
|
||||
String annotationType = IdempotentReceiver.class.getName();
|
||||
if (beanMethod.isAnnotated(annotationType)) { // NOSONAR never null
|
||||
Object value = beanMethod.getAnnotationAttributes(annotationType).get("value"); // NOSONAR
|
||||
@@ -120,13 +119,7 @@ public class IdempotentReceiverAutoProxyCreatorInitializer implements Integratio
|
||||
|
||||
String endpoint = beanName;
|
||||
if (!MessageHandler.class.isAssignableFrom(returnType)) {
|
||||
/*
|
||||
MessageHandler beans, populated from @Bean methods, have a complex id,
|
||||
including @Configuration bean name, method name and the Messaging annotation name.
|
||||
The following pattern matches the bean name, regardless of the annotation name.
|
||||
*/
|
||||
endpoint = beanDefinition.getFactoryBeanName() + "." + beanName +
|
||||
".*" + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
|
||||
endpoint = beanName + ".*" + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
|
||||
}
|
||||
|
||||
String[] interceptors = (String[]) value;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.config;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.JavaUtils;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.router.AbstractMappingMessageRouter;
|
||||
@@ -49,6 +50,10 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
private String defaultOutputChannelName;
|
||||
|
||||
private String prefix;
|
||||
|
||||
private String suffix;
|
||||
|
||||
private Boolean resolutionRequired;
|
||||
|
||||
private Boolean applySequence;
|
||||
@@ -63,6 +68,14 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
this.defaultOutputChannelName = defaultOutputChannelName;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public void setResolutionRequired(Boolean resolutionRequired) {
|
||||
this.resolutionRequired = resolutionRequired;
|
||||
}
|
||||
@@ -106,7 +119,7 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
|
||||
@Override
|
||||
protected MessageHandler createExpressionEvaluatingHandler(Expression expression) {
|
||||
return this.configureRouter(new ExpressionEvaluatingRouter(expression));
|
||||
return configureRouter(new ExpressionEvaluatingRouter(expression));
|
||||
}
|
||||
|
||||
protected AbstractMappingMessageRouter createMethodInvokingRouter(Object targetObject, String targetMethodName) {
|
||||
@@ -116,34 +129,25 @@ public class RouterFactoryBean extends AbstractStandardMessageHandlerFactoryBean
|
||||
}
|
||||
|
||||
protected AbstractMessageRouter configureRouter(AbstractMessageRouter router) {
|
||||
if (this.defaultOutputChannel != null) {
|
||||
router.setDefaultOutputChannel(this.defaultOutputChannel);
|
||||
}
|
||||
if (this.defaultOutputChannelName != null) {
|
||||
router.setDefaultOutputChannelName(this.defaultOutputChannelName);
|
||||
}
|
||||
if (getSendTimeout() != null) {
|
||||
router.setSendTimeout(getSendTimeout());
|
||||
}
|
||||
if (this.applySequence != null) {
|
||||
router.setApplySequence(this.applySequence);
|
||||
}
|
||||
if (this.ignoreSendFailures != null) {
|
||||
router.setIgnoreSendFailures(this.ignoreSendFailures);
|
||||
}
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfNotNull(this.defaultOutputChannel, router::setDefaultOutputChannel)
|
||||
.acceptIfNotNull(this.defaultOutputChannelName, router::setDefaultOutputChannelName)
|
||||
.acceptIfNotNull(getSendTimeout(), router::setSendTimeout)
|
||||
.acceptIfNotNull(this.applySequence, router::setApplySequence)
|
||||
.acceptIfNotNull(this.ignoreSendFailures, router::setIgnoreSendFailures);
|
||||
|
||||
if (router instanceof AbstractMappingMessageRouter) {
|
||||
this.configureMappingRouter((AbstractMappingMessageRouter) router);
|
||||
configureMappingRouter((AbstractMappingMessageRouter) router);
|
||||
}
|
||||
return router;
|
||||
}
|
||||
|
||||
protected void configureMappingRouter(AbstractMappingMessageRouter router) {
|
||||
if (this.channelMappings != null) {
|
||||
router.setChannelMappings(this.channelMappings);
|
||||
}
|
||||
if (this.resolutionRequired != null) {
|
||||
router.setResolutionRequired(this.resolutionRequired);
|
||||
}
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfNotNull(this.channelMappings, router::setChannelMappings)
|
||||
.acceptIfNotNull(this.resolutionRequired, router::setResolutionRequired)
|
||||
.acceptIfHasText(this.prefix, router::setPrefix)
|
||||
.acceptIfHasText(this.suffix, router::setSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,43 +20,63 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
|
||||
import org.springframework.aop.support.NameMatchMethodPointcut;
|
||||
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.integration.annotation.IdempotentReceiver;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.annotation.Reactive;
|
||||
import org.springframework.integration.annotation.Role;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.MessagePublishingErrorHandler;
|
||||
import org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
import org.springframework.integration.config.RouterFactoryBean;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.integration.context.Orderable;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
@@ -68,7 +88,6 @@ import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.handler.AbstractMessageProducingHandler;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.LambdaMessageProcessor;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.handler.ReactiveMessageHandlerAdapter;
|
||||
import org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper;
|
||||
import org.springframework.integration.handler.advice.HandleMessageAdvice;
|
||||
@@ -106,67 +125,300 @@ import reactor.core.publisher.Flux;
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation>
|
||||
implements MethodAnnotationPostProcessor<T> {
|
||||
implements MethodAnnotationPostProcessor<T>, BeanFactoryAware {
|
||||
|
||||
private static final String UNCHECKED = "unchecked";
|
||||
|
||||
private static final String INPUT_CHANNEL_ATTRIBUTE = "inputChannel";
|
||||
|
||||
private static final String ADVICE_CHAIN_ATTRIBUTE = "adviceChain";
|
||||
|
||||
protected static final String SEND_TIMEOUT_ATTRIBUTE = "sendTimeout";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); // NOSONAR
|
||||
|
||||
protected static final String ADVICE_CHAIN_ATTRIBUTE = "adviceChain"; // NOSONAR
|
||||
|
||||
protected static final String SEND_TIMEOUT_ATTRIBUTE = "sendTimeout"; // NOSONAR
|
||||
|
||||
protected final List<String> messageHandlerAttributes = new ArrayList<>(); // NOSONAR
|
||||
|
||||
protected final ConfigurableListableBeanFactory beanFactory; // NOSONAR
|
||||
|
||||
protected final BeanDefinitionRegistry definitionRegistry; // NOSONAR
|
||||
|
||||
protected final ConversionService conversionService; // NOSONAR
|
||||
|
||||
protected final DestinationResolver<MessageChannel> channelResolver; // NOSONAR
|
||||
|
||||
protected final Class<T> annotationType; // NOSONAR
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private BeanDefinitionRegistry definitionRegistry;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private DestinationResolver<MessageChannel> channelResolver;
|
||||
|
||||
@SuppressWarnings(UNCHECKED)
|
||||
public AbstractMethodAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "'beanFactory' must not be null");
|
||||
public AbstractMethodAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.add(SEND_TIMEOUT_ATTRIBUTE);
|
||||
this.beanFactory = beanFactory;
|
||||
this.annotationType =
|
||||
(Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), MethodAnnotationPostProcessor.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
this.definitionRegistry = (BeanDefinitionRegistry) beanFactory;
|
||||
this.conversionService = this.beanFactory.getConversionService() != null
|
||||
? this.beanFactory.getConversionService()
|
||||
: DefaultConversionService.getSharedInstance();
|
||||
this.channelResolver = ChannelResolverUtils.getChannelResolver(beanFactory);
|
||||
this.annotationType =
|
||||
(Class<T>) GenericTypeResolver.resolveTypeArgument(this.getClass(),
|
||||
MethodAnnotationPostProcessor.class);
|
||||
}
|
||||
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
protected BeanDefinitionRegistry getDefinitionRegistry() {
|
||||
return this.definitionRegistry;
|
||||
}
|
||||
|
||||
protected ConversionService getConversionService() {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
protected DestinationResolver<MessageChannel> getChannelResolver() {
|
||||
return this.channelResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
List<Annotation> annotations) {
|
||||
|
||||
ResolvableType handlerBeanType = getHandlerBeanClass(beanDefinition);
|
||||
|
||||
String handlerBeanName = beanName;
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
MergedAnnotations mergedAnnotations = beanDefinition.getFactoryMethodMetadata().getAnnotations();
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
if (handlerBeanDefinition != beanDefinition) {
|
||||
Class<?> handlerBeanClass =
|
||||
org.springframework.util.ClassUtils.resolveClassName(handlerBeanDefinition.getBeanClassName(),
|
||||
this.beanFactory.getBeanClassLoader());
|
||||
|
||||
if (isClassIn(handlerBeanClass, Orderable.class, AbstractSimpleMessageHandlerFactoryBean.class)) {
|
||||
mergedAnnotations.get(Order.class)
|
||||
.getValue(AnnotationUtils.VALUE, String.class).
|
||||
ifPresent((order) -> handlerBeanDefinition.getPropertyValues().add("order", order));
|
||||
}
|
||||
|
||||
if (isClassIn(handlerBeanClass, AbstractMessageProducingHandler.class, AbstractMessageRouter.class,
|
||||
AbstractSimpleMessageHandlerFactoryBean.class)) {
|
||||
|
||||
new BeanDefinitionPropertiesMapper(handlerBeanDefinition, annotations)
|
||||
.setPropertyValue(SEND_TIMEOUT_ATTRIBUTE)
|
||||
.setPropertyReference("outputChannel")
|
||||
.setPropertyReference("defaultOutputChannel");
|
||||
}
|
||||
|
||||
if (isClassIn(handlerBeanClass, AbstractMessageProducingHandler.class,
|
||||
AbstractSimpleMessageHandlerFactoryBean.class)
|
||||
&& !RouterFactoryBean.class.isAssignableFrom(handlerBeanClass)) {
|
||||
|
||||
applyAdviceChainIfAny(handlerBeanDefinition, annotations);
|
||||
}
|
||||
|
||||
handlerBeanName = generateHandlerBeanName(beanName, mergedAnnotations);
|
||||
this.definitionRegistry.registerBeanDefinition(handlerBeanName, handlerBeanDefinition);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new BeanDefinitionValidationException(
|
||||
"The messaging annotation processor for '" + this.annotationType +
|
||||
"' cannot create a target handler bean. " +
|
||||
"The bean definition with the problem is: " + beanName);
|
||||
}
|
||||
|
||||
BeanDefinition endpointBeanDefinition =
|
||||
createEndpointBeanDefinition(new BeanComponentDefinition(handlerBeanDefinition, handlerBeanName),
|
||||
new BeanComponentDefinition(beanDefinition, beanName), annotations);
|
||||
|
||||
new BeanDefinitionPropertiesMapper(endpointBeanDefinition, annotations)
|
||||
.setPropertyValue("autoStartup")
|
||||
.setPropertyValue("phase");
|
||||
|
||||
Poller poller = MessagingAnnotationUtils.resolveAttribute(annotations, "poller", Poller.class);
|
||||
Reactive reactive = MessagingAnnotationUtils.resolveAttribute(annotations, "reactive", Reactive.class);
|
||||
Assert.state(reactive == null || poller == null,
|
||||
() -> "The 'poller' and 'reactive' attributes are mutually exclusive." +
|
||||
"The bean definition with the problem is: " + beanName);
|
||||
if (poller != null) {
|
||||
applyPollerForEndpoint(endpointBeanDefinition, poller);
|
||||
}
|
||||
else if (reactive != null) {
|
||||
BeanMetadataElement reactiveCustomizer;
|
||||
String reactiveCustomizerBean = reactive.value();
|
||||
if (StringUtils.hasText(reactiveCustomizerBean)) {
|
||||
reactiveCustomizer = new RuntimeBeanReference(reactiveCustomizerBean);
|
||||
}
|
||||
else {
|
||||
reactiveCustomizer =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(Function.class)
|
||||
.setFactoryMethod("identity")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
endpointBeanDefinition.getPropertyValues().add("reactiveCustomizer", reactiveCustomizer);
|
||||
}
|
||||
|
||||
mergedAnnotations.get(Role.class)
|
||||
.getValue(AnnotationUtils.VALUE, String.class).
|
||||
ifPresent((role) -> endpointBeanDefinition.getPropertyValues().add("role", role));
|
||||
|
||||
String endpointBeanName =
|
||||
generateHandlerBeanName(beanName, mergedAnnotations)
|
||||
.replaceFirst("\\.(handler|source)$", "");
|
||||
this.definitionRegistry.registerBeanDefinition(endpointBeanName, endpointBeanDefinition);
|
||||
}
|
||||
|
||||
private static boolean isClassIn(Class<?> classToVerify, Class<?>... classesToMatch) {
|
||||
for (Class<?> toMatch : classesToMatch) {
|
||||
if (toMatch.isAssignableFrom(classToVerify)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected BeanDefinition createEndpointBeanDefinition(ComponentDefinition handlerBeanDefinition,
|
||||
ComponentDefinition beanDefinition, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinitionBuilder endpointDefinitionBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class)
|
||||
.addPropertyReference("handler", handlerBeanDefinition.getName());
|
||||
|
||||
String inputChannelAttribute = getInputChannelAttribute();
|
||||
String inputChannelName =
|
||||
MessagingAnnotationUtils.resolveAttribute(annotations, inputChannelAttribute, String.class);
|
||||
Assert.hasText(inputChannelName,
|
||||
() -> "The '" + inputChannelAttribute + "' attribute is required on '" + this.annotationType +
|
||||
"' on @Bean method");
|
||||
if (!this.definitionRegistry.containsBeanDefinition(inputChannelName)) {
|
||||
this.definitionRegistry.registerBeanDefinition(inputChannelName,
|
||||
new RootBeanDefinition(DirectChannel.class));
|
||||
}
|
||||
BeanDefinition endpointBeanDefinition =
|
||||
endpointDefinitionBuilder.addPropertyReference("inputChannel", inputChannelName)
|
||||
.getBeanDefinition();
|
||||
|
||||
applyAdviceChainIfAny(endpointBeanDefinition, annotations);
|
||||
|
||||
return endpointBeanDefinition;
|
||||
}
|
||||
|
||||
private static void applyAdviceChainIfAny(BeanDefinition beanDefinition, List<Annotation> annotations) {
|
||||
String[] adviceChainNames = MessagingAnnotationUtils.resolveAttribute(annotations, ADVICE_CHAIN_ATTRIBUTE,
|
||||
String[].class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(adviceChainNames)) {
|
||||
ManagedList<RuntimeBeanReference> adviceChain =
|
||||
Arrays.stream(adviceChainNames)
|
||||
.map(RuntimeBeanReference::new)
|
||||
.collect(Collectors.toCollection(ManagedList::new));
|
||||
|
||||
beanDefinition.getPropertyValues().addPropertyValue("adviceChain", adviceChain);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyPollerForEndpoint(BeanDefinition endpointBeanDefinition, Poller poller) {
|
||||
BeanMetadataElement pollerMetadataBeanDefinition;
|
||||
String ref = poller.value();
|
||||
if (StringUtils.hasText(ref)) {
|
||||
pollerMetadataBeanDefinition = new RuntimeBeanReference(ref);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder pollerBeanDefinitionBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(PollerMetadata.class);
|
||||
|
||||
new BeanDefinitionPropertiesMapper(pollerBeanDefinitionBuilder.getRawBeanDefinition(), List.of(poller))
|
||||
.setPropertyReference("trigger")
|
||||
.setPropertyReference("taskExecutor")
|
||||
.setPropertyValue("receiveTimeout")
|
||||
.setPropertyValue("maxMessagesPerPoll");
|
||||
|
||||
String errorChannel = poller.errorChannel();
|
||||
if (StringUtils.hasText(errorChannel)) {
|
||||
BeanDefinitionBuilder errorHandler =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(MessagePublishingErrorHandler.class)
|
||||
.addPropertyReference("defaultErrorChannel", errorChannel);
|
||||
pollerBeanDefinitionBuilder.addPropertyValue("errorHandler", errorHandler.getBeanDefinition());
|
||||
}
|
||||
|
||||
String fixedDelay = poller.fixedDelay();
|
||||
String fixedRate = poller.fixedRate();
|
||||
String cron = poller.cron();
|
||||
BeanDefinition triggerBeanDefinition = null;
|
||||
|
||||
if (StringUtils.hasText(cron)) {
|
||||
triggerBeanDefinition = triggerBeanDefinition(CronTrigger.class, cron);
|
||||
}
|
||||
else if (StringUtils.hasText(fixedDelay)) {
|
||||
triggerBeanDefinition = triggerBeanDefinition(PeriodicTrigger.class, fixedDelay);
|
||||
}
|
||||
else if (StringUtils.hasText(fixedRate)) {
|
||||
triggerBeanDefinition = triggerBeanDefinition(PeriodicTrigger.class, fixedRate);
|
||||
triggerBeanDefinition.getPropertyValues().addPropertyValue("fixedRate", true);
|
||||
}
|
||||
|
||||
if (triggerBeanDefinition != null) {
|
||||
pollerBeanDefinitionBuilder.addPropertyValue("trigger", triggerBeanDefinition);
|
||||
}
|
||||
|
||||
pollerMetadataBeanDefinition = pollerBeanDefinitionBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
endpointBeanDefinition.getPropertyValues()
|
||||
.addPropertyValue("pollerMetadata", pollerMetadataBeanDefinition);
|
||||
}
|
||||
|
||||
private static BeanDefinition triggerBeanDefinition(Class<? extends Trigger> triggerClass, String triggerValue) {
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(triggerClass)
|
||||
.addConstructorArgValue(triggerValue)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
private ResolvableType getHandlerBeanClass(AnnotatedBeanDefinition beanDefinition) {
|
||||
MethodMetadata factoryMethodMetadata = beanDefinition.getFactoryMethodMetadata();
|
||||
if (factoryMethodMetadata instanceof StandardMethodMetadata standardMethodMetadata) {
|
||||
return ResolvableType.forMethodReturnType(standardMethodMetadata.getIntrospectedMethod());
|
||||
}
|
||||
else {
|
||||
String typeName = factoryMethodMetadata.getReturnTypeName();
|
||||
Class<?> beanClass =
|
||||
org.springframework.util.ClassUtils.resolveClassName(typeName,
|
||||
this.beanFactory.getBeanClassLoader());
|
||||
return ResolvableType.forClass(beanClass);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
Class<?> classToCheck = handlerBeanType.toClass();
|
||||
|
||||
if (FactoryBean.class.isAssignableFrom(classToCheck)) {
|
||||
classToCheck = this.beanFactory.getType(beanName);
|
||||
}
|
||||
|
||||
if (isClassIn(classToCheck, AbstractReplyProducingMessageHandler.class, AbstractMessageRouter.class)) {
|
||||
checkMessageHandlerAttributes(beanName, annotations);
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcess(Object bean, String beanName, Method method, List<Annotation> annotations) {
|
||||
Object sourceHandler = null;
|
||||
if (beanAnnotationAware() && AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
if (!this.beanFactory.containsBeanDefinition(resolveTargetBeanName(method))) {
|
||||
this.logger.debug("Skipping endpoint creation; perhaps due to some '@Conditional' annotation.");
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
sourceHandler = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
}
|
||||
}
|
||||
|
||||
MessageHandler handler = createHandler(bean, method, annotations);
|
||||
|
||||
if (!(handler instanceof ReactiveMessageHandlerAdapter)) {
|
||||
orderable(method, handler);
|
||||
producerOrRouter(annotations, handler);
|
||||
|
||||
if (!handler.equals(sourceHandler)) {
|
||||
handler = registerHandlerBean(beanName, method, handler);
|
||||
}
|
||||
handler = registerHandlerBean(beanName, method, handler);
|
||||
|
||||
handler = annotated(method, handler);
|
||||
handler = adviceChain(beanName, annotations, handler);
|
||||
@@ -270,27 +522,6 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCreateEndpoint(Method method, List<Annotation> annotations) {
|
||||
String inputChannel = MessagingAnnotationUtils.resolveAttribute(annotations, getInputChannelAttribute(),
|
||||
String.class);
|
||||
boolean createEndpoint = StringUtils.hasText(inputChannel);
|
||||
if (!createEndpoint && beanAnnotationAware()) {
|
||||
boolean isBean = AnnotatedElementUtils.isAnnotated(method, Bean.class.getName());
|
||||
Assert.isTrue(!isBean, () -> "A channel name in '" + getInputChannelAttribute() + "' is required when " +
|
||||
this.annotationType + " is used on '@Bean' methods.");
|
||||
}
|
||||
return createEndpoint;
|
||||
}
|
||||
|
||||
protected String getInputChannelAttribute() {
|
||||
return INPUT_CHANNEL_ATTRIBUTE;
|
||||
}
|
||||
|
||||
protected boolean beanAnnotationAware() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<Advice> extractAdviceChain(String beanName, List<Annotation> annotations) {
|
||||
List<Advice> adviceChain = null;
|
||||
String[] adviceChainNames = MessagingAnnotationUtils.resolveAttribute(annotations, ADVICE_CHAIN_ATTRIBUTE,
|
||||
@@ -519,9 +750,19 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
}
|
||||
|
||||
protected String generateHandlerBeanName(String originalBeanName, Method method) {
|
||||
String name = MessagingAnnotationUtils.endpointIdValue(method);
|
||||
return generateHandlerBeanName(originalBeanName, MergedAnnotations.from(method), method.getName());
|
||||
}
|
||||
|
||||
protected String generateHandlerBeanName(String originalBeanName, MergedAnnotations mergedAnnotations) {
|
||||
return generateHandlerBeanName(originalBeanName, mergedAnnotations, null);
|
||||
}
|
||||
|
||||
protected String generateHandlerBeanName(String originalBeanName, MergedAnnotations mergedAnnotations,
|
||||
@Nullable String methodName) {
|
||||
|
||||
String name = MessagingAnnotationUtils.endpointIdValue(mergedAnnotations);
|
||||
if (!StringUtils.hasText(name)) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "."
|
||||
String baseName = originalBeanName + (methodName != null ? "." + methodName : "") + "."
|
||||
+ org.springframework.util.ClassUtils.getShortNameAsProperty(this.annotationType);
|
||||
name = baseName;
|
||||
int count = 1;
|
||||
@@ -532,47 +773,13 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
return name + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
|
||||
}
|
||||
|
||||
protected void setOutputChannelIfPresent(List<Annotation> annotations,
|
||||
AbstractReplyProducingMessageHandler handler) {
|
||||
String outputChannelName = MessagingAnnotationUtils.resolveAttribute(annotations, "outputChannel",
|
||||
String.class);
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
handler.setOutputChannelName(outputChannelName);
|
||||
}
|
||||
}
|
||||
protected static void setOutputChannelIfPresent(List<Annotation> annotations,
|
||||
AbstractMessageProducingHandler handler) {
|
||||
|
||||
protected Object resolveTargetBeanFromMethodWithBeanAnnotation(Method method) {
|
||||
String id = resolveTargetBeanName(method);
|
||||
return this.beanFactory.getBean(id);
|
||||
}
|
||||
|
||||
protected String resolveTargetBeanName(Method method) {
|
||||
String id = method.getName();
|
||||
String[] names = AnnotationUtils.getAnnotation(method, Bean.class).name(); // NOSONAR never null
|
||||
if (!ObjectUtils.isEmpty(names)) {
|
||||
id = names[0];
|
||||
String outputChannel = MessagingAnnotationUtils.resolveAttribute(annotations, "outputChannel", String.class);
|
||||
if (StringUtils.hasText(outputChannel)) {
|
||||
handler.setOutputChannelName(outputChannel);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@SuppressWarnings(UNCHECKED)
|
||||
protected <H> H extractTypeIfPossible(@Nullable Object targetObject, Class<H> expectedType) {
|
||||
if (targetObject == null) {
|
||||
return null;
|
||||
}
|
||||
if (expectedType.isAssignableFrom(targetObject.getClass())) {
|
||||
return (H) targetObject;
|
||||
}
|
||||
if (targetObject instanceof Advised) {
|
||||
TargetSource targetSource = ((Advised) targetObject).getTargetSource();
|
||||
try {
|
||||
return extractTypeIfPossible(targetSource.getTarget(), expectedType);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void checkMessageHandlerAttributes(String handlerBeanName, List<Annotation> annotations) {
|
||||
@@ -596,18 +803,23 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
return Boolean.parseBoolean(this.beanFactory.resolveEmbeddedValue(attribute));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MessageProcessor<?> buildLambdaMessageProcessorForBeanMethod(Method method, Object target) {
|
||||
if ((target instanceof Function || target instanceof Consumer) && ClassUtils.isLambda(target.getClass())
|
||||
|| ClassUtils.isKotlinFunction1(target.getClass())) {
|
||||
protected static BeanDefinition buildLambdaMessageProcessor(ResolvableType beanType,
|
||||
AnnotatedBeanDefinition beanDefinition) {
|
||||
|
||||
ResolvableType methodReturnType = ResolvableType.forMethodReturnType(method);
|
||||
Class<?> expectedPayloadType = methodReturnType.getGeneric(0).toClass();
|
||||
return new LambdaMessageProcessor(target, expectedPayloadType);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
Class<?> beanClass = beanType.toClass();
|
||||
|
||||
if (Function.class.isAssignableFrom(beanClass)
|
||||
|| Consumer.class.isAssignableFrom(beanClass)
|
||||
|| ClassUtils.isKotlinFunction1(beanClass)) {
|
||||
|
||||
Class<?> expectedPayloadType = beanType.getGeneric(0).toClass();
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(LambdaMessageProcessor.class)
|
||||
.addConstructorArgValue(beanDefinition)
|
||||
.addConstructorArgValue(expectedPayloadType)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void orderable(Method method, MessageHandler handler) {
|
||||
@@ -628,4 +840,33 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
*/
|
||||
protected abstract MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations);
|
||||
|
||||
protected record BeanDefinitionPropertiesMapper(BeanDefinition beanDefinition, List<Annotation> annotations) {
|
||||
|
||||
public BeanDefinitionPropertiesMapper setPropertyValue(String property) {
|
||||
return setPropertyValue(property, null);
|
||||
}
|
||||
|
||||
public BeanDefinitionPropertiesMapper setPropertyValue(String property, @Nullable String propertyName) {
|
||||
String value = MessagingAnnotationUtils.resolveAttribute(this.annotations, property, String.class);
|
||||
if (StringUtils.hasText(value)) {
|
||||
this.beanDefinition.getPropertyValues().add(propertyName != null ? propertyName : property, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public BeanDefinitionPropertiesMapper setPropertyReference(String property) {
|
||||
return setPropertyReference(property, null);
|
||||
}
|
||||
|
||||
public BeanDefinitionPropertiesMapper setPropertyReference(String property, @Nullable String propertyName) {
|
||||
String value = MessagingAnnotationUtils.resolveAttribute(this.annotations, property, String.class);
|
||||
if (StringUtils.hasText(value)) {
|
||||
this.beanDefinition.getPropertyValues()
|
||||
.add(propertyName != null ? propertyName : property, new RuntimeBeanReference(value));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -20,7 +20,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.integration.aggregator.AggregatingMessageHandler;
|
||||
import org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.MethodInvokingMessageGroupProcessor;
|
||||
@@ -43,11 +42,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class AggregatorAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Aggregator> {
|
||||
|
||||
public AggregatorAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
MethodInvokingMessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(bean, method);
|
||||
@@ -86,7 +80,8 @@ public class AggregatorAnnotationPostProcessor extends AbstractMethodAnnotationP
|
||||
return handler;
|
||||
}
|
||||
|
||||
protected boolean beanAnnotationAware() {
|
||||
@Override
|
||||
public boolean beanAnnotationAware() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -20,18 +20,17 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
import org.springframework.integration.handler.BridgeHandler;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Post-processor for the {@link BridgeFrom @BridgeFrom} annotation.
|
||||
@@ -42,46 +41,37 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class BridgeFromAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<BridgeFrom> {
|
||||
|
||||
public BridgeFromAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCreateEndpoint(Method method, List<Annotation> annotations) {
|
||||
boolean isBean = AnnotatedElementUtils.isAnnotated(method, Bean.class.getName());
|
||||
Assert.isTrue(isBean, "'@BridgeFrom' is eligible only for '@Bean' methods");
|
||||
|
||||
boolean isMessageChannelBean = MessageChannel.class.isAssignableFrom(method.getReturnType());
|
||||
Assert.isTrue(isMessageChannelBean, "'@BridgeFrom' is eligible only for 'MessageChannel' '@Bean' methods");
|
||||
|
||||
String channel = MessagingAnnotationUtils.resolveAttribute(annotations, "value", String.class);
|
||||
Assert.isTrue(StringUtils.hasText(channel), "'@BridgeFrom.value()' (inputChannelName) must not be empty");
|
||||
|
||||
boolean hasBridgeTo = AnnotatedElementUtils.isAnnotated(method, BridgeTo.class.getName());
|
||||
|
||||
Assert.isTrue(!hasBridgeTo, "'@BridgeFrom' and '@BridgeTo' are mutually exclusive 'MessageChannel' " +
|
||||
"'@Bean' method annotations");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttribute() {
|
||||
public String getInputChannelAttribute() {
|
||||
return AnnotationUtils.VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
String outputChannelName = resolveTargetBeanName(method);
|
||||
handler.setOutputChannelName(outputChannelName);
|
||||
return handler;
|
||||
public boolean supportsPojoMethod() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveTargetBeanFromMethodWithBeanAnnotation(Method method) {
|
||||
return null;
|
||||
public boolean shouldCreateEndpoint(MergedAnnotations mergedAnnotations, List<Annotation> annotations) {
|
||||
Assert.isTrue(super.shouldCreateEndpoint(mergedAnnotations, annotations),
|
||||
"'@BridgeFrom.value()' (inputChannelName) must not be empty");
|
||||
Assert.isTrue(!mergedAnnotations.isPresent(BridgeTo.class),
|
||||
"'@BridgeFrom' and '@BridgeTo' are mutually exclusive 'MessageChannel' '@Bean' method annotations");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotationChain) {
|
||||
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(BridgeHandler.class)
|
||||
.addPropertyReference("outputChannel", beanName)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2019 the original author or authors.
|
||||
* Copyright 2014-2022 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.
|
||||
@@ -20,15 +20,20 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.handler.BridgeHandler;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -37,45 +42,55 @@ import org.springframework.util.StringUtils;
|
||||
* Post-processor for the {@link BridgeTo @BridgeTo} annotation.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class BridgeToAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<BridgeTo> {
|
||||
|
||||
public BridgeToAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
@Override
|
||||
public boolean supportsPojoMethod() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCreateEndpoint(Method method, List<Annotation> annotations) {
|
||||
boolean isBean = AnnotatedElementUtils.isAnnotated(method, Bean.class.getName());
|
||||
Assert.isTrue(isBean, "'@BridgeTo' is eligible only for '@Bean' methods");
|
||||
|
||||
boolean isMessageChannelBean = MessageChannel.class.isAssignableFrom(method.getReturnType());
|
||||
Assert.isTrue(isMessageChannelBean, "'@BridgeTo' is eligible only for 'MessageChannel' '@Bean' methods");
|
||||
|
||||
boolean hasBridgeFrom = AnnotatedElementUtils.isAnnotated(method, BridgeFrom.class.getName());
|
||||
|
||||
Assert.isTrue(!hasBridgeFrom, "'@BridgeFrom' and '@BridgeTo' are mutually exclusive 'MessageChannel' " +
|
||||
"'@Bean' method annotations");
|
||||
|
||||
public boolean shouldCreateEndpoint(MergedAnnotations mergedAnnotations, List<Annotation> annotations) {
|
||||
Assert.isTrue(!mergedAnnotations.isPresent(BridgeFrom.class),
|
||||
"'@BridgeFrom' and '@BridgeTo' are mutually exclusive 'MessageChannel' '@Bean' method annotations");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
String outputChannelName = MessagingAnnotationUtils.resolveAttribute(annotations, "value", String.class);
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotationChain) {
|
||||
|
||||
GenericBeanDefinition bridgeHandlerBeanDefinition = new GenericBeanDefinition();
|
||||
bridgeHandlerBeanDefinition.setBeanClass(BridgeHandler.class);
|
||||
String outputChannelName = MessagingAnnotationUtils.resolveAttribute(annotationChain, "value", String.class);
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
handler.setOutputChannelName(outputChannelName);
|
||||
bridgeHandlerBeanDefinition.getPropertyValues()
|
||||
.addPropertyValue("outputChannel", new RuntimeBeanReference(outputChannelName));
|
||||
}
|
||||
return handler;
|
||||
return bridgeHandlerBeanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition createEndpointBeanDefinition(ComponentDefinition handlerBeanDefinition,
|
||||
ComponentDefinition beanDefinition, List<Annotation> annotations) {
|
||||
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class)
|
||||
.addPropertyReference("handler", handlerBeanDefinition.getName())
|
||||
.addPropertyReference("inputChannel", beanDefinition.getName())
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractEndpoint createEndpoint(MessageHandler handler, Method method, List<Annotation> annotations) {
|
||||
Object inputChannel = this.resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
Assert.isInstanceOf(MessageChannel.class, inputChannel);
|
||||
return doCreateEndpoint(handler, (MessageChannel) inputChannel, annotations);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,10 +21,14 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.config.FilterFactoryBean;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.integration.filter.MethodInvokingSelector;
|
||||
@@ -43,34 +47,45 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class FilterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Filter> {
|
||||
|
||||
public FilterAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
public FilterAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.addAll(Arrays.asList("discardChannel", "throwExceptionOnRejection",
|
||||
"adviceChain", "discardWithinAdvice"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
super.resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
|
||||
BeanMetadataElement targetObjectBeanDefinition = buildLambdaMessageProcessor(handlerBeanType, beanDefinition);
|
||||
if (targetObjectBeanDefinition == null) {
|
||||
targetObjectBeanDefinition = new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
BeanDefinition filterBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(FilterFactoryBean.class)
|
||||
.addPropertyValue("targetObject", targetObjectBeanDefinition)
|
||||
.getBeanDefinition();
|
||||
|
||||
new BeanDefinitionPropertiesMapper(filterBeanDefinition, annotations)
|
||||
.setPropertyValue("discardWithinAdvice")
|
||||
.setPropertyValue("throwExceptionOnRejection")
|
||||
.setPropertyReference("discardChannel");
|
||||
|
||||
return filterBeanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
MessageSelector selector;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
if (target instanceof MessageSelector) {
|
||||
selector = (MessageSelector) target;
|
||||
}
|
||||
else if (extractTypeIfPossible(target, MessageFilter.class) != null) {
|
||||
checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
|
||||
return (MessageHandler) target;
|
||||
}
|
||||
else {
|
||||
selector = new MethodInvokingSelector(target);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType()),
|
||||
"The Filter annotation may only be applied to methods with a boolean return type.");
|
||||
selector = new MethodInvokingSelector(bean, method);
|
||||
}
|
||||
Assert.isTrue(boolean.class.equals(method.getReturnType()) || Boolean.class.equals(method.getReturnType()),
|
||||
"The Filter annotation may only be applied to methods with a boolean return type.");
|
||||
MessageSelector selector = new MethodInvokingSelector(bean, method);
|
||||
|
||||
MessageFilter filter = new MessageFilter(selector);
|
||||
|
||||
|
||||
@@ -21,20 +21,24 @@ import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.endpoint.MethodInvokingMessageSource;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
import org.springframework.integration.util.ClassUtils;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -51,14 +55,55 @@ import org.springframework.util.Assert;
|
||||
public class InboundChannelAdapterAnnotationPostProcessor extends
|
||||
AbstractMethodAnnotationPostProcessor<InboundChannelAdapter> {
|
||||
|
||||
|
||||
public InboundChannelAdapterAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
@Override
|
||||
public String getInputChannelAttribute() {
|
||||
return "value";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttribute() {
|
||||
return AnnotationUtils.VALUE;
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotationChain) {
|
||||
|
||||
Class<?> handlerBeanClass = handlerBeanType.toClass();
|
||||
|
||||
if (MessageSource.class.isAssignableFrom(handlerBeanClass)) {
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
Method method = null;
|
||||
if (Supplier.class.isAssignableFrom(handlerBeanClass)) {
|
||||
method = ClassUtils.SUPPLIER_GET_METHOD;
|
||||
}
|
||||
else if (ClassUtils.isKotlinFunction0(handlerBeanClass)) {
|
||||
method = ClassUtils.KOTLIN_FUNCTION_0_INVOKE_METHOD;
|
||||
}
|
||||
|
||||
if (method != null) {
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingMessageSource.class)
|
||||
.addPropertyReference("object", beanName)
|
||||
.addPropertyValue("method", method)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
"The '" + this.annotationType + "' on @Bean method level is allowed only for: " +
|
||||
MessageSource.class.getName() + ", or " + Supplier.class.getName() +
|
||||
(ClassUtils.KOTLIN_FUNCTION_0_CLASS != null
|
||||
? ", or " + ClassUtils.KOTLIN_FUNCTION_0_CLASS.getName()
|
||||
: "") + " beans");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition createEndpointBeanDefinition(ComponentDefinition handlerBeanDefinition,
|
||||
ComponentDefinition beanDefinition, List<Annotation> annotations) {
|
||||
|
||||
String channelName = MessagingAnnotationUtils.resolveAttribute(annotations, AnnotationUtils.VALUE, String.class);
|
||||
Assert.hasText(channelName, "The channel ('value' attribute of @InboundChannelAdapter) can't be empty.");
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(SourcePollingChannelAdapterFactoryBean.class)
|
||||
.addPropertyValue("outputChannelName", channelName)
|
||||
.addPropertyReference("source", handlerBeanDefinition.getName())
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,18 +112,7 @@ public class InboundChannelAdapterAnnotationPostProcessor extends
|
||||
MessagingAnnotationUtils.resolveAttribute(annotations, AnnotationUtils.VALUE, String.class);
|
||||
Assert.hasText(channelName, "The channel ('value' attribute of @InboundChannelAdapter) can't be empty.");
|
||||
|
||||
MessageSource<?> messageSource;
|
||||
try {
|
||||
messageSource = createMessageSource(bean, beanName, method);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Skipping endpoint creation; "
|
||||
+ e.getMessage()
|
||||
+ "; perhaps due to some '@Conditional' annotation.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
MessageSource<?> messageSource = createMessageSource(bean, beanName, method);
|
||||
|
||||
SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter();
|
||||
adapter.setOutputChannelName(channelName);
|
||||
@@ -89,51 +123,21 @@ public class InboundChannelAdapterAnnotationPostProcessor extends
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private MessageSource<?> createMessageSource(Object beanArg, String beanNameArg, Method methodArg) {
|
||||
MessageSource<?> messageSource = null;
|
||||
Object bean = beanArg;
|
||||
Method method = methodArg;
|
||||
String beanName = beanNameArg;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
Class<?> targetClass = target.getClass();
|
||||
Assert.isTrue(MessageSource.class.isAssignableFrom(targetClass) ||
|
||||
Supplier.class.isAssignableFrom(targetClass) ||
|
||||
ClassUtils.isKotlinFunction0(targetClass),
|
||||
() -> "The '" + this.annotationType + "' on @Bean method " + "level is allowed only for: " +
|
||||
MessageSource.class.getName() + ", or " + Supplier.class.getName() +
|
||||
(ClassUtils.KOTLIN_FUNCTION_0_CLASS != null
|
||||
? ", or " + ClassUtils.KOTLIN_FUNCTION_0_CLASS.getName()
|
||||
: "") + " beans");
|
||||
if (target instanceof MessageSource<?>) {
|
||||
messageSource = (MessageSource<?>) target;
|
||||
}
|
||||
else if (target instanceof Supplier<?>) {
|
||||
method = ClassUtils.SUPPLIER_GET_METHOD;
|
||||
bean = target;
|
||||
beanName += '.' + methodArg.getName();
|
||||
}
|
||||
else if (ClassUtils.KOTLIN_FUNCTION_0_INVOKE_METHOD != null) {
|
||||
method = ClassUtils.KOTLIN_FUNCTION_0_INVOKE_METHOD;
|
||||
bean = target;
|
||||
beanName += '.' + methodArg.getName();
|
||||
}
|
||||
}
|
||||
if (messageSource == null) {
|
||||
MethodInvokingMessageSource methodInvokingMessageSource = new MethodInvokingMessageSource();
|
||||
methodInvokingMessageSource.setObject(bean);
|
||||
methodInvokingMessageSource.setMethod(method);
|
||||
String messageSourceBeanName = generateHandlerBeanName(beanName, method);
|
||||
this.definitionRegistry.registerBeanDefinition(messageSourceBeanName,
|
||||
new RootBeanDefinition(MethodInvokingMessageSource.class, () -> methodInvokingMessageSource));
|
||||
messageSource = this.beanFactory.getBean(messageSourceBeanName, MessageSource.class);
|
||||
}
|
||||
return messageSource;
|
||||
private MessageSource<?> createMessageSource(Object bean, String beanName, Method method) {
|
||||
MethodInvokingMessageSource methodInvokingMessageSource = new MethodInvokingMessageSource();
|
||||
methodInvokingMessageSource.setObject(bean);
|
||||
methodInvokingMessageSource.setMethod(method);
|
||||
String messageSourceBeanName = generateHandlerBeanName(beanName, method);
|
||||
getDefinitionRegistry().registerBeanDefinition(messageSourceBeanName,
|
||||
new RootBeanDefinition(MethodInvokingMessageSource.class, () -> methodInvokingMessageSource));
|
||||
return getBeanFactory().getBean(messageSourceBeanName, MessageSource.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String generateHandlerBeanName(String originalBeanName, Method method) {
|
||||
return super.generateHandlerBeanName(originalBeanName, method)
|
||||
protected String generateHandlerBeanName(String originalBeanName, MergedAnnotations mergedAnnotations,
|
||||
@Nullable String methodName) {
|
||||
|
||||
return super.generateHandlerBeanName(originalBeanName, mergedAnnotations, methodName)
|
||||
.replaceFirst(IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX + '$', ".source");
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -32,18 +30,25 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aot.AotDetector;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
@@ -73,50 +78,135 @@ import org.springframework.util.StringUtils;
|
||||
* @author Gary Russell
|
||||
* @author Rick Hogge
|
||||
*/
|
||||
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
|
||||
SmartInitializingSingleton {
|
||||
public class MessagingAnnotationPostProcessor
|
||||
implements BeanDefinitionRegistryPostProcessor, BeanPostProcessor, SmartInitializingSingleton {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(this.getClass()); // NOSONAR
|
||||
|
||||
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors = new HashMap<>();
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private final Set<Class<?>> noAnnotationsCache = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
|
||||
|
||||
private final List<Runnable> methodsToPostProcessAfterContextInitialization = new ArrayList<>();
|
||||
|
||||
private BeanDefinitionRegistry registry;
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.isAssignable(ConfigurableListableBeanFactory.class, beanFactory.getClass(),
|
||||
"a ConfigurableListableBeanFactory is required");
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
this.registry = registry;
|
||||
this.postProcessors.put(Filter.class, new FilterAnnotationPostProcessor());
|
||||
this.postProcessors.put(Router.class, new RouterAnnotationPostProcessor());
|
||||
this.postProcessors.put(Transformer.class, new TransformerAnnotationPostProcessor());
|
||||
this.postProcessors.put(ServiceActivator.class, new ServiceActivatorAnnotationPostProcessor());
|
||||
this.postProcessors.put(Splitter.class, new SplitterAnnotationPostProcessor());
|
||||
this.postProcessors.put(Aggregator.class, new AggregatorAnnotationPostProcessor());
|
||||
this.postProcessors.put(InboundChannelAdapter.class, new InboundChannelAdapterAnnotationPostProcessor());
|
||||
this.postProcessors.put(BridgeFrom.class, new BridgeFromAnnotationPostProcessor());
|
||||
this.postProcessors.put(BridgeTo.class, new BridgeToAnnotationPostProcessor());
|
||||
Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> customPostProcessors =
|
||||
setupCustomPostProcessors();
|
||||
if (!CollectionUtils.isEmpty(customPostProcessors)) {
|
||||
this.postProcessors.putAll(customPostProcessors);
|
||||
}
|
||||
this.postProcessors.values().stream()
|
||||
.filter(BeanFactoryAware.class::isInstance)
|
||||
.map(BeanFactoryAware.class::cast)
|
||||
.forEach((processor) -> processor.setBeanFactory((BeanFactory) this.registry));
|
||||
|
||||
if (!AotDetector.useGeneratedArtifacts()) {
|
||||
String[] beanNames = registry.getBeanDefinitionNames();
|
||||
|
||||
for (String beanName : beanNames) {
|
||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||
if (beanDef instanceof AnnotatedBeanDefinition annotatedBeanDefinition
|
||||
&& annotatedBeanDefinition.getFactoryMethodMetadata() != null) {
|
||||
|
||||
processCandidate(beanName, annotatedBeanDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processCandidate(String beanName, AnnotatedBeanDefinition beanDefinition) {
|
||||
MethodMetadata methodMetadata = beanDefinition.getFactoryMethodMetadata();
|
||||
MergedAnnotations annotations = methodMetadata.getAnnotations();
|
||||
if (methodMetadata instanceof StandardMethodMetadata standardMethodMetadata) {
|
||||
annotations = MergedAnnotations.from(standardMethodMetadata.getIntrospectedMethod());
|
||||
}
|
||||
|
||||
List<MessagingMetaAnnotation> messagingAnnotations = obtainMessagingAnnotations(annotations, beanName);
|
||||
|
||||
for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) {
|
||||
Class<? extends Annotation> annotationType = messagingAnnotation.annotationType;
|
||||
List<Annotation> annotationChain =
|
||||
MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation, annotationType);
|
||||
|
||||
processMessagingAnnotationOnBean(beanName, beanDefinition, annotationType, annotationChain);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MessagingMetaAnnotation> obtainMessagingAnnotations(MergedAnnotations annotations, String identified) {
|
||||
List<MessagingMetaAnnotation> messagingAnnotations = new ArrayList<>();
|
||||
|
||||
for (Class<? extends Annotation> annotationType : this.postProcessors.keySet()) {
|
||||
annotations.stream()
|
||||
.filter((ann) -> ann.getType().equals(annotationType))
|
||||
.map(MergedAnnotation::getRoot)
|
||||
.map(MergedAnnotation::synthesize)
|
||||
.map((ann) -> new MessagingMetaAnnotation(ann, annotationType))
|
||||
.forEach(messagingAnnotations::add);
|
||||
}
|
||||
|
||||
if (annotations.get(EndpointId.class, (ann) -> ann.hasNonDefaultValue("value")).isPresent()
|
||||
&& messagingAnnotations.size() > 1) {
|
||||
|
||||
throw new IllegalStateException("@EndpointId on " + identified
|
||||
+ " can only have one EIP annotation, found: " + messagingAnnotations.size());
|
||||
}
|
||||
|
||||
return messagingAnnotations;
|
||||
}
|
||||
|
||||
private void processMessagingAnnotationOnBean(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
Class<? extends Annotation> annotationType, List<Annotation> annotationChain) {
|
||||
|
||||
MethodAnnotationPostProcessor<?> messagingAnnotationProcessor = this.postProcessors.get(annotationType);
|
||||
if (messagingAnnotationProcessor != null) {
|
||||
if (messagingAnnotationProcessor.beanAnnotationAware()) {
|
||||
if (messagingAnnotationProcessor.shouldCreateEndpoint(
|
||||
beanDefinition.getFactoryMethodMetadata().getAnnotations(), annotationChain)) {
|
||||
|
||||
messagingAnnotationProcessor.processBeanDefinition(beanName, beanDefinition, annotationChain);
|
||||
}
|
||||
else {
|
||||
throw new BeanDefinitionValidationException(
|
||||
"The input channel for endpoint on '@Bean' method must be set for the "
|
||||
+ annotationType + ". The bean definition with the problem is: " + beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new BeanDefinitionValidationException(
|
||||
"The messaging annotation '" + annotationType + "' cannot be declared on '@Bean'. " +
|
||||
"The bean definition with the problem is: " + beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
this.postProcessors.put(Filter.class, new FilterAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(Router.class, new RouterAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(Transformer.class, new TransformerAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(ServiceActivator.class, new ServiceActivatorAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(Splitter.class, new SplitterAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(Aggregator.class, new AggregatorAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(InboundChannelAdapter.class,
|
||||
new InboundChannelAdapterAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(BridgeFrom.class, new BridgeFromAnnotationPostProcessor(this.beanFactory));
|
||||
this.postProcessors.put(BridgeTo.class, new BridgeToAnnotationPostProcessor(this.beanFactory));
|
||||
Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> customPostProcessors =
|
||||
setupCustomPostProcessors();
|
||||
if (!CollectionUtils.isEmpty(customPostProcessors)) {
|
||||
this.postProcessors.putAll(customPostProcessors);
|
||||
}
|
||||
protected BeanDefinitionRegistry getBeanDefinitionRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
protected Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> setupCustomPostProcessors() {
|
||||
@@ -160,34 +250,23 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
}
|
||||
|
||||
private void doWithMethod(Method method, Object bean, String beanName, Class<?> beanClass) {
|
||||
MergedAnnotations mergedAnnotations =
|
||||
MergedAnnotations.search(MergedAnnotations.SearchStrategy.DIRECT)
|
||||
.from(method);
|
||||
MergedAnnotations mergedAnnotations = MergedAnnotations.from(method);
|
||||
|
||||
List<MessagingMetaAnnotation> messagingAnnotations = new ArrayList<>();
|
||||
boolean noMessagingAnnotations = true;
|
||||
|
||||
for (Class<? extends Annotation> annotationType : this.postProcessors.keySet()) {
|
||||
mergedAnnotations.stream()
|
||||
.filter((ann) -> ann.getType().equals(annotationType))
|
||||
.map(MergedAnnotation::getRoot)
|
||||
.map(MergedAnnotation::synthesize)
|
||||
.map((ann) -> new MessagingMetaAnnotation(ann, annotationType))
|
||||
.forEach(messagingAnnotations::add);
|
||||
if (!mergedAnnotations.isPresent(Bean.class)) { // See postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)
|
||||
List<MessagingMetaAnnotation> messagingAnnotations =
|
||||
obtainMessagingAnnotations(mergedAnnotations, method.toGenericString());
|
||||
|
||||
for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) {
|
||||
noMessagingAnnotations = false;
|
||||
Class<? extends Annotation> annotationType = messagingAnnotation.annotationType;
|
||||
List<Annotation> annotationChain =
|
||||
MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation, annotationType);
|
||||
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotationChain);
|
||||
}
|
||||
}
|
||||
if (mergedAnnotations.get(EndpointId.class, (ann) -> ann.hasNonDefaultValue("value")).isPresent()
|
||||
&& messagingAnnotations.size() > 1) {
|
||||
|
||||
throw new IllegalStateException("@EndpointId on " + method.toGenericString()
|
||||
+ " can only have one EIP annotation, found: " + messagingAnnotations.size());
|
||||
}
|
||||
|
||||
for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) {
|
||||
Class<? extends Annotation> annotationType = messagingAnnotation.messagingAnnotationType;
|
||||
List<Annotation> annotationChain = getAnnotationChain(messagingAnnotation.annotation, annotationType);
|
||||
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotationChain);
|
||||
}
|
||||
|
||||
if (messagingAnnotations.size() == 0) {
|
||||
if (noMessagingAnnotations) {
|
||||
this.noAnnotationsCache.add(beanClass);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +276,9 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
|
||||
MethodAnnotationPostProcessor<?> postProcessor =
|
||||
MessagingAnnotationPostProcessor.this.postProcessors.get(annotationType);
|
||||
if (postProcessor != null && postProcessor.shouldCreateEndpoint(method, annotations)) {
|
||||
if (postProcessor != null && postProcessor.supportsPojoMethod()
|
||||
&& postProcessor.shouldCreateEndpoint(method, annotations)) {
|
||||
|
||||
Method targetMethod = method;
|
||||
if (AopUtils.isJdkDynamicProxy(bean)) {
|
||||
try {
|
||||
@@ -230,7 +311,6 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
|
||||
Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations);
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
BeanDefinitionRegistry definitionRegistry = (BeanDefinitionRegistry) beanFactory;
|
||||
if (result instanceof AbstractEndpoint endpoint) {
|
||||
String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup", String.class);
|
||||
if (StringUtils.hasText(autoStartup)) {
|
||||
@@ -255,51 +335,13 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotationType);
|
||||
endpoint.setBeanName(endpointBeanName);
|
||||
definitionRegistry.registerBeanDefinition(endpointBeanName,
|
||||
new RootBeanDefinition((Class<AbstractEndpoint>) endpoint.getClass(), () -> endpoint));
|
||||
getBeanDefinitionRegistry()
|
||||
.registerBeanDefinition(endpointBeanName,
|
||||
new RootBeanDefinition((Class<AbstractEndpoint>) endpoint.getClass(), () -> endpoint));
|
||||
beanFactory.getBean(endpointBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messagingAnnotation the {@link Annotation} to take a chain for its meta-annotations.
|
||||
* @param annotationType the annotation type.
|
||||
* @return the hierarchical list of annotations in top-bottom order.
|
||||
*/
|
||||
protected List<Annotation> getAnnotationChain(Annotation messagingAnnotation,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
List<Annotation> annotationChain = new LinkedList<>();
|
||||
Set<Annotation> visited = new HashSet<>();
|
||||
|
||||
recursiveFindAnnotation(annotationType, messagingAnnotation, annotationChain, visited);
|
||||
if (annotationChain.size() > 0) {
|
||||
Collections.reverse(annotationChain);
|
||||
}
|
||||
|
||||
return annotationChain;
|
||||
}
|
||||
|
||||
protected boolean recursiveFindAnnotation(Class<? extends Annotation> annotationType, Annotation ann,
|
||||
List<Annotation> annotationChain, Set<Annotation> visited) {
|
||||
|
||||
if (ann.annotationType().equals(annotationType)) {
|
||||
annotationChain.add(ann);
|
||||
return true;
|
||||
}
|
||||
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||
if (!ann.equals(metaAnn) && !visited.contains(metaAnn)
|
||||
&& !(metaAnn.annotationType().getPackage().getName().startsWith("java.lang"))) {
|
||||
visited.add(metaAnn); // prevent infinite recursion if the same annotation is found again
|
||||
if (recursiveFindAnnotation(annotationType, metaAnn, annotationChain, visited)) {
|
||||
annotationChain.add(ann);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String generateBeanName(String originalBeanName, Method method,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
@@ -309,7 +351,8 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
+ ClassUtils.getShortNameAsProperty(annotationType);
|
||||
name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
while (beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +363,7 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
return this.postProcessors;
|
||||
}
|
||||
|
||||
private record MessagingMetaAnnotation(Annotation annotation, Class<? extends Annotation> messagingAnnotationType) {
|
||||
protected record MessagingMetaAnnotation(Annotation annotation, Class<? extends Annotation> annotationType) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -20,6 +20,11 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Strategy interface for post-processing annotated methods.
|
||||
*
|
||||
@@ -31,8 +36,12 @@ import java.util.List;
|
||||
*/
|
||||
public interface MethodAnnotationPostProcessor<T extends Annotation> {
|
||||
|
||||
String INPUT_CHANNEL_ATTRIBUTE = "inputChannel";
|
||||
|
||||
Object postProcess(Object bean, String beanName, Method method, List<Annotation> annotations);
|
||||
|
||||
void processBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition, List<Annotation> annotations);
|
||||
|
||||
/**
|
||||
* Determine if the provided {@code method} and its {@code annotations} are eligible
|
||||
* to create an {@link org.springframework.integration.endpoint.AbstractEndpoint}.
|
||||
@@ -42,6 +51,26 @@ public interface MethodAnnotationPostProcessor<T extends Annotation> {
|
||||
* {@link org.springframework.integration.endpoint.AbstractEndpoint}
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean shouldCreateEndpoint(Method method, List<Annotation> annotations);
|
||||
default boolean shouldCreateEndpoint(Method method, List<Annotation> annotations) {
|
||||
return shouldCreateEndpoint(MergedAnnotations.from(method), annotations);
|
||||
}
|
||||
|
||||
default boolean shouldCreateEndpoint(MergedAnnotations mergedAnnotations, List<Annotation> annotations) {
|
||||
String inputChannel =
|
||||
MessagingAnnotationUtils.resolveAttribute(annotations, getInputChannelAttribute(), String.class);
|
||||
return StringUtils.hasText(inputChannel);
|
||||
}
|
||||
|
||||
default String getInputChannelAttribute() {
|
||||
return INPUT_CHANNEL_ATTRIBUTE;
|
||||
}
|
||||
|
||||
default boolean beanAnnotationAware() {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean supportsPojoMethod() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -20,18 +20,24 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.config.RouterFactoryBean;
|
||||
import org.springframework.integration.router.AbstractMessageRouter;
|
||||
import org.springframework.integration.router.MethodInvokingRouter;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -44,41 +50,62 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class RouterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Router> {
|
||||
|
||||
public RouterAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
public RouterAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.addAll(Arrays.asList("defaultOutputChannel", "applySequence",
|
||||
"ignoreSendFailures", "resolutionRequired", "channelMappings", "prefix", "suffix"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
super.resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
|
||||
BeanMetadataElement targetObjectBeanDefinition = buildLambdaMessageProcessor(handlerBeanType, beanDefinition);
|
||||
if (targetObjectBeanDefinition == null) {
|
||||
targetObjectBeanDefinition = new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
BeanDefinition routerBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(RouterFactoryBean.class)
|
||||
.addPropertyValue("targetObject", targetObjectBeanDefinition)
|
||||
.getBeanDefinition();
|
||||
|
||||
new BeanDefinitionPropertiesMapper(routerBeanDefinition, annotations)
|
||||
.setPropertyValue("applySequence")
|
||||
.setPropertyValue("ignoreSendFailures")
|
||||
.setPropertyValue("resolutionRequired")
|
||||
.setPropertyValue("prefix")
|
||||
.setPropertyValue("suffix");
|
||||
|
||||
String[] channelMappings = MessagingAnnotationUtils.resolveAttribute(annotations, "channelMappings",
|
||||
String[].class);
|
||||
if (!ObjectUtils.isEmpty(channelMappings)) {
|
||||
Map<String, String> mappings =
|
||||
Arrays.stream(channelMappings)
|
||||
.map((mapping) -> {
|
||||
String[] keyValue = mapping.split("=");
|
||||
return Map.entry(keyValue[0], keyValue[1]);
|
||||
})
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
routerBeanDefinition.getPropertyValues()
|
||||
.addPropertyValue("channelMappings", mappings);
|
||||
}
|
||||
|
||||
return routerBeanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
AbstractMessageRouter router;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
router = extractTypeIfPossible(target, AbstractMessageRouter.class);
|
||||
if (router == null) {
|
||||
if (target instanceof MessageHandler) {
|
||||
Assert.isTrue(routerAttributesProvided(annotations),
|
||||
"'defaultOutputChannel', 'applySequence', 'ignoreSendFailures', 'resolutionRequired', " +
|
||||
"'channelMappings', 'prefix' and 'suffix' " +
|
||||
"can be applied to 'AbstractMessageRouter' implementations, but target handler is: " +
|
||||
target.getClass());
|
||||
return (MessageHandler) target;
|
||||
}
|
||||
else {
|
||||
router = new MethodInvokingRouter(target);
|
||||
}
|
||||
}
|
||||
else {
|
||||
checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
|
||||
return router;
|
||||
}
|
||||
}
|
||||
else {
|
||||
router = new MethodInvokingRouter(bean, method);
|
||||
}
|
||||
String defaultOutputChannelName = MessagingAnnotationUtils.resolveAttribute(annotations,
|
||||
"defaultOutputChannel", String.class);
|
||||
AbstractMessageRouter router = new MethodInvokingRouter(bean, method);
|
||||
String defaultOutputChannelName =
|
||||
MessagingAnnotationUtils.resolveAttribute(annotations, "defaultOutputChannel", String.class);
|
||||
if (StringUtils.hasText(defaultOutputChannelName)) {
|
||||
router.setDefaultOutputChannelName(defaultOutputChannelName);
|
||||
}
|
||||
@@ -110,14 +137,16 @@ public class RouterAnnotationPostProcessor extends AbstractMethodAnnotationPostP
|
||||
methodInvokingRouter.setResolutionRequired(resolveAttributeToBoolean(resolutionRequired));
|
||||
}
|
||||
|
||||
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
String prefix = MessagingAnnotationUtils.resolveAttribute(annotations, "prefix", String.class);
|
||||
if (StringUtils.hasText(prefix)) {
|
||||
methodInvokingRouter.setPrefix(this.beanFactory.resolveEmbeddedValue(prefix));
|
||||
methodInvokingRouter.setPrefix(beanFactory.resolveEmbeddedValue(prefix));
|
||||
}
|
||||
|
||||
String suffix = MessagingAnnotationUtils.resolveAttribute(annotations, "suffix", String.class);
|
||||
if (StringUtils.hasText(suffix)) {
|
||||
methodInvokingRouter.setSuffix(this.beanFactory.resolveEmbeddedValue(suffix));
|
||||
methodInvokingRouter.setSuffix(beanFactory.resolveEmbeddedValue(suffix));
|
||||
}
|
||||
|
||||
String[] channelMappings = MessagingAnnotationUtils.resolveAttribute(annotations, "channelMappings",
|
||||
@@ -127,7 +156,7 @@ public class RouterAnnotationPostProcessor extends AbstractMethodAnnotationPostP
|
||||
for (String channelMapping : channelMappings) {
|
||||
mappings.append(channelMapping).append("\n");
|
||||
}
|
||||
Properties properties = (Properties) this.conversionService.convert(mappings.toString(),
|
||||
Properties properties = (Properties) getConversionService().convert(mappings.toString(),
|
||||
TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Properties.class));
|
||||
methodInvokingRouter.replaceChannelMappings(properties);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,18 +21,18 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.config.ServiceActivatorFactoryBean;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.handler.ReactiveMessageHandlerAdapter;
|
||||
import org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.ReactiveMessageHandler;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -45,47 +45,41 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ServiceActivatorAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<ServiceActivator> {
|
||||
|
||||
public ServiceActivatorAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
public ServiceActivatorAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.addAll(Arrays.asList("outputChannel", "requiresReply", "adviceChain"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
super.resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
|
||||
BeanMetadataElement targetObjectBeanDefinition = buildLambdaMessageProcessor(handlerBeanType, beanDefinition);
|
||||
if (targetObjectBeanDefinition == null) {
|
||||
targetObjectBeanDefinition = new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
BeanDefinition serviceActivatorBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ServiceActivatorFactoryBean.class)
|
||||
.addPropertyValue("targetObject", targetObjectBeanDefinition)
|
||||
.getBeanDefinition();
|
||||
|
||||
new BeanDefinitionPropertiesMapper(serviceActivatorBeanDefinition, annotations)
|
||||
.setPropertyValue("requiresReply")
|
||||
.setPropertyValue("async");
|
||||
|
||||
return serviceActivatorBeanDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
AbstractReplyProducingMessageHandler serviceActivator;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
serviceActivator = extractTypeIfPossible(target, AbstractReplyProducingMessageHandler.class);
|
||||
if (serviceActivator == null) {
|
||||
if (target instanceof ReactiveMessageHandler) {
|
||||
return new ReactiveMessageHandlerAdapter((ReactiveMessageHandler) target);
|
||||
}
|
||||
if (target instanceof MessageHandler) {
|
||||
/*
|
||||
* Return a reply-producing message handler so that we still get 'produced no reply' messages
|
||||
* and the super class will inject the advice chain to advise the handler method if needed.
|
||||
*/
|
||||
return new ReplyProducingMessageHandlerWrapper((MessageHandler) target);
|
||||
}
|
||||
else {
|
||||
MessageProcessor<?> messageProcessor = buildLambdaMessageProcessorForBeanMethod(method, target);
|
||||
if (messageProcessor != null) {
|
||||
serviceActivator = new ServiceActivatingHandler(messageProcessor);
|
||||
}
|
||||
else {
|
||||
serviceActivator = new ServiceActivatingHandler(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
|
||||
return (MessageHandler) target;
|
||||
}
|
||||
}
|
||||
else {
|
||||
serviceActivator = new ServiceActivatingHandler(bean, method);
|
||||
}
|
||||
AbstractReplyProducingMessageHandler serviceActivator = new ServiceActivatingHandler(bean, method);
|
||||
|
||||
String requiresReply = MessagingAnnotationUtils.resolveAttribute(annotations, "requiresReply", String.class);
|
||||
if (StringUtils.hasText(requiresReply)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,15 +21,18 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
import org.springframework.integration.config.SplitterFactoryBean;
|
||||
import org.springframework.integration.splitter.AbstractMessageSplitter;
|
||||
import org.springframework.integration.splitter.MethodInvokingSplitter;
|
||||
import org.springframework.integration.util.MessagingAnnotationUtils;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -41,43 +44,47 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class SplitterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Splitter> {
|
||||
|
||||
public SplitterAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
public SplitterAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.addAll(Arrays.asList("outputChannel", "applySequence", "adviceChain"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
super.resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
|
||||
BeanMetadataElement targetObjectBeanDefinition = buildLambdaMessageProcessor(handlerBeanType, beanDefinition);
|
||||
if (targetObjectBeanDefinition == null) {
|
||||
targetObjectBeanDefinition = new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
BeanDefinitionBuilder splitterBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(SplitterFactoryBean.class)
|
||||
.addPropertyValue("targetObject", targetObjectBeanDefinition);
|
||||
|
||||
String applySequence = MessagingAnnotationUtils.resolveAttribute(annotations, "applySequence", String.class);
|
||||
|
||||
AbstractMessageSplitter splitter;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
splitter = this.extractTypeIfPossible(target, AbstractMessageSplitter.class);
|
||||
if (splitter == null) {
|
||||
if (target instanceof MessageHandler) {
|
||||
Assert.hasText(applySequence, "'applySequence' can be applied to 'AbstractMessageSplitter', but " +
|
||||
"target handler is: " + target.getClass());
|
||||
return (MessageHandler) target;
|
||||
}
|
||||
else {
|
||||
splitter = new MethodInvokingSplitter(target);
|
||||
}
|
||||
}
|
||||
else {
|
||||
checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
|
||||
return splitter;
|
||||
}
|
||||
}
|
||||
else {
|
||||
splitter = new MethodInvokingSplitter(bean, method);
|
||||
if (StringUtils.hasText(applySequence)) {
|
||||
splitterBeanDefinition.addPropertyValue("applySequence", applySequence);
|
||||
}
|
||||
return splitterBeanDefinition.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
AbstractMessageSplitter splitter = new MethodInvokingSplitter(bean, method);
|
||||
|
||||
String applySequence = MessagingAnnotationUtils.resolveAttribute(annotations, "applySequence", String.class);
|
||||
if (StringUtils.hasText(applySequence)) {
|
||||
splitter.setApplySequence(resolveAttributeToBoolean(applySequence));
|
||||
}
|
||||
|
||||
this.setOutputChannelIfPresent(annotations, splitter);
|
||||
setOutputChannelIfPresent(annotations, splitter);
|
||||
return splitter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,11 +21,13 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.integration.config.TransformerFactoryBean;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.transformer.MethodInvokingTransformer;
|
||||
import org.springframework.integration.transformer.Transformer;
|
||||
@@ -42,35 +44,34 @@ import org.springframework.messaging.MessageHandler;
|
||||
public class TransformerAnnotationPostProcessor
|
||||
extends AbstractMethodAnnotationPostProcessor<org.springframework.integration.annotation.Transformer> {
|
||||
|
||||
public TransformerAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
public TransformerAnnotationPostProcessor() {
|
||||
this.messageHandlerAttributes.addAll(Arrays.asList("outputChannel", "adviceChain"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
Transformer transformer;
|
||||
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
|
||||
transformer = extractTypeIfPossible(target, Transformer.class);
|
||||
if (transformer == null) {
|
||||
if (extractTypeIfPossible(target, AbstractReplyProducingMessageHandler.class) != null) {
|
||||
checkMessageHandlerAttributes(resolveTargetBeanName(method), annotations);
|
||||
return (MessageHandler) target;
|
||||
}
|
||||
MessageProcessor<?> messageProcessor = buildLambdaMessageProcessorForBeanMethod(method, target);
|
||||
if (messageProcessor != null) {
|
||||
transformer = new MethodInvokingTransformer(messageProcessor);
|
||||
}
|
||||
else {
|
||||
transformer = new MethodInvokingTransformer(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
transformer = new MethodInvokingTransformer(bean, method);
|
||||
protected BeanDefinition resolveHandlerBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition,
|
||||
ResolvableType handlerBeanType, List<Annotation> annotations) {
|
||||
|
||||
BeanDefinition handlerBeanDefinition =
|
||||
super.resolveHandlerBeanDefinition(beanName, beanDefinition, handlerBeanType, annotations);
|
||||
|
||||
if (handlerBeanDefinition != null) {
|
||||
return handlerBeanDefinition;
|
||||
}
|
||||
|
||||
BeanMetadataElement targetObjectBeanDefinition = buildLambdaMessageProcessor(handlerBeanType, beanDefinition);
|
||||
if (targetObjectBeanDefinition == null) {
|
||||
targetObjectBeanDefinition = new RuntimeBeanReference(beanName);
|
||||
}
|
||||
|
||||
return BeanDefinitionBuilder.genericBeanDefinition(TransformerFactoryBean.class)
|
||||
.addPropertyValue("targetObject", targetObjectBeanDefinition)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
Transformer transformer = new MethodInvokingTransformer(bean, method);
|
||||
MessageTransformingHandler handler = new MessageTransformingHandler(transformer);
|
||||
setOutputChannelIfPresent(annotations, handler);
|
||||
return handler;
|
||||
|
||||
@@ -18,14 +18,21 @@ package org.springframework.integration.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.integration.annotation.EndpointId;
|
||||
import org.springframework.integration.annotation.Payloads;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
@@ -51,7 +58,6 @@ public final class MessagingAnnotationUtils {
|
||||
/**
|
||||
* Get the attribute value from the annotation hierarchy, returning the first
|
||||
* {@link MessagingAnnotationUtils#hasValue non-empty}) value closest to the annotated method.
|
||||
*
|
||||
* @param annotations The meta-annotations in order (closest first).
|
||||
* @param name The attribute name.
|
||||
* @param requiredType The expected type.
|
||||
@@ -88,11 +94,8 @@ public final class MessagingAnnotationUtils {
|
||||
return false;
|
||||
}
|
||||
// Annotation with 'value' set to special 'none' string
|
||||
if ((annotationValue instanceof Annotation) &&
|
||||
ValueConstants.DEFAULT_NONE.equals(AnnotationUtils.getValue((Annotation) annotationValue))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return (!(annotationValue instanceof Annotation)) ||
|
||||
!ValueConstants.DEFAULT_NONE.equals(AnnotationUtils.getValue((Annotation) annotationValue));
|
||||
}
|
||||
|
||||
public static Method findAnnotatedMethod(Object target, final Class<? extends Annotation> annotationType) {
|
||||
@@ -143,9 +146,62 @@ public final class MessagingAnnotationUtils {
|
||||
* @return the id, or null.
|
||||
* @since 5.0.4
|
||||
*/
|
||||
@Nullable
|
||||
public static String endpointIdValue(Method method) {
|
||||
EndpointId endpointId = AnnotationUtils.findAnnotation(method, EndpointId.class);
|
||||
return endpointId != null ? endpointId.value() : null;
|
||||
return endpointIdValue(MergedAnnotations.from(method));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link EndpointId#value()} property, if present.
|
||||
* @param mergedAnnotations the {@link MergedAnnotations} to analyze.
|
||||
* @return the id, or null.
|
||||
* @since 6.0
|
||||
*/
|
||||
@Nullable
|
||||
public static String endpointIdValue(MergedAnnotations mergedAnnotations) {
|
||||
MergedAnnotation<EndpointId> endpointIdAnnotation = mergedAnnotations.get(EndpointId.class);
|
||||
return endpointIdAnnotation.getValue(AnnotationUtils.VALUE, String.class).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chain of its meta-annotations for the provided instance and expected type.
|
||||
* @param messagingAnnotation the {@link Annotation} to take a chain for its meta-annotations.
|
||||
* @param annotationType the annotation type.
|
||||
* @return the hierarchical list of annotations in top-bottom order.
|
||||
* @since 6.0
|
||||
*/
|
||||
public static List<Annotation> getAnnotationChain(Annotation messagingAnnotation,
|
||||
Class<? extends Annotation> annotationType) {
|
||||
|
||||
List<Annotation> annotationChain = new LinkedList<>();
|
||||
Set<Annotation> visited = new HashSet<>();
|
||||
|
||||
recursiveFindAnnotation(annotationType, messagingAnnotation, annotationChain, visited);
|
||||
if (annotationChain.size() > 0) {
|
||||
Collections.reverse(annotationChain);
|
||||
}
|
||||
|
||||
return annotationChain;
|
||||
}
|
||||
|
||||
private static boolean recursiveFindAnnotation(Class<? extends Annotation> annotationType, Annotation ann,
|
||||
List<Annotation> annotationChain, Set<Annotation> visited) {
|
||||
|
||||
if (ann.annotationType().equals(annotationType)) {
|
||||
annotationChain.add(ann);
|
||||
return true;
|
||||
}
|
||||
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
|
||||
if (!ann.equals(metaAnn) && !visited.contains(metaAnn)
|
||||
&& !(metaAnn.annotationType().getPackage().getName().startsWith("java.lang"))) {
|
||||
visited.add(metaAnn); // prevent infinite recursion if the same annotation is found again
|
||||
if (recursiveFindAnnotation(annotationType, metaAnn, annotationChain, visited)) {
|
||||
annotationChain.add(ann);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MessagingAnnotationUtils() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -19,10 +19,11 @@ package org.springframework.integration.bus;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -45,20 +46,20 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
*/
|
||||
public class DirectChannelSubscriptionTests {
|
||||
|
||||
private TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
private final TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
|
||||
private DirectChannel sourceChannel = new DirectChannel();
|
||||
private final DirectChannel sourceChannel = new DirectChannel();
|
||||
|
||||
private PollableChannel targetChannel = new QueueChannel();
|
||||
private final PollableChannel targetChannel = new QueueChannel();
|
||||
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setupChannels() {
|
||||
this.context.registerChannel("sourceChannel", this.sourceChannel);
|
||||
this.context.registerChannel("targetChannel", this.targetChannel);
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
this.context.close();
|
||||
}
|
||||
@@ -80,8 +81,8 @@ public class DirectChannelSubscriptionTests {
|
||||
@Test
|
||||
public void sendAndReceiveForAnnotatedEndpoint() {
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(this.context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory());
|
||||
postProcessor.postProcessBeanFactory(this.context.getBeanFactory());
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
TestEndpoint endpoint = new TestEndpoint();
|
||||
postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint");
|
||||
@@ -91,7 +92,7 @@ public class DirectChannelSubscriptionTests {
|
||||
assertThat(response.getPayload()).isEqualTo("foo-from-annotated-endpoint");
|
||||
}
|
||||
|
||||
@Test(expected = MessagingException.class)
|
||||
@Test
|
||||
public void exceptionThrownFromRegisteredEndpoint() {
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@@ -104,7 +105,8 @@ public class DirectChannelSubscriptionTests {
|
||||
EventDrivenConsumer endpoint = new EventDrivenConsumer(sourceChannel, handler);
|
||||
this.context.registerEndpoint("testEndpoint", endpoint);
|
||||
this.context.refresh();
|
||||
this.sourceChannel.send(new GenericMessage<>("foo"));
|
||||
assertThatExceptionOfType(MessagingException.class)
|
||||
.isThrownBy(() -> this.sourceChannel.send(new GenericMessage<>("foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,8 +114,7 @@ public class DirectChannelSubscriptionTests {
|
||||
QueueChannel errorChannel = new QueueChannel();
|
||||
this.context.registerChannel(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME, errorChannel);
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(this.context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.postProcessBeanFactory(this.context.getBeanFactory());
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
FailingTestEndpoint endpoint = new FailingTestEndpoint();
|
||||
postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint");
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
@@ -105,7 +106,7 @@ public class CustomMessagingAnnotationTests {
|
||||
|
||||
MessagingAnnotationPostProcessor messagingAnnotationPostProcessor = new MessagingAnnotationPostProcessor();
|
||||
messagingAnnotationPostProcessor.
|
||||
addMessagingAnnotationPostProcessor(Logging.class, new LogAnnotationPostProcessor(beanFactory));
|
||||
addMessagingAnnotationPostProcessor(Logging.class, new LogAnnotationPostProcessor());
|
||||
return messagingAnnotationPostProcessor;
|
||||
}
|
||||
|
||||
@@ -124,29 +125,24 @@ public class CustomMessagingAnnotationTests {
|
||||
|
||||
String value();
|
||||
|
||||
|
||||
LoggingHandler.Level level() default LoggingHandler.Level.INFO;
|
||||
|
||||
}
|
||||
|
||||
private static class LogAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Logging> {
|
||||
|
||||
LogAnnotationPostProcessor(ConfigurableListableBeanFactory beanFactory) {
|
||||
super(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInputChannelAttribute() {
|
||||
return "value";
|
||||
public String getInputChannelAttribute() {
|
||||
return AnnotationUtils.VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
|
||||
LoggingHandler.Level level = MessagingAnnotationUtils.resolveAttribute(annotations, "level",
|
||||
LoggingHandler.Level.class);
|
||||
LoggingHandler loggingHandler = new LoggingHandler(level.name());
|
||||
LoggingHandler loggingHandler = new LoggingHandler(level);
|
||||
MethodInvokingMessageProcessor<String> processor = new MethodInvokingMessageProcessor<>(bean, method);
|
||||
processor.setBeanFactory(this.beanFactory);
|
||||
processor.setBeanFactory(getBeanFactory());
|
||||
loggingHandler.setLogExpression(new FunctionExpression<>(processor::processMessage));
|
||||
return loggingHandler;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -22,10 +22,11 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -54,16 +55,16 @@ public class FilterAnnotationPostProcessorTests {
|
||||
|
||||
private final QueueChannel outputChannel = new QueueChannel();
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
this.context.registerChannel("input", this.inputChannel);
|
||||
this.context.registerChannel("output", this.outputChannel);
|
||||
this.postProcessor.setBeanFactory(this.context.getBeanFactory());
|
||||
this.postProcessor.afterPropertiesSet();
|
||||
this.postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory());
|
||||
this.postProcessor.postProcessBeanFactory(this.context.getBeanFactory());
|
||||
this.postProcessor.afterSingletonsInstantiated();
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
this.context.close();
|
||||
}
|
||||
@@ -111,7 +112,7 @@ public class FilterAnnotationPostProcessorTests {
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceArray() {
|
||||
TestAdvice advice = new TestAdvice();
|
||||
context.registerBean("adviceChain", new TestAdvice[] { advice });
|
||||
context.registerBean("adviceChain", new TestAdvice[]{ advice });
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithin());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
assertThat(TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class).get(0)).isSameAs(advice);
|
||||
@@ -122,10 +123,10 @@ public class FilterAnnotationPostProcessorTests {
|
||||
public void filterAnnotationWithAdviceArrayTwice() {
|
||||
TestAdvice advice1 = new TestAdvice();
|
||||
TestAdvice advice2 = new TestAdvice();
|
||||
context.registerBean("adviceChain1", new TestAdvice[] { advice1, advice2 });
|
||||
context.registerBean("adviceChain1", new TestAdvice[]{ advice1, advice2 });
|
||||
TestAdvice advice3 = new TestAdvice();
|
||||
TestAdvice advice4 = new TestAdvice();
|
||||
context.registerBean("adviceChain2", new TestAdvice[] { advice3, advice4 });
|
||||
context.registerBean("adviceChain2", new TestAdvice[]{ advice3, advice4 });
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithinTwice());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
List<?> adviceList = TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class);
|
||||
@@ -151,10 +152,10 @@ public class FilterAnnotationPostProcessorTests {
|
||||
public void filterAnnotationWithAdviceCollectionTwice() {
|
||||
TestAdvice advice1 = new TestAdvice();
|
||||
TestAdvice advice2 = new TestAdvice();
|
||||
context.registerBean("adviceChain1", new TestAdvice[] { advice1, advice2 });
|
||||
context.registerBean("adviceChain1", new TestAdvice[]{ advice1, advice2 });
|
||||
TestAdvice advice3 = new TestAdvice();
|
||||
TestAdvice advice4 = new TestAdvice();
|
||||
context.registerBean("adviceChain2", new TestAdvice[] { advice3, advice4 });
|
||||
context.registerBean("adviceChain2", new TestAdvice[]{ advice3, advice4 });
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithinTwice());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
List<?> adviceList = TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.integration.config.annotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -27,9 +26,10 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
@@ -149,14 +149,6 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostProcessorWithoutBeanFactory() {
|
||||
MessagingAnnotationPostProcessor postProcessor =
|
||||
new MessagingAnnotationPostProcessor();
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(postProcessor::afterPropertiesSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChannelResolution() {
|
||||
TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
@@ -307,12 +299,12 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
private MessagingAnnotationPostProcessor prepareMessagingAnnotationPostProcessor(
|
||||
private static MessagingAnnotationPostProcessor prepareMessagingAnnotationPostProcessor(
|
||||
ConfigurableApplicationContext context) {
|
||||
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) context.getBeanFactory());
|
||||
postProcessor.postProcessBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
return postProcessor;
|
||||
}
|
||||
|
||||
@@ -166,8 +166,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
|
||||
assertThat(receive.getPayload()).isInstanceOf(MessageRejectedException.class);
|
||||
MessageRejectedException exception = (MessageRejectedException) receive.getPayload();
|
||||
assertThat(exception.getMessage())
|
||||
.contains("message has been rejected in filter: bean " +
|
||||
"'messagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.filter.filter.handler'");
|
||||
.contains("message has been rejected in filter: bean 'filter.filter.handler'");
|
||||
|
||||
}
|
||||
|
||||
@@ -330,7 +329,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "aggregatorChannel")
|
||||
public MessageHandler aggregator() {
|
||||
public AggregatingMessageHandler aggregator() {
|
||||
AggregatingMessageHandler handler = new AggregatingMessageHandler(MessageGroup::getMessages);
|
||||
handler.setCorrelationStrategy(new ExpressionEvaluatingCorrelationStrategy("1"));
|
||||
handler.setReleaseStrategy(new ExpressionEvaluatingReleaseStrategy("size() == 10"));
|
||||
@@ -355,7 +354,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
|
||||
|
||||
@Bean
|
||||
@Splitter(inputChannel = "splitterChannel", reactive = @Reactive("reactiveCustomizer"))
|
||||
public MessageHandler splitter() {
|
||||
public DefaultMessageSplitter splitter() {
|
||||
DefaultMessageSplitter defaultMessageSplitter = new DefaultMessageSplitter();
|
||||
defaultMessageSplitter.setOutputChannelName("serviceChannel");
|
||||
return defaultMessageSplitter;
|
||||
@@ -470,7 +469,7 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
|
||||
|
||||
@Bean
|
||||
@Splitter(inputChannel = "splitterChannel", applySequence = "false")
|
||||
public MessageHandler splitter() {
|
||||
public DefaultMessageSplitter splitter() {
|
||||
return new DefaultMessageSplitter();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -21,10 +21,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.Router;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
@@ -42,6 +43,8 @@ public class RouterAnnotationPostProcessorTests {
|
||||
|
||||
private final TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
|
||||
private final MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
|
||||
private final DirectChannel inputChannel = new DirectChannel();
|
||||
|
||||
private final QueueChannel outputChannel = new QueueChannel();
|
||||
@@ -53,26 +56,26 @@ public class RouterAnnotationPostProcessorTests {
|
||||
private final QueueChannel stringChannel = new QueueChannel();
|
||||
|
||||
|
||||
@Before
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
context.registerChannel("input", inputChannel);
|
||||
context.registerChannel("output", outputChannel);
|
||||
context.registerChannel("routingChannel", routingChannel);
|
||||
context.registerChannel("integerChannel", integerChannel);
|
||||
context.registerChannel("stringChannel", stringChannel);
|
||||
this.postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory());
|
||||
this.postProcessor.postProcessBeanFactory(this.context.getBeanFactory());
|
||||
this.postProcessor.afterSingletonsInstantiated();
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRouter() {
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
TestRouter testRouter = new TestRouter();
|
||||
postProcessor.postProcessAfterInitialization(testRouter, "test");
|
||||
context.refresh();
|
||||
@@ -84,10 +87,6 @@ public class RouterAnnotationPostProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testRouterWithListParam() {
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
TestRouter testRouter = new TestRouter();
|
||||
postProcessor.postProcessAfterInitialization(testRouter, "test");
|
||||
context.refresh();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -18,10 +18,11 @@ package org.springframework.integration.config.annotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.Splitter;
|
||||
@@ -39,20 +40,20 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
*/
|
||||
public class SplitterAnnotationPostProcessorTests {
|
||||
|
||||
private TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
private final TestApplicationContext context = TestUtils.createTestApplicationContext();
|
||||
|
||||
private DirectChannel inputChannel = new DirectChannel();
|
||||
private final DirectChannel inputChannel = new DirectChannel();
|
||||
|
||||
private QueueChannel outputChannel = new QueueChannel();
|
||||
private final QueueChannel outputChannel = new QueueChannel();
|
||||
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
context.registerChannel("input", inputChannel);
|
||||
context.registerChannel("output", outputChannel);
|
||||
this.context.registerChannel("input", this.inputChannel);
|
||||
this.context.registerChannel("output", this.outputChannel);
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
this.context.close();
|
||||
}
|
||||
@@ -60,8 +61,8 @@ public class SplitterAnnotationPostProcessorTests {
|
||||
@Test
|
||||
public void testSplitterAnnotation() {
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory());
|
||||
postProcessor.postProcessBeanFactory(this.context.getBeanFactory());
|
||||
postProcessor.afterSingletonsInstantiated();
|
||||
TestSplitter splitter = new TestSplitter();
|
||||
postProcessor.postProcessAfterInitialization(splitter, "testSplitter");
|
||||
|
||||
@@ -298,11 +298,11 @@ public class EnableIntegrationTests {
|
||||
private CountDownLatch inputReceiveLatch;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("enableIntegrationTests.ContextConfiguration2.sendAsyncHandler.serviceActivator")
|
||||
@Qualifier("sendAsyncHandler.serviceActivator")
|
||||
private AbstractEndpoint sendAsyncHandler;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("enableIntegrationTests.ChildConfiguration.autoCreatedChannelMessageSource.inboundChannelAdapter")
|
||||
@Qualifier("autoCreatedChannelMessageSource.inboundChannelAdapter")
|
||||
private Lifecycle autoCreatedChannelMessageSourceAdapter;
|
||||
|
||||
@Autowired
|
||||
@@ -669,8 +669,7 @@ public class EnableIntegrationTests {
|
||||
.isThrownBy(() -> this.metaBridgeInput.send(testMessage))
|
||||
.withMessageContaining("Dispatcher has no subscribers");
|
||||
|
||||
this.context.getBean("enableIntegrationTests.ContextConfiguration.metaBridgeOutput.bridgeFrom",
|
||||
Lifecycle.class).start();
|
||||
this.context.getBean("metaBridgeOutput.bridgeFrom", Lifecycle.class).start();
|
||||
|
||||
this.metaBridgeInput.send(testMessage);
|
||||
receive = this.metaBridgeOutput.receive(10_000);
|
||||
@@ -697,8 +696,7 @@ public class EnableIntegrationTests {
|
||||
.isThrownBy(() -> this.myBridgeToInput.send(testMessage))
|
||||
.withMessageContaining("Dispatcher has no subscribers");
|
||||
|
||||
this.context.getBean("enableIntegrationTests.ContextConfiguration.myBridgeToInput.bridgeTo",
|
||||
Lifecycle.class).start();
|
||||
this.context.getBean("myBridgeToInput.bridgeTo", Lifecycle.class).start();
|
||||
|
||||
this.myBridgeToInput.send(bridgeMessage);
|
||||
receive = replyChannel.receive(10_000);
|
||||
@@ -741,8 +739,7 @@ public class EnableIntegrationTests {
|
||||
assertThat(this.roleController.noEndpointsRunning("bar")).isFalse();
|
||||
Map<String, Boolean> state = this.roleController.getEndpointsRunningStatus("foo");
|
||||
assertThat(state.get("annotationTestService.handle.serviceActivator")).isEqualTo(Boolean.FALSE);
|
||||
assertThat(state.get("enableIntegrationTests.ContextConfiguration2.sendAsyncHandler.serviceActivator"))
|
||||
.isEqualTo(Boolean.TRUE);
|
||||
assertThat(state.get("sendAsyncHandler.serviceActivator")).isEqualTo(Boolean.TRUE);
|
||||
this.roleController.startLifecyclesInRole("foo");
|
||||
assertThat(this.roleController.allEndpointsRunning("foo")).isTrue();
|
||||
this.roleController.stopLifecyclesInRole("foo");
|
||||
@@ -1690,6 +1687,7 @@ public class EnableIntegrationTests {
|
||||
@InboundChannelAdapter(value = "counterChannel", autoStartup = "false", phase = "23")
|
||||
public @interface MyInboundChannelAdapter {
|
||||
|
||||
@AliasFor(annotation = InboundChannelAdapter.class, attribute = "value")
|
||||
String value() default "";
|
||||
|
||||
String autoStartup() default "";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2022 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.
|
||||
@@ -67,7 +67,7 @@ public class BeanNameTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipBean.handler")
|
||||
@Qualifier("eipBeanHandler")
|
||||
private MessageHandler eipBeanHandler;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -76,11 +76,11 @@ public class BeanNameTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipBean2.handler")
|
||||
@Qualifier("eipBean2Handler")
|
||||
private MessageHandler eipBean2Handler;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("eipBean2.handler.wrapper")
|
||||
@Qualifier("eipBean2.handler")
|
||||
private ReplyProducingMessageHandlerWrapper eipBean2HandlerWrapper;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -98,7 +98,7 @@ public class BeanNameTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Autowired
|
||||
@Qualifier("eipSource.source")
|
||||
@Qualifier("eipSourceSource")
|
||||
private MessageSource<?> eipSourceSource;
|
||||
|
||||
@Test
|
||||
@@ -124,10 +124,10 @@ public class BeanNameTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean("eipBean.handler")
|
||||
@Bean("eipBeanHandler")
|
||||
@EndpointId("eipBean")
|
||||
@ServiceActivator(inputChannel = "channel2")
|
||||
public MessageHandler replyingHandler() {
|
||||
public AbstractReplyProducingMessageHandler replyingHandler() {
|
||||
return new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
@@ -138,7 +138,7 @@ public class BeanNameTests {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean("eipBean2.handler")
|
||||
@Bean("eipBean2Handler")
|
||||
@EndpointId("eipBean2")
|
||||
@ServiceActivator(inputChannel = "channel3")
|
||||
public MessageHandler handler() {
|
||||
@@ -151,7 +151,7 @@ public class BeanNameTests {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Bean("eipSource.source")
|
||||
@Bean("eipSourceSource")
|
||||
@EndpointId("eipSource")
|
||||
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
|
||||
public MessageSource<?> source() {
|
||||
|
||||
@@ -139,7 +139,7 @@ public class IntegrationGraphServerTests {
|
||||
assertThat(links.size()).isEqualTo(34);
|
||||
|
||||
jsonArray =
|
||||
JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'expressionRouter')]");
|
||||
JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'expressionRouter.router')]");
|
||||
|
||||
Map<String, Object> expressionRouter = (Map<String, Object>) jsonArray.get(0);
|
||||
assertThat(((List<?>) expressionRouter.get("routes")).size()).isEqualTo(0);
|
||||
@@ -151,7 +151,7 @@ public class IntegrationGraphServerTests {
|
||||
this.testSource.receive();
|
||||
this.expressionRouterInput.send(MessageBuilder.withPayload("foo").setHeader("foo", "fizChannel").build());
|
||||
|
||||
jsonArray = JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'router')]");
|
||||
jsonArray = JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'router.router')]");
|
||||
String routerJson = jsonArray.toJSONString();
|
||||
|
||||
this.server.rebuild();
|
||||
@@ -172,7 +172,7 @@ public class IntegrationGraphServerTests {
|
||||
assertThat(links).isNotNull();
|
||||
assertThat(links.size()).isEqualTo(37);
|
||||
|
||||
jsonArray = JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'router')]");
|
||||
jsonArray = JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'router.router')]");
|
||||
routerJson = jsonArray.toJSONString();
|
||||
assertThat(routerJson).contains("\"sendTimers\":{\"successes\":{\"count\":4");
|
||||
jsonArray = JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'toRouter')]");
|
||||
@@ -200,7 +200,7 @@ public class IntegrationGraphServerTests {
|
||||
assertThat(sourceJson).contains("\"receiveCounters\":{\"successes\":2,\"failures\":1");
|
||||
|
||||
jsonArray =
|
||||
JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'expressionRouter')]");
|
||||
JsonPathUtils.evaluate(baos.toByteArray(), "$..nodes[?(@.name == 'expressionRouter.router')]");
|
||||
|
||||
expressionRouter = (Map<String, Object>) jsonArray.get(0);
|
||||
JSONArray routes = (JSONArray) expressionRouter.get("routes");
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.integration.gateway.MessagingGatewaySupport;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
@@ -71,7 +70,7 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
@TestExecutionListeners(DependencyInjectionTestExecutionListener.class)
|
||||
@TestExecutionListeners(DependencyInjectionTestExecutionListener.class) // To ignore other default listeners
|
||||
public class MicrometerMetricsTests {
|
||||
|
||||
@Autowired
|
||||
@@ -149,7 +148,7 @@ public class MicrometerMetricsTests {
|
||||
.counter().count()).isEqualTo(1);
|
||||
|
||||
assertThat(registry.get("spring.integration.send")
|
||||
.tag("name", "eipBean.handler")
|
||||
.tag("name", "eipBean")
|
||||
.tag("result", "success")
|
||||
.timer().count()).isEqualTo(1);
|
||||
|
||||
@@ -277,7 +276,7 @@ public class MicrometerMetricsTests {
|
||||
SimpleMeterRegistry registry = new SimpleMeterRegistry();
|
||||
registry.config().meterFilter(MeterFilter.deny(id ->
|
||||
"channel".equals(id.getTag("type")) &&
|
||||
"noMeters".equals(id.getTag("name"))));
|
||||
"noMeters".equals(id.getTag("name"))));
|
||||
return registry;
|
||||
}
|
||||
|
||||
@@ -292,7 +291,7 @@ public class MicrometerMetricsTests {
|
||||
@Bean("eipBean.handler")
|
||||
@EndpointId("eipBean")
|
||||
@ServiceActivator(inputChannel = "channel2")
|
||||
public MessageHandler replyingHandler() {
|
||||
public AbstractReplyProducingMessageHandler replyingHandler() {
|
||||
return new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,9 +20,8 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -31,9 +30,7 @@ import org.springframework.integration.hazelcast.HazelcastTestRequestHandlerAdvi
|
||||
import org.springframework.integration.hazelcast.outbound.util.HazelcastOutboundChannelAdapterTestUtils;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.hazelcast.instance.impl.HazelcastInstanceFactory;
|
||||
import com.hazelcast.map.IMap;
|
||||
@@ -45,11 +42,11 @@ import com.hazelcast.topic.ITopic;
|
||||
* Hazelcast Outbound Channel Adapter JavaConfig driven Unit Test Class
|
||||
*
|
||||
* @author Eren Avsarogullari
|
||||
* @author Atem Bilan
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = HazelcastIntegrationOutboundTestConfiguration.class,
|
||||
loader = AnnotationConfigContextLoader.class)
|
||||
@SpringJUnitConfig(classes = HazelcastIntegrationOutboundTestConfiguration.class)
|
||||
@DirtiesContext
|
||||
public class HazelcastOutboundChannelAdapterConfigTests {
|
||||
|
||||
@@ -141,7 +138,7 @@ public class HazelcastOutboundChannelAdapterConfigTests {
|
||||
@Qualifier("replicatedMapRequestHandlerAdvice")
|
||||
private HazelcastTestRequestHandlerAdvice replicatedMapRequestHandlerAdvice;
|
||||
|
||||
@AfterClass
|
||||
@AfterAll
|
||||
public static void shutdown() {
|
||||
HazelcastInstanceFactory.terminateAll();
|
||||
}
|
||||
|
||||
@@ -80,9 +80,9 @@ public class StoredProcJavaConfigTests {
|
||||
// verify maxMessagesPerPoll == 1
|
||||
assertThat(received).isNull();
|
||||
MessagingTemplate template = new MessagingTemplate(this.control);
|
||||
template.convertAndSend("@'storedProcJavaConfigTests.Config.storedProc.inboundChannelAdapter'.stop()");
|
||||
template.convertAndSend("@'storedProc.inboundChannelAdapter'.stop()");
|
||||
assertThat(template.convertSendAndReceive(
|
||||
"@'storedProcJavaConfigTests.Config.storedProc.inboundChannelAdapter'.isRunning()", Boolean.class))
|
||||
"@'storedProc.inboundChannelAdapter'.isRunning()", Boolean.class))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
|
||||
@@ -139,14 +139,14 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
|
||||
@Test
|
||||
public void testPollingFlow() {
|
||||
this.controlBus.send("@'jmsTests.ContextConfiguration.integerMessageSource.inboundChannelAdapter'.start()");
|
||||
this.controlBus.send("@'integerMessageSource.inboundChannelAdapter'.start()");
|
||||
assertThat(this.beanFactory.getBean("integerChannel")).isInstanceOf(FixedSubscriberChannel.class);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Message<?> message = this.outputChannel.receive(20000);
|
||||
assertThat(message).isNotNull();
|
||||
assertThat(message.getPayload()).isEqualTo("" + i);
|
||||
}
|
||||
this.controlBus.send("@'jmsTests.ContextConfiguration.integerMessageSource.inboundChannelAdapter'.stop()");
|
||||
this.controlBus.send("@'integerMessageSource.inboundChannelAdapter'.stop()");
|
||||
|
||||
assertThat(((InterceptableChannel) this.outputChannel).getInterceptors())
|
||||
.contains(this.testChannelInterceptor);
|
||||
@@ -353,7 +353,7 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
public IntegrationFlow jmsMessageDrivenFlow() {
|
||||
return IntegrationFlow
|
||||
.from(Jms.messageDrivenChannelAdapter(amqFactory,
|
||||
DefaultMessageListenerContainer.class)
|
||||
DefaultMessageListenerContainer.class)
|
||||
.outputChannel(jmsMessageDrivenInputChannel())
|
||||
.destination("jmsMessageDriven")
|
||||
.configureListenerContainer(c -> c.clientId("foo")))
|
||||
@@ -406,23 +406,23 @@ public class JmsTests extends ActiveMQMultiContextTests {
|
||||
@Bean
|
||||
public IntegrationFlow jmsInboundGatewayFlow() {
|
||||
return IntegrationFlow.from(
|
||||
Jms.inboundGateway(amqFactory)
|
||||
.requestChannel(jmsInboundGatewayInputChannel())
|
||||
.replyTimeout(1)
|
||||
.errorOnTimeout(true)
|
||||
.errorChannel(new FixedSubscriberChannel(new AbstractReplyProducingMessageHandler() {
|
||||
Jms.inboundGateway(amqFactory)
|
||||
.requestChannel(jmsInboundGatewayInputChannel())
|
||||
.replyTimeout(1)
|
||||
.errorOnTimeout(true)
|
||||
.errorChannel(new FixedSubscriberChannel(new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return "error: " +
|
||||
((MessageTimeoutException) requestMessage.getPayload())
|
||||
.getFailedMessage().getPayload() + " is not convertible";
|
||||
}
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
return "error: " +
|
||||
((MessageTimeoutException) requestMessage.getPayload())
|
||||
.getFailedMessage().getPayload() + " is not convertible";
|
||||
}
|
||||
|
||||
}))
|
||||
.requestDestination("jmsPipelineTest")
|
||||
.configureListenerContainer(c ->
|
||||
c.transactionManager(mock(PlatformTransactionManager.class))))
|
||||
}))
|
||||
.requestDestination("jmsPipelineTest")
|
||||
.configureListenerContainer(c ->
|
||||
c.transactionManager(mock(PlatformTransactionManager.class))))
|
||||
.filter(payload -> !"junk".equals(payload))
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.get();
|
||||
|
||||
@@ -34,13 +34,14 @@ import org.springframework.integration.aggregator.ExpressionEvaluatingMessageGro
|
||||
import org.springframework.integration.aggregator.ExpressionEvaluatingReleaseStrategy;
|
||||
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.MessageCountReleaseStrategy;
|
||||
import org.springframework.integration.annotation.BridgeFrom;
|
||||
import org.springframework.integration.annotation.BridgeTo;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.BridgeHandler;
|
||||
import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport;
|
||||
import org.springframework.integration.router.RecipientListRouter;
|
||||
import org.springframework.integration.scattergather.ScatterGatherHandler;
|
||||
@@ -58,6 +59,7 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@@ -79,7 +81,6 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
@Test
|
||||
public void testSimpleAuction() {
|
||||
Message<String> quoteMessage = MessageBuilder.withPayload("testQuote").build();
|
||||
|
||||
this.inputAuctionWithoutGatherChannel.send(quoteMessage);
|
||||
Message<?> bestQuoteMessage = this.output.receive(10000);
|
||||
assertThat(bestQuoteMessage).isNotNull();
|
||||
@@ -92,7 +93,6 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
@Test
|
||||
public void testAuctionWithGatherChannel() {
|
||||
Message<String> quoteMessage = MessageBuilder.withPayload("testQuote").build();
|
||||
|
||||
this.inputAuctionWithGatherChannel.send(quoteMessage);
|
||||
Message<?> bestQuoteMessage = this.output.receive(10000);
|
||||
assertThat(bestQuoteMessage).isNotNull();
|
||||
@@ -104,7 +104,6 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
@Test
|
||||
public void testDistribution() {
|
||||
Message<String> quoteMessage = MessageBuilder.withPayload("testQuote").build();
|
||||
|
||||
this.distributionChannel.send(quoteMessage);
|
||||
Message<?> bestQuoteMessage = this.output.receive(10000);
|
||||
assertThat(bestQuoteMessage).isNotNull();
|
||||
@@ -145,51 +144,25 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
new ExpressionEvaluatingReleaseStrategy("size() == 2"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel inputAuctionWithoutGatherChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "inputAuctionWithoutGatherChannel")
|
||||
public MessageHandler scatterGatherAuctionWithoutGatherChannel() {
|
||||
public ScatterGatherHandler scatterGatherAuctionWithoutGatherChannel() {
|
||||
ScatterGatherHandler handler = new ScatterGatherHandler(scatterAuctionWithoutGatherChannel(), gatherer1());
|
||||
handler.setOutputChannel(output());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithoutGatherChannel")
|
||||
public MessageHandler auctionWithoutGatherChannelBridge1() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithoutGatherChannel")
|
||||
public MessageHandler auctionWithoutGatherChannelBridge2() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithoutGatherChannel")
|
||||
public MessageHandler auctionWithoutGatherChannelBridge3() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@BridgeFrom("scatterAuctionWithoutGatherChannel")
|
||||
@BridgeFrom("scatterAuctionWithoutGatherChannel")
|
||||
@BridgeFrom("scatterAuctionWithoutGatherChannel")
|
||||
public MessageChannel serviceChannel1() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "serviceChannel1")
|
||||
public MessageHandler service1() {
|
||||
public AbstractReplyProducingMessageHandler service1() {
|
||||
return new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
@@ -201,7 +174,7 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageHandler distributor() {
|
||||
public RecipientListRouter distributor() {
|
||||
RecipientListRouter router = new RecipientListRouter();
|
||||
router.setApplySequence(true);
|
||||
router.setChannels(Arrays.asList(distributionChannel1(), distributionChannel2(), distributionChannel3()));
|
||||
@@ -209,16 +182,19 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@BridgeTo("serviceChannel1")
|
||||
public MessageChannel distributionChannel1() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@BridgeTo("serviceChannel1")
|
||||
public MessageChannel distributionChannel2() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@BridgeTo("serviceChannel1")
|
||||
public MessageChannel distributionChannel3() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
@@ -230,36 +206,12 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "distributionChannel")
|
||||
public MessageHandler scatterGatherDistribution() {
|
||||
public ScatterGatherHandler scatterGatherDistribution() {
|
||||
ScatterGatherHandler handler = new ScatterGatherHandler(distributor(), gatherer1());
|
||||
handler.setOutputChannel(output());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "distributionChannel1")
|
||||
public MessageHandler distributionBridge1() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "distributionChannel2")
|
||||
public MessageHandler distributionBridge2() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "distributionChannel3")
|
||||
public MessageHandler distributionBridge3() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel1());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel inputAuctionWithGatherChannel() {
|
||||
return new DirectChannel();
|
||||
@@ -288,7 +240,7 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "inputAuctionWithGatherChannel")
|
||||
public MessageHandler scatterGatherAuctionWithGatherChannel() {
|
||||
public ScatterGatherHandler scatterGatherAuctionWithGatherChannel() {
|
||||
ScatterGatherHandler handler =
|
||||
new ScatterGatherHandler(scatterAuctionWithGatherChannel(null), gatherer2());
|
||||
handler.setGatherChannel(gatherChannel());
|
||||
@@ -297,38 +249,10 @@ public class ScatterGatherHandlerIntegrationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithGatherChannel")
|
||||
public MessageHandler auctionWithGatherChannelBridge1() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel2());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithGatherChannel")
|
||||
public MessageHandler auctionWithGatherChannelBridge2() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel2());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithGatherChannel")
|
||||
public MessageHandler auctionWithGatherChannelBridge3() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel2());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "scatterAuctionWithGatherChannel")
|
||||
public MessageHandler auctionWithGatherChannelBridge4() {
|
||||
BridgeHandler handler = new BridgeHandler();
|
||||
handler.setOutputChannel(serviceChannel2());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@BridgeFrom("scatterAuctionWithGatherChannel")
|
||||
@BridgeFrom("scatterAuctionWithGatherChannel")
|
||||
@BridgeFrom("scatterAuctionWithGatherChannel")
|
||||
@BridgeFrom("scatterAuctionWithGatherChannel")
|
||||
public MessageChannel serviceChannel2() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@@ -100,9 +100,7 @@ public class MockMessageSourceTests {
|
||||
@Test
|
||||
public void testMockMessageSourceInConfig() {
|
||||
Lifecycle channelAdapter =
|
||||
this.applicationContext
|
||||
.getBean("mockMessageSourceTests.Config.testingMessageSource.inboundChannelAdapter",
|
||||
Lifecycle.class);
|
||||
this.applicationContext.getBean("testingMessageSource.inboundChannelAdapter", Lifecycle.class);
|
||||
channelAdapter.start();
|
||||
|
||||
Message<?> receive = this.results.receive(10_000);
|
||||
|
||||
@@ -624,7 +624,7 @@ public class MyFlowConfiguration {
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "httpChannel")
|
||||
public MessageHandler httpHandler() {
|
||||
public HttpRequestExecutingMessageHandler httpHandler() {
|
||||
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://foo/service");
|
||||
handler.setExpectedResponseType(String.class);
|
||||
handler.setOutputChannelName("outputChannel");
|
||||
@@ -676,12 +676,15 @@ NOTE: The bean names are generated with the following algorithm:
|
||||
|
||||
* The `MessageHandler` (`MessageSource`) `@Bean` gets its own standard name from the method name or `name` attribute on the `@Bean`.
|
||||
This works as though there were no messaging annotation on the `@Bean` method.
|
||||
* The `AbstractEndpoint` bean name is generated with the following pattern: `[configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName]`.
|
||||
For example, the `SourcePollingChannelAdapter` endpoint for the `consoleSource()` definition <<annotations_on_beans,shown earlier>> gets a bean name of `myFlowConfiguration.consoleSource.inboundChannelAdapter`.
|
||||
* The `AbstractEndpoint` bean name is generated with the following pattern: `[@Bean name].[decapitalizedAnnotationClassShortName]`.
|
||||
For example, the `SourcePollingChannelAdapter` endpoint for the `consoleSource()` definition <<annotations_on_beans,shown earlier>> gets a bean name of `consoleSource.inboundChannelAdapter`.
|
||||
Unlike with POJO methods, the bean method name is not included in the endpoint bean name.
|
||||
See also <<./overview.adoc#endpoint-bean-names,Endpoint Bean Names>>.
|
||||
* If `@Bean` cannot be used directly in the target endpoint (not an instance of a `MessageSource`, `AbstractReplyProducingMessageHandler` or `AbstractMessageRouter`), a respective `AbstractStandardMessageHandlerFactoryBean` is registered to delegate to this `@Bean`.
|
||||
The bean name for this wrapper is generated with the following pattern: `[@Bean name].[decapitalizedAnnotationClassShortName].[handler (or source)]`.
|
||||
|
||||
IMPORTANT: When using these annotations on `@Bean` definitions, the `inputChannel` must reference a declared bean.
|
||||
Channels are not automatically declared in this case.
|
||||
Channels are automatically declared iif not present in the application context yet.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
|
||||
Reference in New Issue
Block a user