INT-1834: Register Beans for Annotated Handlers
JIRA: https://jira.springsource.org/browse/INT-1834 The general issue has been raised about `MessageHistory` for annotated endpoints (`@ServiceActivator`, `@Transformer` etc.) * Register generated `MessageHandler`s as beans with concrete names based on component, method and annotation. * Add tests and docs In addition revert the version for JRuby to 1.7.8 as other latests versions have a NPE issue around `System.console()` Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
12287056d3
commit
5d2820dfbe
@@ -78,7 +78,7 @@ subprojects { subproject ->
|
||||
javaxMailVersion = '1.4.7'
|
||||
jmsSpecVersion = '1.1.1'
|
||||
jpa21ApiVersion = '1.0.0.Final'
|
||||
jrubyVersion = '1.7.10'
|
||||
jrubyVersion = '1.7.8'
|
||||
jschVersion = '0.1.50'
|
||||
jsonpathVersion = '0.9.1'
|
||||
junitVersion = '4.11'
|
||||
|
||||
@@ -32,6 +32,8 @@ public final class IntegrationConfigUtils {
|
||||
|
||||
public static final String BASE_PACKAGE = "org.springframework.integration";
|
||||
|
||||
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
|
||||
|
||||
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
|
||||
String methodSignature) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class)
|
||||
|
||||
@@ -25,11 +25,11 @@ import java.util.List;
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
import org.springframework.integration.context.Orderable;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
@@ -40,6 +40,7 @@ import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
import org.springframework.messaging.core.DestinationResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -70,7 +71,7 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
@Override
|
||||
public Object postProcess(Object bean, String beanName, Method method, T annotation) {
|
||||
MessageHandler handler = this.createHandler(bean, method, annotation);
|
||||
setAdviceChainIfPresent(beanName, annotation, handler);
|
||||
this.setAdviceChainIfPresent(beanName, annotation, handler);
|
||||
if (handler instanceof Orderable) {
|
||||
Order orderAnnotation = AnnotationUtils.findAnnotation(method, Order.class);
|
||||
if (orderAnnotation != null) {
|
||||
@@ -78,7 +79,10 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
}
|
||||
}
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
handler = (MessageHandler) ((ConfigurableListableBeanFactory) beanFactory).initializeBean(handler, "_initHandlerFor_" + beanName);
|
||||
String handlerBeanName = this.generateHandlerBeanName(beanName, method, annotation.annotationType());
|
||||
ConfigurableListableBeanFactory listableBeanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
listableBeanFactory.registerSingleton(handlerBeanName, handler);
|
||||
handler = (MessageHandler) listableBeanFactory.initializeBean(handler, handlerBeanName);
|
||||
}
|
||||
AbstractEndpoint endpoint = this.createEndpoint(handler, annotation);
|
||||
if (endpoint != null) {
|
||||
@@ -131,16 +135,23 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
if (StringUtils.hasText(inputChannelName)) {
|
||||
MessageChannel inputChannel = this.channelResolver.resolveDestination(inputChannelName);
|
||||
Assert.notNull(inputChannel, "failed to resolve inputChannel '" + inputChannelName + "'");
|
||||
Assert.isTrue(inputChannel instanceof SubscribableChannel,
|
||||
Assert.isInstanceOf(SubscribableChannel.class, inputChannel,
|
||||
"The input channel for an Annotation-based endpoint must be a SubscribableChannel.");
|
||||
endpoint = new EventDrivenConsumer((SubscribableChannel) inputChannel, handler);
|
||||
if (handler instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) handler).setBeanFactory(this.beanFactory);
|
||||
}
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
private String generateHandlerBeanName(String originalBeanName, Method method, Class<? extends Annotation> annotationType) {
|
||||
String baseName = originalBeanName + "." + method.getName() + "." + ClassUtils.getShortNameAsProperty(annotationType);
|
||||
String name = baseName;
|
||||
int count = 1;
|
||||
while (this.beanFactory.containsBean(name)) {
|
||||
name = baseName + "#" + (++count);
|
||||
}
|
||||
return name + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to create the MessageHandler.
|
||||
*
|
||||
|
||||
@@ -140,7 +140,7 @@ public class ChainParser extends AbstractConsumerEndpointParser {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
holder = new BeanDefinitionHolder(beanDefinition, handlerComponentName + IntegrationNamespaceUtils.HANDLER_ALIAS_SUFFIX);
|
||||
holder = new BeanDefinitionHolder(beanDefinition, handlerComponentName + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ public abstract class IntegrationNamespaceUtils {
|
||||
public static final String METHOD_ATTRIBUTE = "method";
|
||||
public static final String ORDER = "order";
|
||||
public static final String EXPRESSION_ATTRIBUTE = "expression";
|
||||
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
|
||||
public static final String REQUEST_HANDLER_ADVICE_CHAIN = "request-handler-advice-chain";
|
||||
public static final String AUTO_STARTUP = "auto-startup";
|
||||
public static final String PHASE = "phase";
|
||||
@@ -382,7 +381,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
String[] handlerAlias = null;
|
||||
String id = element.getAttribute(ID_ATTRIBUTE);
|
||||
if (StringUtils.hasText(id)) {
|
||||
handlerAlias = new String[] {id + HANDLER_ALIAS_SUFFIX};
|
||||
handlerAlias = new String[] {id + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX};
|
||||
}
|
||||
return handlerAlias;
|
||||
}
|
||||
|
||||
@@ -6,16 +6,6 @@
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd">
|
||||
|
||||
<message-history tracked-components="publishedChannel,input"/>
|
||||
|
||||
<beans:bean id="messageHistoryConfigurer"
|
||||
class="org.springframework.integration.history.MessageHistoryConfigurer">
|
||||
<beans:property name="componentNamePatterns">
|
||||
<beans:array>
|
||||
<beans:value> input </beans:value>
|
||||
<beans:value> publishedChannel </beans:value>
|
||||
</beans:array>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
<message-history tracked-components="publishedChannel,input,*AnnotationTestService*"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -93,6 +93,7 @@ public class EnableIntegrationTests {
|
||||
assertNotNull(messageHistory);
|
||||
String messageHistoryString = messageHistory.toString();
|
||||
assertThat(messageHistoryString, Matchers.containsString("input"));
|
||||
assertThat(messageHistoryString, Matchers.containsString("AnnotationTestService.handle.serviceActivator.handler"));
|
||||
assertThat(messageHistoryString, Matchers.not(Matchers.containsString("output")));
|
||||
|
||||
receive = this.publishedChannel.receive(1000);
|
||||
@@ -132,7 +133,7 @@ public class EnableIntegrationTests {
|
||||
@IntegrationComponentScan
|
||||
@EnableIntegration
|
||||
@PropertySource("classpath:org/springframework/integration/configuration/EnableIntegrationTests.properties")
|
||||
@EnableMessageHistory({"input", "publishedChannel"})
|
||||
@EnableMessageHistory({"input", "publishedChannel", "*AnnotationTestService*"})
|
||||
public static class ContextConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1 +1 @@
|
||||
message.history.tracked.components=input, publishedChannel
|
||||
message.history.tracked.components=input, publishedChannel, *AnnotationTestService*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
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">
|
||||
@@ -14,7 +14,7 @@
|
||||
<channel id="inMethodInvoking" />
|
||||
<channel id="out" />
|
||||
|
||||
<splitter ref="splitterBeanXmlConfig" input-channel="inMethodInvoking"
|
||||
<splitter id="splitter" ref="splitterBeanXmlConfig" input-channel="inMethodInvoking"
|
||||
method="split" output-channel="out" />
|
||||
|
||||
<splitter input-channel="inDefault" output-channel="out" />
|
||||
@@ -29,4 +29,4 @@
|
||||
|
||||
<beans:bean class="org.springframework.integration.splitter.SplitterIntegrationTests$Receiver" />
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -29,6 +29,7 @@ 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.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -65,6 +66,7 @@ public class SplitterIntegrationTests {
|
||||
MessageChannel inDelimiters;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("splitter.handler")
|
||||
MethodInvokingSplitter splitter;
|
||||
|
||||
private String sentence = "The quick brown fox jumped over the lazy dog";
|
||||
|
||||
@@ -326,6 +326,15 @@ public class FooService {
|
||||
still available. For more detail see <xref linkend="bridge"/>.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
The processing of these annotations creates the same beans (<classname>EventDrivenConsumer</classname>s and
|
||||
<interfacename>MessageHandler</interfacename>s) as with similar xml components. The bean names are generated
|
||||
with this pattern:
|
||||
<code>[componentName].[methodName].[annotationClassShortName]</code> for the <classname>EventDrivenConsumer</classname>
|
||||
endpoint and the same name with an additional <code>.handler</code> suffix for
|
||||
the <interfacename>MessageHandler</interfacename> bean. The
|
||||
<interfacename>MessageHandler</interfacename>s are also eligible to be tracked by <xref linkend="message-history"/>.
|
||||
</para>
|
||||
<para>
|
||||
Also see <xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
|
||||
@@ -70,7 +70,8 @@ assertEquals("sampleChain", chainHistory.get("name"));]]></programlisting>
|
||||
</para>
|
||||
<important>
|
||||
If multiple beans (declared by <code>@EnableMessageHistory</code> and/or <code><message-history/></code>) they
|
||||
all must have identical component name patterns (when trimmed and sorted).
|
||||
all must have identical component name patterns (when trimmed and sorted). <emphasis role="bold">Do not use a generic
|
||||
<code><bean/></code> definition for the <classname>MessageHistoryConfigurer</classname></emphasis>.
|
||||
</important>
|
||||
<note>
|
||||
Remember that by definition the Message History header is immutable (you can't re-write history, although some try). Therefore, when writing
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
<para>
|
||||
Message history can now be enabled with the <code>@EnableMessageHistory</code> annotation in a
|
||||
<code>@Configuration</code> class; in addition the message history settings can be modified
|
||||
by a JMX MBean. For more information, see <xref linkend="message-history"/>.
|
||||
by a JMX MBean. In addition auto-created <interfacename>MessageHandler</interfacename>s for
|
||||
annotated endpoints (e.g. <classname>@ServiceActivator</classname>, <classname>@Splitter</classname> etc.)
|
||||
now are also trackable by <code>MessageHistory</code>. For more information, see <xref linkend="message-history"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.0-messaging-gateway">
|
||||
|
||||
Reference in New Issue
Block a user