Defer Messaging annotations process (#2769)

* Defer Messaging annotations process

The `AbstractMethodAnnotationPostProcessor` and its implementations
have a `beanFactory.getBean()` call for the `@Bean` methods with
Messaging annotations.
This is done, actually, from the
`MessagingAnnotationPostProcessor.postProcessAfterInitialization()`
which might be still too early in some scenarios, like Spring Cloud Feign
with its child application contexts being initialized from the
`FeignClientFactoryBean`, causing a `BeanCurrentlyInCreationException`

See https://stackoverflow.com/questions/54887963/beancurrentlyincreationexception-when-using-spring-integration-with-spring-cloud

* Implement a `SmartInitializingSingleton` for the `MessagingAnnotationPostProcessor`
and gather `Runnable` wrappers for newly introduced `postProcessMethodAndRegisterEndpointIfAny()`
to be called later in the `afterSingletonsInstantiated()` when context is
still in the initialization phase.
All runtime-registered beans are going to be processed normally from the
regular `postProcessAfterInitialization()`

**Cherry-pick to 5.1.x**

* * Fix unused imports in the `MessagingAnnotationsWithBeanAnnotationTests`

* * Fix `IntegrationEndpointsInitializer` in the testing framework to handle
all the possible `AbstractEndpoint` beans registration.
See its JavaDocs for more info
* Fix `AbstractCorrelatingMessageHandlerParser` and
`AbstractConsumerEndpointParser` to use bean names for `outputChannel`
and `discardChannel` instead of bean references.
Since `MessagingAnnotationPostProcessor` now registers endpoints and
beans for channels much later, than parsers, we can't rely on bean
references any more there.
* Fixes for failing tests which expected `outputChannel/discardChannel`
bean references, when it is already just their names for late binding.
* Apply some code style polishing for the affected classes.
* Add `@Nullable` for `MessageSelector` parameter in the `QueueChannel.purge()`
This commit is contained in:
Artem Bilan
2019-03-01 10:26:05 -05:00
committed by Gary Russell
parent 4365eaef6d
commit 3657d05596
27 changed files with 345 additions and 253 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -175,7 +175,7 @@ public class QueueChannel extends AbstractPollableChannel implements QueueChanne
}
@Override
public List<Message<?>> purge(MessageSelector selector) {
public List<Message<?>> purge(@Nullable MessageSelector selector) {
if (selector == null) {
return this.clear();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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,12 +19,15 @@ package org.springframework.integration.channel;
import java.util.List;
import org.springframework.integration.core.MessageSelector;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
/**
* Operations available on a channel that has queuing semantics.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 3.0
*
*/
@@ -32,25 +35,25 @@ public interface QueueChannelOperations {
/**
* Remove all {@link Message Messages} from this channel.
*
* @return The messages that were removed.
*/
List<Message<?>> clear();
/**
* Remove any {@link Message Messages} that are not accepted by the provided selector.
*
* @param selector The message selector.
* @return The list of messages that were purged.
*/
List<Message<?>> purge(MessageSelector selector);
List<Message<?>> purge(@Nullable MessageSelector selector);
/**
* Obtain the current number of queued {@link Message Messages} in this channel.
* @return The current number of queued {@link Message Messages} in this channel.
*/
int getQueueSize();
/**
* Obtain the remaining capacity of this channel.
* @return The remaining capacity of this channel.
*/
int getRemainingCapacity();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -70,6 +70,8 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
private MessageChannel outputChannel;
private String outputChannelName;
private Integer order;
private List<Advice> adviceChain;
@@ -119,6 +121,15 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
this.outputChannel = outputChannel;
}
/**
* Set the handler's output channel name.
* @param outputChannelName the output channel bean name to set.
* @since 5.1.4
*/
public void setOutputChannelName(String outputChannelName) {
this.outputChannelName = outputChannelName;
}
/**
* Set the order in which the handler will be subscribed to its channel
* (when subscribable).
@@ -197,9 +208,7 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
((ApplicationEventPublisherAware) this.handler)
.setApplicationEventPublisher(this.applicationEventPublisher);
}
if (this.handler instanceof MessageProducer && this.outputChannel != null) {
((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
}
configureOutputChannelIfAny();
Object actualHandler = extractTarget(this.handler);
if (actualHandler == null) {
actualHandler = this.handler;
@@ -247,6 +256,18 @@ public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageH
return this.handler;
}
private void configureOutputChannelIfAny() {
if (this.handler instanceof MessageProducer) {
MessageProducer messageProducer = (MessageProducer) this.handler;
if (this.outputChannel != null) {
messageProducer.setOutputChannel(this.outputChannel);
}
else if (this.outputChannelName != null) {
messageProducer.setOutputChannelName(this.outputChannelName);
}
}
}
protected abstract H createHandler();
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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,6 +18,7 @@ package org.springframework.integration.config.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,6 +37,7 @@ 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.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -71,7 +73,8 @@ import org.springframework.util.StringUtils;
* @author Gary Russell
* @author Rick Hogge
*/
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean {
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean,
SmartInitializingSingleton {
protected final Log logger = LogFactory.getLog(this.getClass()); // NOSONAR
@@ -81,6 +84,10 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
private final Set<Class<?>> noAnnotationsCache = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
private final List<Runnable> methodsToPostProcessAfterContextInitialization = new ArrayList<>();
private volatile boolean initialized;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isAssignable(ConfigurableListableBeanFactory.class, beanFactory.getClass(),
@@ -122,9 +129,17 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
public <A extends Annotation> void addMessagingAnnotationPostProcessor(Class<A> annotation,
MethodAnnotationPostProcessor<A> postProcessor) {
this.postProcessors.put(annotation, postProcessor);
}
@Override
public void afterSingletonsInstantiated() {
this.initialized = true;
this.methodsToPostProcessAfterContextInitialization.forEach(Runnable::run);
this.methodsToPostProcessAfterContextInitialization.clear();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
@@ -141,38 +156,43 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
return bean;
}
ReflectionUtils.doWithMethods(beanClass, method -> {
Map<Class<? extends Annotation>, List<Annotation>> annotationChains = new HashMap<>();
for (Class<? extends Annotation> annotationType :
this.postProcessors.keySet()) {
if (AnnotatedElementUtils.isAnnotated(method, annotationType.getName())) {
List<Annotation> annotationChain = getAnnotationChain(method, annotationType);
if (annotationChain.size() > 0) {
annotationChains.put(annotationType, annotationChain);
}
}
}
if (StringUtils.hasText(MessagingAnnotationUtils.endpointIdValue(method))
&& annotationChains.keySet().size() > 1) {
throw new IllegalStateException("@EndpointId on " + method.toGenericString()
+ " can only have one EIP annotation, found: " + annotationChains.keySet().size());
}
for (Entry<Class<? extends Annotation>, List<Annotation>> entry : annotationChains.entrySet()) {
Class<? extends Annotation> annotationType = entry.getKey();
List<Annotation> annotations = entry.getValue();
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotations);
}
if (annotationChains.size() == 0) {
this.noAnnotationsCache.add(beanClass);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
ReflectionUtils.doWithMethods(beanClass,
method -> doWithMethod(method, bean, beanName, beanClass),
ReflectionUtils.USER_DECLARED_METHODS);
return bean;
}
private void doWithMethod(Method method, Object bean, String beanName, Class<?> beanClass) {
Map<Class<? extends Annotation>, List<Annotation>> annotationChains = new HashMap<>();
for (Class<? extends Annotation> annotationType :
this.postProcessors.keySet()) {
if (AnnotatedElementUtils.isAnnotated(method, annotationType.getName())) {
List<Annotation> annotationChain = getAnnotationChain(method, annotationType);
if (annotationChain.size() > 0) {
annotationChains.put(annotationType, annotationChain);
}
}
}
if (StringUtils.hasText(MessagingAnnotationUtils.endpointIdValue(method))
&& annotationChains.keySet().size() > 1) {
throw new IllegalStateException("@EndpointId on " + method.toGenericString()
+ " can only have one EIP annotation, found: " + annotationChains.keySet().size());
}
for (Entry<Class<? extends Annotation>, List<Annotation>> entry : annotationChains.entrySet()) {
Class<? extends Annotation> annotationType = entry.getKey();
List<Annotation> annotations = entry.getValue();
processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotations);
}
if (annotationChains.size() == 0) {
this.noAnnotationsCache.add(beanClass);
}
}
protected void processAnnotationTypeOnMethod(Object bean, String beanName, Method method,
Class<? extends Annotation> annotationType, List<Annotation> annotations) {
MethodAnnotationPostProcessor<?> postProcessor =
MessagingAnnotationPostProcessor.this.postProcessors.get(annotationType);
if (postProcessor != null && postProcessor.shouldCreateEndpoint(method, annotations)) {
@@ -187,36 +207,53 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
+ "and its method: '" + method + "'", e);
}
}
Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations);
if (result != null && result instanceof AbstractEndpoint) {
AbstractEndpoint endpoint = (AbstractEndpoint) result;
String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup",
String.class);
if (StringUtils.hasText(autoStartup)) {
autoStartup = getBeanFactory().resolveEmbeddedValue(autoStartup);
if (StringUtils.hasText(autoStartup)) {
endpoint.setAutoStartup(Boolean.parseBoolean(autoStartup));
}
}
String phase = MessagingAnnotationUtils.resolveAttribute(annotations, "phase", String.class);
if (StringUtils.hasText(phase)) {
phase = getBeanFactory().resolveEmbeddedValue(phase);
if (StringUtils.hasText(phase)) {
endpoint.setPhase(Integer.parseInt(phase));
}
}
Role role = AnnotationUtils.findAnnotation(method, Role.class);
if (role != null) {
endpoint.setRole(role.value());
}
String endpointBeanName = generateBeanName(beanName, method, annotationType);
endpoint.setBeanName(endpointBeanName);
getBeanFactory().registerSingleton(endpointBeanName, endpoint);
getBeanFactory().initializeBean(endpoint, endpointBeanName);
if (this.initialized) {
postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations,
postProcessor, targetMethod);
}
else {
Method methodToPostProcess = targetMethod;
this.methodsToPostProcessAfterContextInitialization.add(() ->
postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations,
postProcessor, methodToPostProcess));
}
}
}
private void postProcessMethodAndRegisterEndpointIfAny(Object bean, String beanName, Method method,
Class<? extends Annotation> annotationType, List<Annotation> annotations,
MethodAnnotationPostProcessor<?> postProcessor, Method targetMethod) {
Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations);
if (result instanceof AbstractEndpoint) {
AbstractEndpoint endpoint = (AbstractEndpoint) result;
String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup",
String.class);
if (StringUtils.hasText(autoStartup)) {
autoStartup = getBeanFactory().resolveEmbeddedValue(autoStartup);
if (StringUtils.hasText(autoStartup)) {
endpoint.setAutoStartup(Boolean.parseBoolean(autoStartup));
}
}
String phase = MessagingAnnotationUtils.resolveAttribute(annotations, "phase", String.class);
if (StringUtils.hasText(phase)) {
phase = getBeanFactory().resolveEmbeddedValue(phase);
if (StringUtils.hasText(phase)) {
endpoint.setPhase(Integer.parseInt(phase));
}
}
Role role = AnnotationUtils.findAnnotation(method, Role.class);
if (role != null) {
endpoint.setRole(role.value());
}
String endpointBeanName = generateBeanName(beanName, method, annotationType);
endpoint.setBeanName(endpointBeanName);
getBeanFactory().registerSingleton(endpointBeanName, endpoint);
getBeanFactory().initializeBean(endpoint, endpointBeanName);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -84,8 +84,9 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder handlerBuilder = this.parseHandler(element, parserContext);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(handlerBuilder, element, "output-channel");
BeanDefinitionBuilder handlerBuilder = parseHandler(element, parserContext);
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "output-channel",
"outputChannelName");
IntegrationNamespaceUtils.setValueIfAttributeDefined(handlerBuilder, element, "order");
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
@@ -78,8 +78,9 @@ public abstract class AbstractCorrelatingMessageHandlerParser extends AbstractCo
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, MESSAGE_STORE_ATTRIBUTE);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "scheduler", "taskScheduler");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, DISCARD_CHANNEL_ATTRIBUTE);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "lock-registry");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, DISCARD_CHANNEL_ATTRIBUTE,
"discardChannelName");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, SEND_TIMEOUT_ATTRIBUTE);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, SEND_PARTIAL_RESULT_ON_EXPIRY_ATTRIBUTE);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "empty-group-min-timeout",

View File

@@ -17,6 +17,7 @@
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;
@@ -81,6 +82,7 @@ public class DirectChannelSubscriptionTests {
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(this.context.getBeanFactory());
postProcessor.afterPropertiesSet();
postProcessor.afterSingletonsInstantiated();
TestEndpoint endpoint = new TestEndpoint();
postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint");
this.context.refresh();
@@ -105,17 +107,19 @@ public class DirectChannelSubscriptionTests {
this.sourceChannel.send(new GenericMessage<>("foo"));
}
@Test(expected = MessagingException.class)
@Test
public void exceptionThrownFromAnnotatedEndpoint() {
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.afterSingletonsInstantiated();
FailingTestEndpoint endpoint = new FailingTestEndpoint();
postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint");
this.context.refresh();
this.sourceChannel.send(new GenericMessage<>("foo"));
assertThatExceptionOfType(MessagingException.class)
.isThrownBy(() -> this.sourceChannel.send(new GenericMessage<>("foo")));
}

View File

@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.DirectFieldAccessor;
@@ -38,6 +38,7 @@ import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrateg
import org.springframework.integration.dispatcher.UnicastingDispatcher;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@@ -48,18 +49,19 @@ import org.springframework.util.ReflectionUtils;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*/
public class DirectChannelTests {
class DirectChannelTests {
@Test
public void testSend() {
void testSend() {
DirectChannel channel = new DirectChannel();
Log logger = spy(TestUtils.getPropertyValue(channel, "logger", Log.class));
when(logger.isDebugEnabled()).thenReturn(true);
new DirectFieldAccessor(channel).setPropertyValue("logger", logger);
ThreadNameExtractingTestTarget target = new ThreadNameExtractingTestTarget();
channel.subscribe(target);
GenericMessage<String> message = new GenericMessage<String>("test");
GenericMessage<String> message = new GenericMessage<>("test");
assertThat(channel.send(message)).isTrue();
assertThat(target.threadName).isEqualTo(Thread.currentThread().getName());
DirectFieldAccessor channelAccessor = new DirectFieldAccessor(channel);
@@ -76,7 +78,7 @@ public class DirectChannelTests {
}
@Test
public void testSendPerfOneHandler() {
void testSendPerfOneHandler() {
/*
* INT-3308 - used to run 12 million/sec
* 1. optimize for single handler 20 million/sec
@@ -89,7 +91,7 @@ public class DirectChannelTests {
DirectChannel channel = new DirectChannel();
final AtomicInteger count = new AtomicInteger();
channel.subscribe(message -> count.incrementAndGet());
GenericMessage<String> message = new GenericMessage<String>("test");
GenericMessage<String> message = new GenericMessage<>("test");
assertThat(channel.send(message)).isTrue();
for (int i = 0; i < 10000000; i++) {
channel.send(message);
@@ -97,7 +99,7 @@ public class DirectChannelTests {
}
@Test
public void testSendPerfTwoHandlers() {
void testSendPerfTwoHandlers() {
/*
* INT-3308 - used to run 6.4 million/sec
* 1. Skip empty iterators as above 7.2 million/sec
@@ -110,7 +112,7 @@ public class DirectChannelTests {
final AtomicInteger count2 = new AtomicInteger();
channel.subscribe(message -> count1.incrementAndGet());
channel.subscribe(message -> count2.getAndIncrement());
GenericMessage<String> message = new GenericMessage<String>("test");
GenericMessage<String> message = new GenericMessage<>("test");
assertThat(channel.send(message)).isTrue();
for (int i = 0; i < 10000000; i++) {
channel.send(message);
@@ -120,7 +122,7 @@ public class DirectChannelTests {
}
@Test
public void testSendPerfFixedSubscriberChannel() {
void testSendPerfFixedSubscriberChannel() {
/*
* INT-3308 - 96 million/sec
* NOTE: in order to get a measurable time, I had to add some code to the handler -
@@ -130,7 +132,7 @@ public class DirectChannelTests {
*/
final AtomicInteger count = new AtomicInteger();
FixedSubscriberChannel channel = new FixedSubscriberChannel(message -> count.incrementAndGet());
GenericMessage<String> message = new GenericMessage<String>("test");
GenericMessage<String> message = new GenericMessage<>("test");
assertThat(channel.send(message)).isTrue();
for (int i = 0; i < 100000000; i++) {
channel.send(message, 0);
@@ -138,26 +140,27 @@ public class DirectChannelTests {
}
@Test
public void testSendInSeparateThread() throws InterruptedException {
void testSendInSeparateThread() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
final DirectChannel channel = new DirectChannel();
ThreadNameExtractingTestTarget target = new ThreadNameExtractingTestTarget(latch);
channel.subscribe(target);
final GenericMessage<String> message = new GenericMessage<String>("test");
final GenericMessage<String> message = new GenericMessage<>("test");
new Thread((Runnable) () -> channel.send(message), "test-thread").start();
latch.await(1000, TimeUnit.MILLISECONDS);
assertThat(target.threadName).isEqualTo("test-thread");
}
@Test // See INT-2434
public void testChannelCreationWithBeanDefinitionOverrideTrue() throws Exception {
ClassPathXmlApplicationContext parentContext = new ClassPathXmlApplicationContext("parent-config.xml", this.getClass());
@Test
void testChannelCreationWithBeanDefinitionOverrideTrue() throws Exception {
ClassPathXmlApplicationContext parentContext =
new ClassPathXmlApplicationContext("parent-config.xml", getClass());
MessageChannel parentChannelA = parentContext.getBean("parentChannelA", MessageChannel.class);
MessageChannel parentChannelB = parentContext.getBean("parentChannelB", MessageChannel.class);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
context.setConfigLocations(new String[]{"classpath:org/springframework/integration/channel/channel-override-config.xml"});
context.setConfigLocations("classpath:org/springframework/integration/channel/channel-override-config.xml");
context.setParent(parentContext);
Method method = ReflectionUtils.findMethod(ClassPathXmlApplicationContext.class, "obtainFreshBeanFactory");
method.setAccessible(true);
@@ -177,28 +180,28 @@ public class DirectChannelTests {
assertThat(context.containsBean("channelD")).isTrue();
EventDrivenConsumer consumerA = context.getBean("serviceA", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerA, "inputChannel")).isEqualTo(context.getBean("channelA"));
assertThat(TestUtils.getPropertyValue(consumerA, "handler.outputChannel"))
.isEqualTo(context.getBean("channelB"));
assertThat(TestUtils.getPropertyValue(consumerA, "handler.outputChannelName")).isEqualTo("channelB");
EventDrivenConsumer consumerB = context.getBean("serviceB", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerB, "inputChannel")).isEqualTo(context.getBean("channelB"));
assertThat(TestUtils.getPropertyValue(consumerB, "handler.outputChannel"))
.isEqualTo(context.getBean("channelC"));
assertThat(TestUtils.getPropertyValue(consumerB, "handler.outputChannelName")).isEqualTo("channelC");
EventDrivenConsumer consumerC = context.getBean("serviceC", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerC, "inputChannel")).isEqualTo(context.getBean("channelC"));
assertThat(TestUtils.getPropertyValue(consumerC, "handler.outputChannel"))
.isEqualTo(context.getBean("channelD"));
assertThat(TestUtils.getPropertyValue(consumerC, "handler.outputChannelName")).isEqualTo("channelD");
EventDrivenConsumer consumerD = context.getBean("serviceD", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerD, "inputChannel")).isEqualTo(parentChannelA);
assertThat(TestUtils.getPropertyValue(consumerD, "handler.outputChannel")).isEqualTo(parentChannelB);
assertThat(TestUtils.getPropertyValue(consumerD, "handler.outputChannelName")).isEqualTo("parentChannelB");
EventDrivenConsumer consumerE = context.getBean("serviceE", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerE, "inputChannel")).isEqualTo(parentChannelB);
EventDrivenConsumer consumerF = context.getBean("serviceF", EventDrivenConsumer.class);
assertThat(TestUtils.getPropertyValue(consumerF, "inputChannel")).isEqualTo(channelEarly);
context.close();
parentContext.close();
}
@@ -213,7 +216,7 @@ public class DirectChannelTests {
this(null);
}
ThreadNameExtractingTestTarget(CountDownLatch latch) {
ThreadNameExtractingTestTarget(@Nullable CountDownLatch latch) {
this.latch = latch;
}
@@ -224,6 +227,7 @@ public class DirectChannelTests {
this.latch.countDown();
}
}
}
}

View File

@@ -153,8 +153,6 @@ public class AggregatorParserTests {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("completelyDefinedAggregator");
ReleaseStrategy releaseStrategy = (ReleaseStrategy) context.getBean("releaseStrategy");
CorrelationStrategy correlationStrategy = (CorrelationStrategy) context.getBean("correlationStrategy");
MessageChannel outputChannel = (MessageChannel) context.getBean("outputChannel");
MessageChannel discardChannel = (MessageChannel) context.getBean("discardChannel");
Object consumer = new DirectFieldAccessor(endpoint).getPropertyValue("handler");
assertThat(consumer).isInstanceOf(AggregatingMessageHandler.class);
DirectFieldAccessor accessor = new DirectFieldAccessor(consumer);
@@ -172,12 +170,12 @@ public class AggregatorParserTests {
assertThat(accessor.getPropertyValue("correlationStrategy"))
.as("The AggregatorEndpoint is not injected with the appropriate CorrelationStrategy instance")
.isEqualTo(correlationStrategy);
assertThat(accessor.getPropertyValue("outputChannel"))
assertThat(accessor.getPropertyValue("outputChannelName"))
.as("The AggregatorEndpoint is not injected with the appropriate output channel")
.isEqualTo(outputChannel);
assertThat(accessor.getPropertyValue("discardChannel"))
.isEqualTo("outputChannel");
assertThat(accessor.getPropertyValue("discardChannelName"))
.as("The AggregatorEndpoint is not injected with the appropriate discard channel")
.isEqualTo(discardChannel);
.isEqualTo("discardChannel");
assertThat(TestUtils.getPropertyValue(consumer, "messagingTemplate.sendTimeout"))
.as("The AggregatorEndpoint is not set with the appropriate timeout value").isEqualTo(86420000L);
assertThat(accessor.getPropertyValue("sendPartialResultOnExpiry"))

View File

@@ -21,11 +21,10 @@ import static org.springframework.integration.test.util.TestUtils.getPropertyVal
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.aggregator.CorrelationStrategy;
import org.springframework.integration.aggregator.MethodInvokingCorrelationStrategy;
import org.springframework.integration.aggregator.MethodInvokingReleaseStrategy;
@@ -38,7 +37,7 @@ import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Marius Bogoevici
@@ -49,17 +48,14 @@ import org.springframework.messaging.MessageChannel;
* @author Artem Bilan
* @author Gary Russell
*/
@SpringJUnitConfig
public class ResequencerParserTests {
@Autowired
private ApplicationContext context;
@Before
public void setUp() {
this.context = new ClassPathXmlApplicationContext("resequencerParserTests.xml", this.getClass());
}
@Test
public void testDefaultResequencerProperties() {
void testDefaultResequencerProperties() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("defaultResequencer");
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
@@ -69,7 +65,9 @@ public class ResequencerParserTests {
resequencer, "messagingTemplate.sendTimeout"))
.as("The ResequencerEndpoint is not set with the appropriate timeout value").isEqualTo(-1L);
assertThat(getPropertyValue(resequencer, "sendPartialResultOnExpiry"))
.as("The ResequencerEndpoint is not configured with the appropriate 'send partial results on timeout' " +
.as("The ResequencerEndpoint is not configured with the appropriate 'send partial results on " +
"timeout'" +
" " +
"flag")
.isEqualTo(false);
assertThat(getPropertyValue(resequencer, "releasePartialSequences"))
@@ -78,23 +76,21 @@ public class ResequencerParserTests {
}
@Test
public void testPropertyAssignment() throws Exception {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("completelyDefinedResequencer");
MessageChannel outputChannel = (MessageChannel) context.getBean("outputChannel");
MessageChannel discardChannel = (MessageChannel) context.getBean("discardChannel");
void testPropertyAssignment() {
EventDrivenConsumer endpoint = this.context.getBean("completelyDefinedResequencer", EventDrivenConsumer.class);
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
assertThat(getPropertyValue(resequencer, "outputChannel"))
assertThat(getPropertyValue(resequencer, "outputChannelName"))
.as("The ResequencerEndpoint is not injected with the appropriate output channel")
.isEqualTo(outputChannel);
assertThat(getPropertyValue(resequencer, "discardChannel"))
.isEqualTo("outputChannel");
assertThat(getPropertyValue(resequencer, "discardChannelName"))
.as("The ResequencerEndpoint is not injected with the appropriate discard channel")
.isEqualTo(discardChannel);
.isEqualTo("discardChannel");
assertThat(getPropertyValue(resequencer, "messagingTemplate.sendTimeout"))
.as("The ResequencerEndpoint is not set with the appropriate timeout value").isEqualTo(86420000L);
assertThat(getPropertyValue(resequencer, "sendPartialResultOnExpiry"))
.as("The ResequencerEndpoint is not configured with the appropriate 'send partial results on timeout' " +
"flag")
.as("The ResequencerEndpoint is not configured with the appropriate " +
"'send partial results on timeout' flag")
.isEqualTo(true);
assertThat(getPropertyValue(getPropertyValue(resequencer, "releaseStrategy"), "releasePartialSequences"))
.as("The ResequencerEndpoint is not configured with the appropriate 'release partial sequences' flag")
@@ -104,7 +100,7 @@ public class ResequencerParserTests {
}
@Test
public void testCorrelationStrategyRefOnly() throws Exception {
void testCorrelationStrategyRefOnly() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context
.getBean("resequencerWithCorrelationStrategyRefOnly");
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
@@ -116,7 +112,7 @@ public class ResequencerParserTests {
}
@Test
public void testReleaseStrategyRefOnly() throws Exception {
void testReleaseStrategyRefOnly() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("resequencerWithReleaseStrategyRefOnly");
ResequencingMessageHandler resequencer = getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
@@ -127,7 +123,7 @@ public class ResequencerParserTests {
}
@Test
public void testReleaseStrategyRefAndMethod() throws Exception {
void testReleaseStrategyRefAndMethod() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context
.getBean("resequencerWithReleaseStrategyRefAndMethod");
ResequencingMessageHandler resequencer = getPropertyValue(endpoint, "handler",
@@ -150,17 +146,17 @@ public class ResequencerParserTests {
}
@Test
public void shouldSetReleasePartialSequencesFlag() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("completelyDefinedResequencer");
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
void shouldSetReleasePartialSequencesFlag() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("completelyDefinedResequencer");
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
ResequencingMessageHandler.class);
assertThat(getPropertyValue(getPropertyValue(resequencer, "releaseStrategy"), "releasePartialSequences"))
.as("The ResequencerEndpoint is not configured with the appropriate 'release partial sequences' flag")
.isEqualTo(true);
}
@Test
public void testCorrelationStrategyRefAndMethod() throws Exception {
void testCorrelationStrategyRefAndMethod() {
EventDrivenConsumer endpoint = (EventDrivenConsumer) context
.getBean("resequencerWithCorrelationStrategyRefAndMethod");
ResequencingMessageHandler resequencer = TestUtils.getPropertyValue(endpoint, "handler",
@@ -173,19 +169,13 @@ public class ResequencerParserTests {
assertThat(adapter.getCorrelationKey(MessageBuilder.withPayload("not important").build())).isEqualTo("foo");
}
@SuppressWarnings("unused")
private static <T> Message<T> createMessage(T payload, Object correlationId, int sequenceSize, int sequenceNumber,
MessageChannel outputChannel) {
return MessageBuilder.withPayload(payload).setCorrelationId(correlationId).setSequenceSize(sequenceSize)
.setSequenceNumber(sequenceNumber).setReplyChannel(outputChannel).build();
}
static class TestCorrelationStrategy implements CorrelationStrategy {
@Override
public Object getCorrelationKey(Message<?> message) {
return "test";
}
}
static class TestCorrelationStrategyPojo {
@@ -193,22 +183,27 @@ public class ResequencerParserTests {
public Object foo(Object o) {
return "foo";
}
}
static class TestReleaseStrategy implements ReleaseStrategy {
@Override
public boolean canRelease(MessageGroup group) {
return true;
}
}
static class TestReleaseStrategyPojo {
private int invocationCount = 0;
public boolean bar(List<Message<?>> messages) {
public boolean bar(List<Message<?>> __) {
invocationCount++;
return true;
}
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import java.util.Collections;
import java.util.List;
@@ -55,10 +56,11 @@ public class FilterAnnotationPostProcessorTests {
@Before
public void init() {
context.registerChannel("input", inputChannel);
context.registerChannel("output", outputChannel);
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
this.context.registerChannel("input", this.inputChannel);
this.context.registerChannel("output", this.outputChannel);
this.postProcessor.setBeanFactory(this.context.getBeanFactory());
this.postProcessor.afterPropertiesSet();
this.postProcessor.afterSingletonsInstantiated();
}
@After
@@ -169,16 +171,18 @@ public class FilterAnnotationPostProcessorTests {
testValidFilter(new TestFilterWithBooleanWrapperClass());
}
@Test(expected = IllegalArgumentException.class)
@Test
public void invalidMethodWithStringReturnType() {
Object filter = new TestFilterWithStringReturnType();
postProcessor.postProcessAfterInitialization(filter, "testFilter");
assertThatIllegalArgumentException()
.isThrownBy(() -> this.postProcessor.postProcessAfterInitialization(filter, "testFilter"));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void invalidMethodWithVoidReturnType() {
Object filter = new TestFilterWithVoidReturnType();
postProcessor.postProcessAfterInitialization(filter, "testFilter");
assertThatIllegalArgumentException()
.isThrownBy(() -> postProcessor.postProcessAfterInitialization(filter, "testFilter"));
}

View File

@@ -16,8 +16,7 @@
package org.springframework.integration.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@@ -40,6 +39,8 @@ import org.springframework.messaging.core.DestinationResolutionException;
/**
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.3.8
*
*/
@@ -57,6 +58,7 @@ public class MessagingAnnotationPostProcessorChannelCreationTests {
MessagingAnnotationPostProcessor mapp = new MessagingAnnotationPostProcessor();
mapp.setBeanFactory(beanFactory);
mapp.afterPropertiesSet();
mapp.afterSingletonsInstantiated();
mapp.postProcessAfterInitialization(new Foo(), "foo");
verify(beanFactory).registerSingleton(eq("channel"), any(DirectChannel.class));
}
@@ -73,14 +75,10 @@ public class MessagingAnnotationPostProcessorChannelCreationTests {
MessagingAnnotationPostProcessor mapp = new MessagingAnnotationPostProcessor();
mapp.setBeanFactory(beanFactory);
mapp.afterPropertiesSet();
try {
mapp.postProcessAfterInitialization(new Foo(), "foo");
fail("Expected a DestinationResolutionException");
}
catch (DestinationResolutionException e) {
assertThat(e.getMessage())
.contains("A bean definition with name 'channel' exists, but failed to be created");
}
mapp.afterSingletonsInstantiated();
assertThatExceptionOfType(DestinationResolutionException.class)
.isThrownBy(() -> mapp.postProcessAfterInitialization(new Foo(), "foo"))
.withMessageContaining("A bean definition with name 'channel' exists, but failed to be created");
}
public static class Foo {

View File

@@ -17,6 +17,7 @@
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;
@@ -29,6 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.annotation.MessageEndpoint;
@@ -62,9 +64,7 @@ public class MessagingAnnotationPostProcessorTests {
context.registerChannel("inputChannel", inputChannel);
context.refresh();
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
ServiceActivatorAnnotatedBean bean = new ServiceActivatorAnnotatedBean();
postProcessor.postProcessAfterInitialization(bean, "testBean");
assertThat(context.containsBean("testBean.test.serviceActivator")).isTrue();
@@ -79,7 +79,7 @@ public class MessagingAnnotationPostProcessorTests {
"serviceActivatorAnnotationPostProcessorTests.xml", this.getClass());
MessageChannel inputChannel = (MessageChannel) context.getBean("inputChannel");
PollableChannel outputChannel = (PollableChannel) context.getBean("outputChannel");
inputChannel.send(new GenericMessage<String>("world"));
inputChannel.send(new GenericMessage<>("world"));
Message<?> reply = outputChannel.receive(0);
assertThat(reply.getPayload()).isEqualTo("hello world");
context.close();
@@ -87,13 +87,12 @@ public class MessagingAnnotationPostProcessorTests {
@Test
public void testSimpleHandler() {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("simpleAnnotatedEndpointTests.xml",
this
.getClass());
AbstractApplicationContext context =
new ClassPathXmlApplicationContext("simpleAnnotatedEndpointTests.xml", getClass());
context.start();
MessageChannel inputChannel = (MessageChannel) context.getBean("inputChannel");
PollableChannel outputChannel = (PollableChannel) context.getBean("outputChannel");
GenericMessage<String> messageToSend = new GenericMessage<String>("world");
GenericMessage<String> messageToSend = new GenericMessage<>("world");
inputChannel.send(messageToSend);
Message<?> message = outputChannel.receive(1000);
assertThat(message.getPayload()).isEqualTo("hello world");
@@ -136,27 +135,26 @@ public class MessagingAnnotationPostProcessorTests {
public void outboundOnlyServiceActivator() throws InterruptedException {
TestApplicationContext context = TestUtils.createTestApplicationContext();
context.registerChannel("testChannel", new DirectChannel());
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
CountDownLatch latch = new CountDownLatch(1);
OutboundOnlyTestBean testBean = new OutboundOnlyTestBean(latch);
postProcessor.postProcessAfterInitialization(testBean, "testBean");
context.refresh();
DestinationResolver<MessageChannel> channelResolver = new BeanFactoryChannelResolver(context);
MessageChannel testChannel = channelResolver.resolveDestination("testChannel");
testChannel.send(new GenericMessage<String>("foo"));
testChannel.send(new GenericMessage<>("foo"));
latch.await(1000, TimeUnit.MILLISECONDS);
assertThat(latch.getCount()).isEqualTo(0);
assertThat(testBean.getMessageText()).isEqualTo("foo");
context.close();
}
@Test(expected = IllegalArgumentException.class)
@Test
public void testPostProcessorWithoutBeanFactory() {
MessagingAnnotationPostProcessor postProcessor =
new MessagingAnnotationPostProcessor();
postProcessor.afterPropertiesSet();
assertThatIllegalArgumentException()
.isThrownBy(postProcessor::afterPropertiesSet);
}
@Test
@@ -168,9 +166,7 @@ public class MessagingAnnotationPostProcessorTests {
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
context.registerChannel("eventBus", eventBus);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
ServiceActivatorAnnotatedBean bean = new ServiceActivatorAnnotatedBean();
postProcessor.postProcessAfterInitialization(bean, "testBean");
context.refresh();
@@ -193,9 +189,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
ProxyFactory proxyFactory = new ProxyFactory(new AnnotatedTestService());
Object proxy = proxyFactory.getProxy();
postProcessor.postProcessAfterInitialization(proxy, "proxy");
@@ -213,9 +207,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointSubclass(), "subclass");
context.refresh();
inputChannel.send(new GenericMessage<>("world"));
@@ -231,9 +223,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
ProxyFactory proxyFactory = new ProxyFactory(new SimpleAnnotatedEndpointSubclass());
Object proxy = proxyFactory.getProxy();
postProcessor.postProcessAfterInitialization(proxy, "proxy");
@@ -251,9 +241,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointImplementation(), "impl");
context.refresh();
inputChannel.send(new GenericMessage<>("ABC"));
@@ -269,9 +257,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointImplementation(), "impl");
context.refresh();
inputChannel.send(new GenericMessage<>("ABC"));
@@ -287,9 +273,7 @@ public class MessagingAnnotationPostProcessorTests {
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("inputChannel", inputChannel);
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
ProxyFactory proxyFactory = new ProxyFactory(new SimpleAnnotatedEndpointImplementation());
Object proxy = proxyFactory.getProxy();
postProcessor.postProcessAfterInitialization(proxy, "proxy");
@@ -307,9 +291,7 @@ public class MessagingAnnotationPostProcessorTests {
context.registerChannel("inputChannel", inputChannel);
QueueChannel outputChannel = new QueueChannel();
context.registerChannel("outputChannel", outputChannel);
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context);
TransformerAnnotationTestBean testBean = new TransformerAnnotationTestBean();
postProcessor.postProcessAfterInitialization(testBean, "testBean");
context.refresh();
@@ -319,6 +301,15 @@ public class MessagingAnnotationPostProcessorTests {
context.close();
}
private MessagingAnnotationPostProcessor prepareMessagingAnnotationPostProcessor(
ConfigurableApplicationContext context) {
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
postProcessor.afterSingletonsInstantiated();
return postProcessor;
}
@MessageEndpoint
public static class OutboundOnlyTestBean {

View File

@@ -17,7 +17,7 @@
package org.springframework.integration.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.ArrayList;
import java.util.List;
@@ -32,7 +32,6 @@ import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
@@ -78,7 +77,7 @@ import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Artem Bilan
@@ -88,7 +87,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @since 4.0
*/
@ContextConfiguration(classes = MessagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.class)
@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@DirtiesContext
public class MessagingAnnotationsWithBeanAnnotationTests {
@@ -102,7 +101,8 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
private List<Message<?>> collector;
@Autowired(required = false)
@Qualifier("messagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.skippedMessageHandler.serviceActivator")
@Qualifier(
"messagingAnnotationsWithBeanAnnotationTests.ContextConfiguration.skippedMessageHandler.serviceActivator")
private EventDrivenConsumer skippedServiceActivator;
@Autowired(required = false)
@@ -217,15 +217,9 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Test
public void testInvalidMessagingAnnotationsConfig() {
try {
new AnnotationConfigApplicationContext(InvalidContextConfiguration.class).close();
fail("BeanCreationException expected");
}
catch (Exception e) {
assertThat(e).isInstanceOf(BeanCreationException.class);
assertThat(e.getCause()).isInstanceOf(BeanDefinitionValidationException.class);
assertThat(e.getMessage()).contains("The attribute causing the ambiguity is: [applySequence].");
}
assertThatExceptionOfType(BeanDefinitionValidationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(InvalidContextConfiguration.class))
.withMessageContaining("The attribute causing the ambiguity is: [applySequence].");
}
@Configuration

View File

@@ -72,6 +72,7 @@ public class RouterAnnotationPostProcessorTests {
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
postProcessor.afterSingletonsInstantiated();
TestRouter testRouter = new TestRouter();
postProcessor.postProcessAfterInitialization(testRouter, "test");
context.refresh();
@@ -86,6 +87,7 @@ public class RouterAnnotationPostProcessorTests {
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
postProcessor.afterSingletonsInstantiated();
TestRouter testRouter = new TestRouter();
postProcessor.postProcessAfterInitialization(testRouter, "test");
context.refresh();

View File

@@ -62,6 +62,7 @@ public class SplitterAnnotationPostProcessorTests {
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor();
postProcessor.setBeanFactory(context.getBeanFactory());
postProcessor.afterPropertiesSet();
postProcessor.afterSingletonsInstantiated();
TestSplitter splitter = new TestSplitter();
postProcessor.postProcessAfterInitialization(splitter, "testSplitter");
context.refresh();

View File

@@ -48,6 +48,7 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
* @author Artem Bilan
* @author Gunnar Hillert
* @author Gary Russell
*
* @since 1.0.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -61,7 +62,7 @@ public class DelayerParserTests {
public void defaultScheduler() {
DelayHandler delayHandler = context.getBean("delayerWithDefaultScheduler.handler", DelayHandler.class);
assertThat(delayHandler.getOrder()).isEqualTo(99);
assertThat(TestUtils.getPropertyValue(delayHandler, "outputChannel")).isEqualTo(context.getBean("output"));
assertThat(delayHandler.getOutputChannel()).isSameAs(this.context.getBean("output"));
assertThat(TestUtils.getPropertyValue(delayHandler, "defaultDelay", Long.class)).isEqualTo(new Long(1234));
//INT-2243
assertThat(TestUtils.getPropertyValue(delayHandler, "delayExpression")).isNotNull();
@@ -81,8 +82,8 @@ public class DelayerParserTests {
DelayHandler delayHandler = (DelayHandler) handler;
assertThat(delayHandler.getOrder()).isEqualTo(Ordered.LOWEST_PRECEDENCE);
DirectFieldAccessor accessor = new DirectFieldAccessor(delayHandler);
assertThat(accessor.getPropertyValue("outputChannel")).isEqualTo(context.getBean("output"));
assertThat(accessor.getPropertyValue("defaultDelay")).isEqualTo(new Long(0));
assertThat(delayHandler.getOutputChannel()).isSameAs(this.context.getBean("output"));
assertThat(accessor.getPropertyValue("defaultDelay")).isEqualTo(0L);
assertThat(accessor.getPropertyValue("taskScheduler")).isEqualTo(context.getBean("testScheduler"));
assertThat(accessor.getPropertyValue("taskScheduler")).isNotNull();
assertThat(new DirectFieldAccessor(

View File

@@ -71,7 +71,7 @@ public class EnricherParserTests {
ContentEnricher enricher = (ContentEnricher) handler;
assertThat(enricher.getOrder()).isEqualTo(99);
DirectFieldAccessor accessor = new DirectFieldAccessor(enricher);
assertThat(accessor.getPropertyValue("outputChannel")).isEqualTo(context.getBean("output"));
assertThat(accessor.getPropertyValue("outputChannelName")).isEqualTo("output");
assertThat(accessor.getPropertyValue("shouldClonePayload")).isEqualTo(true);
assertThat(accessor.getPropertyValue("requestPayloadExpression")).isNull();
assertThat(TestUtils.getPropertyValue(enricher, "gateway.beanFactory")).isNotNull();
@@ -136,6 +136,7 @@ public class EnricherParserTests {
protected Object handleRequestMessage(Message<?> requestMessage) {
return new Source("foo");
}
}
Foo foo = new Foo();

View File

@@ -64,7 +64,7 @@ public class EnricherParserTestsWithoutRequestChannel {
DirectFieldAccessor accessor = new DirectFieldAccessor(enricher);
assertThat(accessor.getPropertyValue("gateway")).isNull();
assertThat(accessor.getPropertyValue("outputChannel")).isEqualTo(context.getBean("output"));
assertThat(accessor.getPropertyValue("outputChannelName")).isEqualTo("output");
assertThat(accessor.getPropertyValue("shouldClonePayload")).isEqualTo(false);
assertThat(accessor.getPropertyValue("requestPayloadExpression")).isNull();

View File

@@ -42,6 +42,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @author Artem Bilan
*
* @since 2.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@@ -62,7 +63,7 @@ public class EnricherParserWithRequestPayloadExpressionTests {
ContentEnricher enricher = (ContentEnricher) handler;
assertThat(enricher.getOrder()).isEqualTo(99);
DirectFieldAccessor accessor = new DirectFieldAccessor(enricher);
assertThat(accessor.getPropertyValue("outputChannel")).isEqualTo(context.getBean("output"));
assertThat(accessor.getPropertyValue("outputChannelName")).isEqualTo("output");
assertThat(accessor.getPropertyValue("shouldClonePayload")).isEqualTo(false);
Expression requestPayloadExpression = (Expression) accessor.getPropertyValue("requestPayloadExpression");

View File

@@ -65,12 +65,12 @@ public class FileSplitterParserTests {
assertThat(TestUtils.getPropertyValue(this.splitter, "applySequence", Boolean.class)).isTrue();
assertThat(TestUtils.getPropertyValue(this.splitter, "charset")).isEqualTo(Charset.forName("UTF-8"));
assertThat(TestUtils.getPropertyValue(this.splitter, "messagingTemplate.sendTimeout")).isEqualTo(5L);
assertThat(TestUtils.getPropertyValue(this.splitter, "outputChannel")).isEqualTo(this.out);
assertThat(TestUtils.getPropertyValue(this.splitter, "order")).isEqualTo(2);
assertThat(TestUtils.getPropertyValue(this.splitter, "firstLineHeaderName")).isEqualTo("foo");
assertThat(TestUtils.getPropertyValue(this.fullBoat, "inputChannel")).isEqualTo(this.in);
assertThat(TestUtils.getPropertyValue(this.fullBoat, "autoStartup", Boolean.class)).isFalse();
assertThat(TestUtils.getPropertyValue(this.fullBoat, "phase")).isEqualTo(1);
assertThat(this.splitter.getOutputChannel()).isSameAs(this.out);
assertThat(this.splitter.getOrder()).isEqualTo(2);
assertThat(this.fullBoat.getInputChannel()).isSameAs(this.in);
assertThat(this.fullBoat.isAutoStartup()).isFalse();
assertThat(this.fullBoat.getPhase()).isEqualTo(1);
}
}

View File

@@ -115,11 +115,11 @@ public class FtpStreamingMessageSourceTests extends FtpTestSupport {
this.data.purge(null);
this.metadataMap.clear();
this.adapter.start();
assertThat(this.data.receive(10000)).isNotNull();
received = (Message<byte[]>) this.data.receive(10000);
assertThat(received).isNotNull();
this.adapter.stop();
assertThat(received.getHeaders().get(FileHeaders.REMOTE_FILE_INFO)).isInstanceOf(FtpFileInfo.class);
this.adapter.stop();
}
@Test

View File

@@ -21,6 +21,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,8 +40,10 @@ import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.filters.AcceptAllFileListFilter;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.metadata.SimpleMetadataStore;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.integration.sftp.SftpTestSupport;
import org.springframework.integration.sftp.filters.SftpPersistentAcceptOnceFileListFilter;
import org.springframework.integration.sftp.session.SftpFileInfo;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import org.springframework.integration.transformer.StreamTransformer;
@@ -76,6 +80,9 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
@Autowired
private ApplicationContext context;
@Autowired
private ConcurrentMap<String, String> metadataMap;
@SuppressWarnings("unchecked")
@Test
public void testAllContents() {
@@ -106,7 +113,9 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
this.adapter.stop();
this.source.setFileInfoJson(false);
this.data.purge(null);
this.metadataMap.clear();
this.adapter.start();
assertThat(this.data.receive(10000)).isNotNull();
received = (Message<byte[]>) this.data.receive(10000);
assertThat(received).isNotNull();
assertThat(received.getHeaders().get(FileHeaders.REMOTE_FILE_INFO)).isInstanceOf(SftpFileInfo.class);
@@ -179,12 +188,20 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
return pollerMetadata;
}
@Bean
public ConcurrentMap<String, String> metadataMap() {
return new ConcurrentHashMap<>();
}
@Bean
@InboundChannelAdapter(channel = "stream", autoStartup = "false")
public MessageSource<InputStream> sftpMessageSource() {
SftpStreamingMessageSource messageSource = new SftpStreamingMessageSource(template(),
Comparator.comparing(LsEntry::getFilename));
messageSource.setFilter(new AcceptAllFileListFilter<>());
messageSource.setFilter(
new SftpPersistentAcceptOnceFileListFilter(
new SimpleMetadataStore(metadataMap()), "testStreaming"));
messageSource.setRemoteDirectory("sftpSource/");
return messageSource;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2019 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.
@@ -16,12 +16,13 @@
package org.springframework.integration.test.context;
import java.util.Map;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.util.Assert;
@@ -31,19 +32,30 @@ import org.springframework.util.PatternMatchUtils;
* A component to customize {@link AbstractEndpoint} beans according
* to the provided options in the {@link SpringIntegrationTest} annotation
* after all beans are registered in the application context but before its refresh.
* <p>
* This class implements both {@link SmartInitializingSingleton} and {@link BeanPostProcessor}
* to cover all the possible variants of {@link AbstractEndpoint} beans registration.
* First of all a bean for this class is registered in the application context, when XML configurations
* are parsed and registered already by the Spring Testing Framework, therefore a
* {@link BeanPostProcessor#postProcessBeforeInitialization(Object, String)} hook is not called for those beans.
* On the other hand we can't always rely on just a {@link SmartInitializingSingleton#afterSingletonsInstantiated()}
* because {@link SmartInitializingSingleton} beans are not ordered and some implementations may register beans
* later, than this {@link #afterSingletonsInstantiated()} is called.
* Plus beans might be registered at runtime, therefore {@link #postProcessBeforeInitialization(Object, String)}
* is still applied.
*
* @author Artem Bilan
*
* @since 5.0
*/
class IntegrationEndpointsInitializer implements SmartInitializingSingleton, BeanFactoryAware {
class IntegrationEndpointsInitializer implements SmartInitializingSingleton, BeanPostProcessor, BeanFactoryAware {
private final SpringIntegrationTest springIntegrationTest;
private final String[] patterns;
private ConfigurableListableBeanFactory beanFactory;
IntegrationEndpointsInitializer(SpringIntegrationTest springIntegrationTest) {
this.springIntegrationTest = springIntegrationTest;
this.patterns = springIntegrationTest.noAutoStartup();
}
@Override
@@ -53,22 +65,25 @@ class IntegrationEndpointsInitializer implements SmartInitializingSingleton, Bea
}
@Override
public void afterSingletonsInstantiated() {
Map<String, AbstractEndpoint> endpoints = this.beanFactory.getBeansOfType(AbstractEndpoint.class);
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractEndpoint && ((AbstractEndpoint) bean).isAutoStartup() && match(beanName)) {
((AbstractEndpoint) bean).setAutoStartup(false);
}
return bean;
}
endpoints.entrySet()
@Override
public void afterSingletonsInstantiated() {
this.beanFactory.getBeansOfType(AbstractEndpoint.class)
.entrySet()
.stream()
.filter(entry -> match(entry.getKey()))
.filter(entry -> entry.getValue().isAutoStartup() && match(entry.getKey()))
.forEach(entry -> entry.getValue().setAutoStartup(false));
}
private boolean match(String name) {
for (String pattern : this.springIntegrationTest.noAutoStartup()) {
if (PatternMatchUtils.simpleMatch(pattern, name)) {
return true;
}
}
return false;
return Arrays.stream(this.patterns)
.anyMatch(pattern -> PatternMatchUtils.simpleMatch(pattern, name));
}
}

View File

@@ -3,7 +3,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<int:inbound-channel-adapter id="inboundChannelAdapter" channel="results">
<bean class="org.springframework.integration.test.mock.MockIntegration" factory-method="mockMessageSource">

View File

@@ -57,7 +57,7 @@ import org.springframework.test.context.junit4.SpringRunner;
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = MockMessageSourceTests.Config.class)
@SpringIntegrationTest(noAutoStartup = {"inboundChannelAdapter", "*Source*"})
@SpringIntegrationTest(noAutoStartup = { "inboundChannelAdapter", "*Source*" })
@DirtiesContext
public class MockMessageSourceTests {
@@ -76,7 +76,7 @@ public class MockMessageSourceTests {
@After
public void tearDown() {
this.mockIntegrationContext.resetBeans("mySourceEndpoint", "inboundChannelAdapter");
results.purge(null);
this.results.purge(null);
}
@Test
@@ -103,8 +103,11 @@ public class MockMessageSourceTests {
@Test
public void testMockMessageSourceInConfig() {
this.applicationContext.getBean("mockMessageSourceTests.Config.testingMessageSource.inboundChannelAdapter",
Lifecycle.class).start();
Lifecycle channelAdapter =
this.applicationContext
.getBean("mockMessageSourceTests.Config.testingMessageSource.inboundChannelAdapter",
Lifecycle.class);
channelAdapter.start();
Message<?> receive = this.results.receive(10_000);
assertThat(receive).isNotNull();
@@ -120,13 +123,13 @@ public class MockMessageSourceTests {
assertThat(receive.getPayload()).isEqualTo(3);
}
this.applicationContext.getBean("mockMessageSourceTests.Config.testingMessageSource.inboundChannelAdapter",
Lifecycle.class).stop();
channelAdapter.stop();
}
@Test
public void testMockMessageSourceInXml() {
this.applicationContext.getBean("inboundChannelAdapter", Lifecycle.class).start();
Lifecycle channelAdapter = this.applicationContext.getBean("inboundChannelAdapter", Lifecycle.class);
channelAdapter.start();
Message<?> receive = this.results.receive(10_000);
assertThat(receive).isNotNull();
@@ -142,7 +145,7 @@ public class MockMessageSourceTests {
assertThat(receive.getPayload()).isEqualTo("c");
}
this.applicationContext.getBean("inboundChannelAdapter", Lifecycle.class).stop();
channelAdapter.stop();
}
@Test
@@ -198,6 +201,11 @@ public class MockMessageSourceTests {
return Pollers.fixedDelay(10);
}
@Bean
public QueueChannel results() {
return new QueueChannel();
}
@Bean
public IntegrationFlow myFlow() {
return IntegrationFlows
@@ -208,11 +216,6 @@ public class MockMessageSourceTests {
.get();
}
@Bean
public QueueChannel results() {
return new QueueChannel();
}
@InboundChannelAdapter(channel = "results")
@Bean
public MessageSource<Integer> testingMessageSource() {