Both "source-endpoint" and "target-endpoint" elements are now configured as <channel-adapter/>. That element requires exactly one of "source" or "target" and optionally accepts a "method" attribute for creating either a MethodInvokingSource or a MethodInvokingTarget (INT-277).

This commit is contained in:
Mark Fisher
2008-07-05 20:02:12 +00:00
parent 63c8c2a30f
commit 5a40f17826
7 changed files with 89 additions and 111 deletions

View File

@@ -18,26 +18,38 @@ package org.springframework.integration.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
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.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.endpoint.SourceEndpoint;
import org.springframework.integration.endpoint.TargetEndpoint;
import org.springframework.integration.handler.MethodInvokingTarget;
import org.springframework.integration.message.MethodInvokingSource;
import org.springframework.integration.scheduling.PollingSchedule;
import org.springframework.integration.scheduling.Schedule;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the <source-endpoint/> element.
* Parser for the <channel-adapter/> element.
*
* @author Mark Fisher
*/
public class SourceEndpointParser extends AbstractSimpleBeanDefinitionParser {
public class ChannelAdapterParser extends AbstractSimpleBeanDefinitionParser {
protected final Class<?> getBeanClass(Element element) {
return SourceEndpoint.class;
boolean hasSource = StringUtils.hasText(element.getAttribute("source"));
boolean hasTarget = StringUtils.hasText(element.getAttribute("target"));
if (!(hasSource ^ hasTarget)) {
throw new ConfigurationException("exactly one of 'source' or 'target' is required");
}
return hasSource ? SourceEndpoint.class : TargetEndpoint.class;
}
protected boolean shouldGenerateId() {
@@ -49,21 +61,31 @@ public class SourceEndpointParser extends AbstractSimpleBeanDefinitionParser {
}
protected boolean isEligibleAttribute(String name) {
return (!"source".equals(name) && !"channel".equals(name) && super.isEligibleAttribute(name));
return (!"source".equals(name)
&& !"target".equals(name)
&& !"channel".equals(name)
&& super.isEligibleAttribute(name));
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String source = element.getAttribute("source");
if (!StringUtils.hasText(source)) {
throw new ConfigurationException("'source' is required");
}
String output = element.getAttribute("channel");
if (!StringUtils.hasText(output)) {
String target = element.getAttribute("target");
String channel = element.getAttribute("channel");
if (!StringUtils.hasText(channel)) {
throw new ConfigurationException("'channel' is required");
}
builder.addConstructorArgReference(source);
builder.addPropertyValue("outputChannelName", output);
boolean isSource = StringUtils.hasText(source);
if (isSource) {
builder.addConstructorArgReference(this.resolveConstructorArgument(
source, MethodInvokingSource.class, element, parserContext));
builder.addPropertyValue("outputChannelName", channel);
}
else {
builder.addConstructorArgReference(this.resolveConstructorArgument(
target, MethodInvokingTarget.class, element, parserContext));
builder.addPropertyValue("inputChannelName", channel);
}
Element scheduleElement = DomUtils.getChildElementByTagName(element, "schedule");
if (scheduleElement != null) {
builder.addPropertyValue("schedule", this.parseSchedule(scheduleElement));
@@ -76,6 +98,19 @@ public class SourceEndpointParser extends AbstractSimpleBeanDefinitionParser {
}
}
private String resolveConstructorArgument(String ref, Class<?> targetClass, Element element, ParserContext parserContext) {
String method = element.getAttribute("method");
if (StringUtils.hasText(method)) {
BeanDefinition adapterDef = new RootBeanDefinition(targetClass);
adapterDef.getPropertyValues().addPropertyValue("object", new RuntimeBeanReference(ref));
adapterDef.getPropertyValues().addPropertyValue("methodName", method);
String adapterBeanName = parserContext.getReaderContext().generateBeanName(adapterDef);
parserContext.registerBeanComponent(new BeanComponentDefinition(adapterDef, adapterBeanName));
return adapterBeanName;
}
return ref;
}
/**
* Subclasses may override this method to control the creation of the {@link Schedule}. The default
* implementation creates a {@link PollingSchedule} instance based on the provided "period" attribute.

View File

@@ -64,11 +64,8 @@ public class IntegrationNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("priority-channel", new PriorityChannelParser());
registerBeanDefinitionParser("rendezvous-channel", new RendezvousChannelParser());
registerBeanDefinitionParser("thread-local-channel", new ThreadLocalChannelParser());
registerBeanDefinitionParser("source-adapter", new MethodInvokingAdapterParser());
registerBeanDefinitionParser("target-adapter", new MethodInvokingAdapterParser());
registerBeanDefinitionParser("source-endpoint", new SourceEndpointParser());
registerBeanDefinitionParser("handler-endpoint", new HandlerEndpointParser());
registerBeanDefinitionParser("target-endpoint", new TargetEndpointParser());
registerBeanDefinitionParser("channel-adapter", new ChannelAdapterParser());
registerBeanDefinitionParser("gateway", new GatewayParser());
registerBeanDefinitionParser("handler", new HandlerParser());
registerBeanDefinitionParser("handler-chain", new HandlerParser());

View File

@@ -168,58 +168,30 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="source-endpoint">
<xsd:element name="channel-adapter">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines a source endpoint.
Defines a Channel Adapter that is capable of either receiving from a MessageSource and sending
the result to a MessageChannel or receiving from a MessageChannel and sending the result to a
MessageTarget. Therefore, either "source" or "target" should be provided (but never both).
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:sequence>
<xsd:element ref="schedule" minOccurs="1" maxOccurs="1"/>
<xsd:all>
<xsd:element ref="schedule" minOccurs="0" maxOccurs="1"/>
<xsd:element name="interceptors" type="interceptorsType" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
<xsd:attribute name="source" type="xsd:string" use="required"/>
</xsd:all>
<xsd:attribute name="source" type="xsd:string"/>
<xsd:attribute name="target" type="xsd:string"/>
<xsd:attribute name="method" type="xsd:string"/>
<xsd:attribute name="channel" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="source-adapter" type="methodInvokingAdapterType">
<xsd:annotation>
<xsd:documentation>
Defines a MethodInvokingSource.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="target-endpoint">
<xsd:complexType>
<xsd:annotation>
<xsd:documentation>
Defines a target endpoint.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="targetEndpointType">
<xsd:attribute name="target" type="xsd:string" use="required"/>
<xsd:attribute name="method" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="target-adapter" type="methodInvokingAdapterType">
<xsd:annotation>
<xsd:documentation>
Defines a MethodInvokingTarget.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="handler-endpoint">
<xsd:complexType>
<xsd:annotation>
@@ -228,10 +200,16 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="targetEndpointType">
<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:attribute name="reply-handler" type="xsd:string"/>
<xsd:attribute name="return-address-overrides" type="xsd:boolean" default="false"/>
</xsd:extension>
@@ -417,38 +395,6 @@
</xsd:complexType>
</xsd:element>
<xsd:complexType name="targetEndpointType">
<xsd:annotation>
<xsd:documentation>
Defines common configuration properties of target message endpoints.
</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="input-channel" type="xsd:string" use="required"/>
<xsd:attribute name="selector" type="xsd:string"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="methodInvokingAdapterType">
<xsd:annotation>
<xsd:documentation>
Base type for method-invoking adapters.
</xsd:documentation>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="ref" type="xsd:string" use="required"/>
<xsd:attribute name="method" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="interceptorsType">
<xsd:annotation>
<xsd:documentation>

View File

@@ -16,20 +16,24 @@
package org.springframework.integration.channel.config;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageSource;
import org.springframework.integration.message.StringMessage;
/**
* @author Mark Fisher
*/
public class TestSourceBean {
public class TestSource implements MessageSource<String> {
private final String text;
public TestSourceBean(String text) {
public TestSource(String text) {
this.text = text;
}
public String getText() {
return this.text;
public Message<String> receive() {
return new StringMessage(this.text);
}
}

View File

@@ -31,29 +31,29 @@
</interceptors>
</handler-endpoint>
<target-endpoint id="targetEndpointWithoutAdvice"
input-channel="testChannel"
target="testTarget">
<channel-adapter id="targetEndpointWithoutAdvice"
channel="testChannel"
target="testTarget">
<schedule period="100"/>
</target-endpoint>
</channel-adapter>
<target-endpoint id="targetEndpointWithAdvice"
input-channel="testChannel"
target="testTarget">
<channel-adapter id="targetEndpointWithAdvice"
channel="testChannel"
target="testTarget">
<schedule period="100"/>
<interceptors>
<ref bean="simpleAdvice"/>
<ref bean="interceptor"/>
</interceptors>
</target-endpoint>
</channel-adapter>
<source-endpoint id="sourceEndpointWithoutAdvice"
<channel-adapter id="sourceEndpointWithoutAdvice"
source="testSource"
channel="replyChannel">
<schedule period="100"/>
</source-endpoint>
</channel-adapter>
<source-endpoint id="sourceEndpointWithAdvice"
<channel-adapter id="sourceEndpointWithAdvice"
source="testSource"
channel="testChannel">
<schedule period="100"/>
@@ -61,7 +61,7 @@
<ref bean="simpleAdvice"/>
<ref bean="interceptor"/>
</interceptors>
</source-endpoint>
</channel-adapter>
<beans:bean id="testHandler" class="org.springframework.integration.config.TestHandler"/>

View File

@@ -9,11 +9,9 @@
<direct-channel id="channelWithoutSource"/>
<direct-channel id="channelWithSource" source="source"/>
<direct-channel id="channelWithSource" source="testSource"/>
<source-adapter id="source" ref="testSourceBean" method="getText"/>
<beans:bean id="testSourceBean" class="org.springframework.integration.channel.config.TestSourceBean">
<beans:bean id="testSource" class="org.springframework.integration.channel.config.TestSource">
<beans:constructor-arg value="foo"/>
</beans:bean>

View File

@@ -11,13 +11,11 @@
<si:channel id="channel"/>
<si:source-endpoint source="sourceAdapter" channel="channel">
<si:channel-adapter source="source" method="foo" channel="channel">
<si:schedule period="100"/>
</si:source-endpoint>
</si:channel-adapter>
<si:target-endpoint target="sink" method="store" input-channel="channel"/>
<si:source-adapter id="sourceAdapter" ref="source" method="foo"/>
<si:channel-adapter target="sink" method="store" channel="channel"/>
<bean id="source" class="org.springframework.integration.handler.TestSource"/>