Skip to content

Commit

Permalink
INT-3829: Care about @Profile on the @Bean
Browse files Browse the repository at this point in the history
JIRA: https://jira.spring.io/browse/INT-3829

Alongside with the `@Bean` definition we can use for example `@Profile` `@Conditional` annotation.
And the final bean won't be populated to the context.

Previously the `MessagingAnnotationPostProcessor` did take care about that infrastructure outcome,
hence we ended up with the `NoSuchBeanDefinitionException`

* Add appropriate `try...catch(NoSuchBeanDefinitionException)` to the `AbstractMethodAnnotationPostProcessor` and
`InboundChannelAdapterAnnotationPostProcessor` to skip further endpoint processing if there is the target bean
by the condition reason.
  • Loading branch information
artembilan committed Sep 22, 2015
1 parent a9f50d3 commit b29b4f4
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 6 deletions.
Expand Up @@ -31,6 +31,7 @@
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -122,6 +123,16 @@ public AbstractMethodAnnotationPostProcessor(ListableBeanFactory beanFactory, En

@Override
public Object postProcess(Object bean, String beanName, Method method, List<Annotation> annotations) {
if (this.beanAnnotationAware() && AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
try {
resolveTargetBeanFromMethodWithBeanAnnotation(method);
}
catch (NoSuchBeanDefinitionException e) {
// Skip the @Bean for farther endpoint processing.
// Mainly by the @Conditional reason.
return null;
}
}
MessageHandler handler = createHandler(bean, method, annotations);
setAdviceChainIfPresent(beanName, annotations, handler);
if (handler instanceof Orderable) {
Expand Down
Expand Up @@ -21,6 +21,7 @@
import java.util.List;

import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
Expand Down Expand Up @@ -59,7 +60,15 @@ public Object postProcess(Object bean, String beanName, Method method, List<Anno
String channelName = MessagingAnnotationUtils.resolveAttribute(annotations, AnnotationUtils.VALUE, String.class);
Assert.hasText(channelName, "The channel ('value' attribute of @InboundChannelAdapter) can't be empty.");

MessageSource<?> messageSource = this.createMessageSource(bean, beanName, method);
MessageSource<?> messageSource = null;
try {
messageSource = createMessageSource(bean, beanName, method);
}
catch (NoSuchBeanDefinitionException e) {
// Skip the @Bean for farther endpoint processing.
// Mainly by the @Conditional reason.
return null;
}

MessageChannel channel = this.channelResolver.resolveDestination(channelName);

Expand Down
Expand Up @@ -20,6 +20,7 @@
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
Expand All @@ -36,16 +37,20 @@

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;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.aggregator.AggregatingMessageHandler;
import org.springframework.integration.aggregator.ExpressionEvaluatingCorrelationStrategy;
import org.springframework.integration.aggregator.ExpressionEvaluatingReleaseStrategy;
import org.springframework.integration.aggregator.SimpleMessageGroupProcessor;
import org.springframework.integration.annotation.BridgeFrom;
import org.springframework.integration.annotation.BridgeTo;
import org.springframework.integration.annotation.Filter;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
Expand Down Expand Up @@ -93,6 +98,22 @@ public class MessagingAnnotationsWithBeanAnnotationTests {
@Resource(name="collector")
private List<Message<?>> collector;

@Autowired(required = false)
@Qualifier("skippedMessageHandler")
private MessageHandler skippedMessageHandler;

@Autowired(required = false)
@Qualifier("skippedChannel")
private MessageChannel skippedChannel;

@Autowired(required = false)
@Qualifier("skippedChannel2")
private MessageChannel skippedChannel2;

@Autowired(required = false)
@Qualifier("skippedMessageSource")
private MessageSource<?> skippedMessageSource;

@Test
public void testMessagingAnnotationsFlow() {
this.sourcePollingChannelAdapter.start();
Expand All @@ -113,6 +134,11 @@ public void testMessagingAnnotationsFlow() {
assertThat(messageHistoryString, Matchers.containsString("serviceChannel"));
assertThat(messageHistoryString, Matchers.not(Matchers.containsString("discardChannel")));
}

assertNull(this.skippedMessageHandler);
assertNull(this.skippedChannel);
assertNull(this.skippedChannel2);
assertNull(this.skippedMessageSource);
}

@Test
Expand Down Expand Up @@ -237,6 +263,38 @@ public void handleMessage(Message<?> message) throws MessagingException {
};
}

@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Splitter(inputChannel = "skippedChannel2")
@Router(inputChannel = "skippedChannel3")
@Transformer(inputChannel = "skippedChannel4")
@Filter(inputChannel = "skippedChannel5")
@Profile("foo")
public MessageHandler skippedMessageHandler() {
return System.out::println;
}

@Bean
@BridgeFrom("skippedChannel6")
@Profile("foo")
public MessageChannel skippedChannel1() {
return new DirectChannel();
}

@Bean
@BridgeTo
@Profile("foo")
public MessageChannel skippedChannel2() {
return new DirectChannel();
}

@Bean
@InboundChannelAdapter("serviceChannel")
@Profile("foo")
public MessageSource<?> skippedMessageSource() {
return () -> new GenericMessage<>("foo");
}

}

@Configuration
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
Expand Down Expand Up @@ -206,7 +206,7 @@ public void run() {
}
});
}
latch.await(10, TimeUnit.SECONDS);
latch.await(30, TimeUnit.SECONDS);
for (int i = 0; i < numRequests; i++) {
assertEquals("test-" + i + "!!!", results[i]);
}
Expand Down
Expand Up @@ -22,8 +22,8 @@
<beans:property name="serviceInterface" value="org.springframework.integration.gateway.TestService"/>
<beans:property name="defaultRequestChannel" ref="requestChannel"/>
<beans:property name="defaultReplyChannel" ref="replyChannel"/>
<beans:property name="defaultRequestTimeout" value="5000"/>
<beans:property name="defaultReplyTimeout" value="5000"/>
<beans:property name="defaultRequestTimeout" value="10000"/>
<beans:property name="defaultReplyTimeout" value="10000"/>
</beans:bean>

<beans:bean id="handler" class="org.springframework.integration.gateway.TestHandler"/>
Expand Down
16 changes: 15 additions & 1 deletion src/reference/asciidoc/configuration.adoc
Expand Up @@ -16,7 +16,7 @@ Direct usage of the API is of course always an option, but we expect that most u
=== Namespace Support

Spring Integration components can be configured with XML elements that map directly to the terminology and concepts of enterprise integration.
In many cases, the element names match those of thehttp://www.eaipatterns.com[Enterprise Integration Patterns].
In many cases, the element names match those of the http://www.eaipatterns.com[Enterprise Integration Patterns].

To enable Spring Integration's core namespace support within your Spring configuration files, add the following namespace reference and schema mapping in your top-level 'beans' element:

Expand Down Expand Up @@ -532,6 +532,20 @@ For example the endpoint (`SourcePollingChannelAdapter`) for the `consoleSource(

IMPORTANT: When using these annotations on `@Bean` definitions, the `inputChannel` must reference a declared bean; channels are not automatically declared in this case.

NOTE: With Java & Annotation configuration we can use any `@Conditional` (e.g. `@Profile`) definition on the `@Bean`
method level, meaning to skip the bean registration by some condition reason:
[source,java]
----
@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("foo")
public MessageHandler skipped() {
return System.out::println;
}
----
Together with the existing Spring Container logic, the Messaging Endpoint bean, based on the `@ServiceActivator`
annotation, won't be registered as well.

==== Creating a Bridge with Annotations

Starting with _version 4.0_, the Messaging Annotation and Java configuration provides `@BridgeFrom` and `@BridgeTo` `@Bean` method annotations to mark `MessageChannel` beans in `@Configuration` classes.
Expand Down

0 comments on commit b29b4f4

Please sign in to comment.