The <splitter/> element now creates its own endpoint (i.e. it is no longer necessary to create a <handler-endpoint/> as well) (INT-283). Also, the <handler-endpoint/>'s "handler" attribute has been replaced with "ref" to be more consistent with other spring configuration options (e.g. defining jms-listeners with the JMS namespace support).

This commit is contained in:
Mark Fisher
2008-07-06 17:36:42 +00:00
parent 1b90086e4e
commit a83e39b6ce
31 changed files with 212 additions and 129 deletions

View File

@@ -20,48 +20,48 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.endpoint.HandlerEndpoint;
import org.springframework.integration.handler.DefaultMessageHandlerAdapter;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.scheduling.PollingSchedule;
import org.springframework.integration.scheduling.Schedule;
import org.springframework.util.StringUtils;
/**
* Parser for the <em>handler-endpoint</em> element of the integration namespace.
* Base class parser for elements that create handler-invoking endpoints.
*
* @author Mark Fisher
*/
public class HandlerEndpointParser extends AbstractSingleBeanDefinitionParser {
public abstract class AbstractHandlerEndpointParser extends AbstractSingleBeanDefinitionParser {
private static final String INPUT_CHANNEL_ATTRIBUTE = "input-channel";
protected static final String REF_ATTRIBUTE = "ref";
private static final String OUTPUT_CHANNEL_ATTRIBUTE = "output-channel";
protected static final String METHOD_ATTRIBUTE = "method";
private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannelName";
protected static final String INPUT_CHANNEL_ATTRIBUTE = "input-channel";
private static final String RETURN_ADDRESS_OVERRIDES_ATTRIBUTE = "return-address-overrides";
protected static final String OUTPUT_CHANNEL_ATTRIBUTE = "output-channel";
private static final String REPLY_HANDLER_ATTRIBUTE = "reply-handler";
protected static final String OUTPUT_CHANNEL_PROPERTY = "outputChannelName";
private static final String REPLY_HANDLER_PROPERTY = "replyHandler";
private static final String SELECTOR_ATTRIBUTE = "selector";
private static final String SELECTOR_PROPERTY = "messageSelector";
protected static final String RETURN_ADDRESS_OVERRIDES_ATTRIBUTE = "return-address-overrides";
private static final String PERIOD_ATTRIBUTE = "period";
private static final String SCHEDULE_ELEMENT = "schedule";
private static final String SELECTOR_ATTRIBUTE = "selector";
private static final String SELECTOR_PROPERTY = "messageSelector";
private static final String INTERCEPTORS_ELEMENT = "interceptors";
@@ -82,17 +82,17 @@ public class HandlerEndpointParser extends AbstractSingleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String handler = element.getAttribute("handler");
if (!StringUtils.hasText(handler)) {
throw new ConfigurationException("The 'handler' attribute is required.");
String ref = element.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
throw new ConfigurationException("The '" + REF_ATTRIBUTE + "' attribute is required.");
}
String method = element.getAttribute("method");
String method = element.getAttribute(METHOD_ATTRIBUTE);
if (StringUtils.hasText(method)) {
String adapterBeanName = this.parseAdapter(handler, method, parserContext);
String adapterBeanName = this.parseAdapter(ref, method, element, parserContext);
builder.addConstructorArgReference(adapterBeanName);
}
else {
builder.addConstructorArgReference(handler);
builder.addConstructorArgReference(ref);
}
String inputChannelName = element.getAttribute(INPUT_CHANNEL_ATTRIBUTE);
Schedule schedule = null;
@@ -129,18 +129,26 @@ public class HandlerEndpointParser extends AbstractSingleBeanDefinitionParser {
String returnAddressOverridesAttribute = element.getAttribute(RETURN_ADDRESS_OVERRIDES_ATTRIBUTE);
boolean returnAddressOverrides = "true".equals(returnAddressOverridesAttribute);
builder.addPropertyValue("returnAddressOverrides", returnAddressOverrides);
String replyHandler = element.getAttribute(REPLY_HANDLER_ATTRIBUTE);
if (StringUtils.hasText(replyHandler)) {
builder.addPropertyValue(REPLY_HANDLER_PROPERTY, new RuntimeBeanReference(replyHandler));
}
this.postProcessEndpointBean(builder, element, parserContext);
}
private String parseAdapter(String ref, String method, ParserContext parserContext) {
BeanDefinition adapterDef = new RootBeanDefinition(DefaultMessageHandlerAdapter.class);
adapterDef.getPropertyValues().addPropertyValue("object", new RuntimeBeanReference(ref));
adapterDef.getPropertyValues().addPropertyValue("methodName", method);
String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDef);
parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDef, adapterBeanName));
protected abstract Class<? extends MessageHandler> getHandlerAdapterClass();
protected void postProcessEndpointBean(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
}
protected void postProcessAdapterBean(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
}
private String parseAdapter(String ref, String method, Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getHandlerAdapterClass());
builder.addPropertyValue("object", new RuntimeBeanReference(ref));
builder.addPropertyValue("methodName", method);
String adapterBeanName = BeanDefinitionReaderUtils.generateBeanName(builder.getBeanDefinition(), parserContext.getRegistry());
this.postProcessAdapterBean(builder, element, parserContext);
BeanDefinitionHolder holder = new BeanDefinitionHolder(builder.getBeanDefinition(), adapterBeanName);
parserContext.registerBeanComponent(new BeanComponentDefinition(holder));
return adapterBeanName;
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.handler.DefaultMessageHandlerAdapter;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.util.StringUtils;
/**
* @author Mark Fisher
*/
public class DefaultHandlerEndpointParser extends AbstractHandlerEndpointParser {
private static final String REPLY_HANDLER_ATTRIBUTE = "reply-handler";
private static final String REPLY_HANDLER_PROPERTY = "replyHandler";
@Override
protected Class<? extends MessageHandler> getHandlerAdapterClass() {
return DefaultMessageHandlerAdapter.class;
}
@Override
protected void postProcessEndpointBean(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
String replyHandler = element.getAttribute(REPLY_HANDLER_ATTRIBUTE);
if (StringUtils.hasText(replyHandler)) {
builder.addPropertyValue(REPLY_HANDLER_PROPERTY, new RuntimeBeanReference(replyHandler));
}
}
}

View File

@@ -64,7 +64,7 @@ public class IntegrationNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("priority-channel", new PriorityChannelParser());
registerBeanDefinitionParser("rendezvous-channel", new RendezvousChannelParser());
registerBeanDefinitionParser("thread-local-channel", new ThreadLocalChannelParser());
registerBeanDefinitionParser("handler-endpoint", new HandlerEndpointParser());
registerBeanDefinitionParser("handler-endpoint", new DefaultHandlerEndpointParser());
registerBeanDefinitionParser("channel-adapter", new ChannelAdapterParser());
registerBeanDefinitionParser("gateway", new GatewayParser());
registerBeanDefinitionParser("handler", new HandlerParser());

View File

@@ -92,6 +92,9 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Init
if (bean instanceof ChannelRegistryAware) {
((ChannelRegistryAware) bean).setChannelRegistry(this.messageBus);
}
if (!bean.equals(originalBean) && originalBean instanceof ChannelRegistryAware) {
((ChannelRegistryAware) originalBean).setChannelRegistry(this.messageBus);
}
if (endpointAnnotation != null && bean.equals(originalBean)) {
throw new ConfigurationException("Class [" + beanClass.getName()
+ "] is annotated with @MessageEndpoint but contains no source, target, or handler method annotations.");

View File

@@ -200,23 +200,35 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:all>
<xsd:element ref="schedule" minOccurs="0" maxOccurs="1"/>
<xsd:element name="interceptors" type="interceptorsType" minOccurs="0" maxOccurs="1"/>
</xsd:all>
<xsd:attribute name="handler" type="xsd:string" use="required"/>
<xsd:attribute name="method" type="xsd:string"/>
<xsd:attribute name="input-channel" type="xsd:string" use="required"/>
<xsd:attribute name="output-channel" type="xsd:string"/>
<xsd:attribute name="selector" type="xsd:string"/>
<xsd:extension base="handlerEndpointType">
<xsd:attribute name="reply-handler" type="xsd:string"/>
<xsd:attribute name="return-address-overrides" type="xsd:boolean" default="false"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="handlerEndpointType">
<xsd:annotation>
<xsd:documentation>
Base type for handler endpoint elements.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:all>
<xsd:element ref="schedule" minOccurs="0" maxOccurs="1"/>
<xsd:element name="interceptors" type="interceptorsType" minOccurs="0" maxOccurs="1"/>
</xsd:all>
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="method" type="xsd:string"/>
<xsd:attribute name="input-channel" type="xsd:string" use="required"/>
<xsd:attribute name="output-channel" type="xsd:string"/>
<xsd:attribute name="selector" type="xsd:string"/>
<xsd:attribute name="return-address-overrides" type="xsd:boolean" default="false"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="schedule">
<xsd:complexType>
<xsd:annotation>
@@ -319,18 +331,12 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="splitter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
<xsd:element name="splitter" type="handlerEndpointType">
<xsd:annotation>
<xsd:documentation>
Defines a Splitter.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:ID"/>
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="method" type="xsd:string" use="required"/>
<xsd:attribute name="output-channel" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="aggregator">

View File

@@ -19,7 +19,8 @@ package org.springframework.integration.handler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.core.Ordered;
import org.springframework.integration.channel.ChannelRegistry;
import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.message.DefaultMessageCreator;
import org.springframework.integration.message.DefaultMessageMapper;
import org.springframework.integration.message.Message;
@@ -44,19 +45,20 @@ import org.springframework.util.ObjectUtils;
*
* @author Mark Fisher
*/
public abstract class AbstractMessageHandlerAdapter extends AbstractMethodInvokingAdapter implements MessageHandler {
public abstract class AbstractMessageHandlerAdapter extends AbstractMethodInvokingAdapter
implements MessageHandler, ChannelRegistryAware {
public static final String OUTPUT_CHANNEL_NAME_KEY = "outputChannelName";
private volatile int order;
private volatile boolean methodExpectsMessage;
private volatile MessageMapper messageMapper = new DefaultMessageMapper();
private volatile MessageCreator messageCreator = new DefaultMessageCreator();
private volatile ChannelRegistry channelRegistry;
public void setMethodExpectsMessage(boolean methodExpectsMessage) {
this.methodExpectsMessage = methodExpectsMessage;
@@ -72,6 +74,14 @@ public abstract class AbstractMessageHandlerAdapter extends AbstractMethodInvoki
this.messageCreator = messageCreator;
}
public void setChannelRegistry(ChannelRegistry channelRegistry) {
this.channelRegistry = channelRegistry;
}
protected ChannelRegistry getChannelRegistry() {
return this.channelRegistry;
}
public Message<?> handle(Message<?> message) {
if (!this.isInitialized()) {
this.afterPropertiesSet();

View File

@@ -26,7 +26,6 @@ import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.handler.AbstractMessageHandlerAdapter;
import org.springframework.integration.message.Message;
import org.springframework.util.Assert;
/**
* MessageHandler adapter for methods annotated with {@link Splitter @Splitter}.
@@ -36,18 +35,14 @@ import org.springframework.util.Assert;
*/
public class SplitterMessageHandlerAdapter extends AbstractMessageHandlerAdapter implements ChannelRegistryAware {
private final String outputChannelName;
private volatile ChannelRegistry channelRegistry;
private volatile String outputChannelName;
private volatile long sendTimeout = -1;
public SplitterMessageHandlerAdapter(Object object, Method method, String outputChannelName) {
Assert.hasText(outputChannelName, "output channel name is required");
public SplitterMessageHandlerAdapter(Object object, Method method) {
this.setObject(object);
this.setMethod(method);
this.outputChannelName = outputChannelName;
if (method.getParameterTypes().length < 1) {
throw new ConfigurationException("The splitter method must accept at least one argument.");
}
@@ -56,15 +51,17 @@ public class SplitterMessageHandlerAdapter extends AbstractMessageHandlerAdapter
}
}
public SplitterMessageHandlerAdapter(Object object, String methodName, String outputChannelName) {
Assert.hasText(outputChannelName, "output channel name is required");
public SplitterMessageHandlerAdapter(Object object, String methodName) {
this.setObject(object);
this.setMethodName(methodName);
this.outputChannelName = outputChannelName;
}
public void setChannelRegistry(ChannelRegistry channelRegistry) {
this.channelRegistry = channelRegistry;
public SplitterMessageHandlerAdapter() {
}
public void setOutputChannelName(String outputChannelName) {
this.outputChannelName = outputChannelName;
}
public void setSendTimeout(long sendTimeout) {
@@ -115,10 +112,11 @@ public class SplitterMessageHandlerAdapter extends AbstractMessageHandlerAdapter
}
private boolean sendMessage(Message<?> message, String channelName) {
if (this.channelRegistry == null) {
ChannelRegistry channelRegistry = this.getChannelRegistry();
if (channelRegistry == null) {
throw new IllegalStateException(this.getClass().getSimpleName() + " requires a ChannelRegistry reference.");
}
MessageChannel channel = this.channelRegistry.lookupChannel(channelName);
MessageChannel channel = channelRegistry.lookupChannel(channelName);
if (channel == null) {
if (logger.isWarnEnabled()) {
logger.warn("unable to resolve channel for name '" + channelName + "'");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2008 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.
@@ -42,7 +42,9 @@ public class SplitterMessageHandlerCreator extends AbstractMessageHandlerCreator
outputChannelName = endpointAnnotation.output();
}
}
return new SplitterMessageHandlerAdapter(object, method, outputChannelName);
SplitterMessageHandlerAdapter adapter = new SplitterMessageHandlerAdapter(object, method);
adapter.setOutputChannelName(outputChannelName);
return adapter;
}
}

View File

@@ -19,8 +19,10 @@ package org.springframework.integration.router.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.config.AbstractHandlerEndpointParser;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.router.SplitterMessageHandlerAdapter;
import org.springframework.util.StringUtils;
@@ -29,25 +31,20 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
*/
public class SplitterParser extends AbstractSingleBeanDefinitionParser {
public class SplitterParser extends AbstractHandlerEndpointParser {
@Override
protected Class<?> getBeanClass(Element element) {
protected Class<? extends MessageHandler> getHandlerAdapterClass() {
return SplitterMessageHandlerAdapter.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String ref = element.getAttribute("ref");
String methodName = element.getAttribute("method");
protected void postProcessAdapterBean(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
String outputChannelName = element.getAttribute("output-channel");
if (!StringUtils.hasText(ref) || !StringUtils.hasText(methodName) || !StringUtils.hasText(outputChannelName)) {
throw new ConfigurationException(
"The 'ref', 'method', and 'output-channel' attributes are all required.");
if (!StringUtils.hasText(outputChannelName)) {
throw new ConfigurationException("The 'output-channel' attribute is required.");
}
builder.addConstructorArgReference(ref);
builder.addConstructorArgValue(methodName);
builder.addConstructorArgValue(outputChannelName);
builder.addPropertyValue("outputChannelName", outputChannelName);
}
}