INT-1494 replaced 'scheduled-producer' with 'inbound-channel-adapter'

This commit is contained in:
Mark Fisher
2010-10-17 12:08:45 -04:00
parent a6769f6fef
commit 70bf4fb7fa
6 changed files with 145 additions and 216 deletions

View File

@@ -61,7 +61,6 @@ public class IntegrationNamespaceHandler extends AbstractIntegrationNamespaceHan
registerBeanDefinitionParser("poller", new PollerParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigParser());
registerBeanDefinitionParser("application-event-multicaster", new ApplicationEventMulticasterParser());
registerBeanDefinitionParser("scheduled-producer", new ScheduledProducerParser());
registerBeanDefinitionParser("publishing-interceptor", new PublishingInterceptorParser());
registerBeanDefinitionParser("channel-interceptor", new GlobalChannelInterceptorParser());
registerBeanDefinitionParser("converter", new ConverterParser());

View File

@@ -16,13 +16,19 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
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.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the <inbound-channel-adapter/> element.
@@ -33,26 +39,79 @@ public class MethodInvokingInboundChannelAdapterParser extends AbstractPollingIn
@Override
protected String parseSource(Element element, ParserContext parserContext) {
BeanComponentDefinition bcDef = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
String sourceRef = null;
if (bcDef != null){
sourceRef = bcDef.getBeanName();
} else {
sourceRef = element.getAttribute("ref");
BeanComponentDefinition innnerBeanDef = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
String sourceRef = element.getAttribute("ref");
String expressionString = element.getAttribute("expression");
if (innnerBeanDef != null) {
if (StringUtils.hasText(sourceRef)) {
parserContext.getReaderContext().error(
"inner bean and a 'ref' attribute are mutually exclusive options", element);
}
sourceRef = innnerBeanDef.getBeanName();
}
else if (StringUtils.hasText(expressionString)) {
if (StringUtils.hasText(sourceRef)) {
parserContext.getReaderContext().error(
"the 'expression' and 'ref' attributes are mutually exclusive options", element);
}
sourceRef = this.parseExpression(expressionString, element, parserContext);
}
if (!StringUtils.hasText(sourceRef)) {
parserContext.getReaderContext().error("Either 'ref' attribute or inner-bean consumer definition is required.", element);
parserContext.getReaderContext().error("One of the following is required: " +
"'ref' attribute, 'expression' attribute, or an inner-bean definition.", element);
}
String methodName = element.getAttribute("method");
if (StringUtils.hasText(methodName)) {
BeanDefinitionBuilder invokerBuilder = BeanDefinitionBuilder.genericBeanDefinition(
BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".endpoint.MethodInvokingMessageSource");
invokerBuilder.addPropertyReference("object", sourceRef);
invokerBuilder.addPropertyValue("methodName", methodName);
sourceBuilder.addPropertyReference("object", sourceRef);
sourceBuilder.addPropertyValue("methodName", methodName);
this.parseHeaderExpressions(sourceBuilder, element, parserContext);
sourceRef = BeanDefinitionReaderUtils.registerWithGeneratedName(
invokerBuilder.getBeanDefinition(), parserContext.getRegistry());
sourceBuilder.getBeanDefinition(), parserContext.getRegistry());
}
return sourceRef;
}
private String parseExpression(String expressionString, Element element, ParserContext parserContext) {
BeanDefinitionBuilder sourceBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.endpoint.ExpressionEvaluatingMessageSource");
RootBeanDefinition expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(expressionString);
sourceBuilder.addConstructorArgValue(expressionDef);
sourceBuilder.addConstructorArgValue(null); // TODO: add support for expectedType?
this.parseHeaderExpressions(sourceBuilder, element, parserContext);
return BeanDefinitionReaderUtils.registerWithGeneratedName(sourceBuilder.getBeanDefinition(), parserContext.getRegistry());
}
private void parseHeaderExpressions(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
List<Element> headerElements = DomUtils.getChildElementsByTagName(element, "header");
if (!CollectionUtils.isEmpty(headerElements)) {
ManagedMap<String, Object> headerExpressions = new ManagedMap<String, Object>();
for (Element headerElement : headerElements) {
String headerName = headerElement.getAttribute("name");
String headerValue = headerElement.getAttribute("value");
String headerExpression = headerElement.getAttribute("expression");
boolean hasValue = StringUtils.hasText(headerValue);
boolean hasExpression = StringUtils.hasText(headerExpression);
if (!(hasValue ^ hasExpression)) {
parserContext.getReaderContext().error("exactly one of 'value' or 'expression' is required on a header sub-element",
parserContext.extractSource(headerElement));
continue;
}
RootBeanDefinition expressionDef = null;
if (hasValue) {
expressionDef = new RootBeanDefinition("org.springframework.expression.common.LiteralExpression");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerValue);
}
else {
expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerExpression);
}
headerExpressions.put(headerName, expressionDef);
}
builder.addPropertyValue("headerExpressions", headerExpressions);
}
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2002-2010 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.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Parser for the &lt;scheduled-producer&gt; element.
*
* @author Mark Fisher
* @since 2.0
*/
public class ScheduledProducerParser extends AbstractPollingInboundChannelAdapterParser {
@Override
protected String parseSource(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.endpoint.ExpressionEvaluatingMessageSource");
String payloadExpression = element.getAttribute("payload-expression");
RootBeanDefinition expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(payloadExpression);
builder.addConstructorArgValue(expressionDef);
builder.addConstructorArgValue(null); // TODO: add support for expectedType?
this.parseHeaderExpressions(builder, element, parserContext);
return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry());
}
private void parseHeaderExpressions(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) {
List<Element> headerElements = DomUtils.getChildElementsByTagName(element, "header");
if (!CollectionUtils.isEmpty(headerElements)) {
ManagedMap<String, Object> headerExpressions = new ManagedMap<String, Object>();
for (Element headerElement : headerElements) {
String headerName = headerElement.getAttribute("name");
String headerValue = headerElement.getAttribute("value");
String headerExpression = headerElement.getAttribute("expression");
boolean hasValue = StringUtils.hasText(headerValue);
boolean hasExpression = StringUtils.hasText(headerExpression);
if (!(hasValue ^ hasExpression)) {
parserContext.getReaderContext().error("exactly one of 'value' or 'expression' is required on a header sub-element",
parserContext.extractSource(headerElement));
continue;
}
RootBeanDefinition expressionDef = null;
if (hasValue) {
expressionDef = new RootBeanDefinition("org.springframework.expression.common.LiteralExpression");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerValue);
}
else {
expressionDef = new RootBeanDefinition("org.springframework.integration.config.ExpressionFactoryBean");
expressionDef.getConstructorArgumentValues().addGenericArgumentValue(headerExpression);
}
headerExpressions.put(headerName, expressionDef);
}
builder.addPropertyValue("headerExpressions", headerExpressions);
}
}
}

View File

@@ -656,13 +656,30 @@
</xsd:attribute>
</xsd:complexType>
<xsd:element name="inbound-channel-adapter" type="methodInvokingChannelAdapterType">
<xsd:element name="inbound-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Defines a Channel Adapter that receives from a MessageSource and sends to a
MessageChannel.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:choice>
<xsd:sequence>
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:sequence>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
</xsd:choice>
<xsd:element name="header" type="headerSubElementType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes"/>
<xsd:attributeGroup ref="channelAdapterAttributes"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-channel-adapter">
@@ -737,36 +754,37 @@
</xsd:complexType>
</xsd:element>
<xsd:complexType name="methodInvokingChannelAdapterType">
<xsd:complexContent>
<xsd:extension base="channelAdapterType">
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-method type-ref="@ref" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:attributeGroup name="methodInvokingOrExpressionEvaluatingAttributes">
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.lang.Object" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-method type-ref="@ref" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
SpEL expression to be evaluated for each triggered execution.
The result of the evaluation will be passed as the payload of
the Message that is sent to the MessageChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attributeGroup>
<xsd:complexType name="channelAdapterType">
<xsd:all>
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup name="channelAdapterAttributes">
<xsd:attribute name="id" type="xsd:ID" />
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
@@ -778,6 +796,22 @@
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string" default="true" />
</xsd:attributeGroup>
<xsd:complexType name="methodInvokingChannelAdapterType">
<xsd:complexContent>
<xsd:extension base="channelAdapterType">
<xsd:attributeGroup ref="methodInvokingOrExpressionEvaluatingAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="channelAdapterType">
<xsd:all>
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
<xsd:attributeGroup ref="channelAdapterAttributes"/>
</xsd:complexType>
<xsd:element name="service-activator">
@@ -2409,86 +2443,6 @@ Name of the header whose value to use.
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="scheduled-producer">
<xsd:annotation>
<xsd:documentation>
Defines a component that evaluates an expression to generate a Message payload
(as well as optional
expression evaluation for headers). The resulting Message
is then sent to a MessageChannel. Each execution is driven
by a Trigger.
Exactly one of the trigger type attributes must be provided. The options are:
fixed-delay, fixed-rate,
cron, or trigger (reference).
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="header" type="headerSubElementType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:ID" />
<xsd:attribute name="fixed-delay" type="xsd:string">
<xsd:annotation>
<xsd:documentation>Fixed delay trigger (in milliseconds).</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="fixed-rate" type="xsd:string">
<xsd:annotation>
<xsd:documentation>Fixed rate trigger (in milliseconds).</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cron" type="xsd:string">
<xsd:annotation>
<xsd:documentation>Cron trigger.</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="trigger" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Reference to a Trigger instance.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.scheduling.Trigger" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="payload-expression" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
SpEL expression to be evaluated for each triggered execution.
The result of the evaluation will
be passed as the payload of
the Message that is sent to the MessageChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
MessageChannel to which this producer's output should be sent.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="org.springframework.integration.core.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify whether this producer should start automatically.
By default it will. Set this to 'false'
to require a manual start.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="publishing-interceptor">
<xsd:annotation>
<xsd:documentation>

View File

@@ -19,27 +19,27 @@
<queue/>
</channel>
<scheduled-producer id="fixedDelayProducer" payload-expression="'fixedDelayTest'" channel="fixedDelayChannel" auto-startup="false">
<inbound-channel-adapter id="fixedDelayProducer" expression="'fixedDelayTest'" channel="fixedDelayChannel" auto-startup="false">
<poller fixed-delay="1234"/>
</scheduled-producer>
</inbound-channel-adapter>
<scheduled-producer id="fixedRateProducer" payload-expression="'fixedRateTest'" channel="fixedRateChannel" auto-startup="false">
<inbound-channel-adapter id="fixedRateProducer" expression="'fixedRateTest'" channel="fixedRateChannel" auto-startup="false">
<poller fixed-rate="5678"/>
</scheduled-producer>
</inbound-channel-adapter>
<scheduled-producer id="cronProducer" payload-expression="'cronTest'" channel="cronChannel" auto-startup="false">
<inbound-channel-adapter id="cronProducer" expression="'cronTest'" channel="cronChannel" auto-startup="false">
<poller cron="7 6 5 4 3 ?"/>
</scheduled-producer>
</inbound-channel-adapter>
<scheduled-producer id="headerExpressionsProducer" payload-expression="'headerExpressionsTest'" channel="headerExpressionsChannel" auto-startup="false">
<inbound-channel-adapter id="headerExpressionsProducer" expression="'headerExpressionsTest'" channel="headerExpressionsChannel" auto-startup="false">
<poller fixed-delay="99"/>
<header name="foo" expression="6 * 7"/>
<header name="bar" value="x"/>
</scheduled-producer>
</inbound-channel-adapter>
<scheduled-producer id="triggerRefProducer" payload-expression="'triggerRefTest'" channel="triggerRefChannel">
<inbound-channel-adapter id="triggerRefProducer" expression="'triggerRefTest'" channel="triggerRefChannel">
<poller trigger="customTrigger"/>
</scheduled-producer>
</inbound-channel-adapter>
<beans:bean id="customTrigger" class="org.springframework.scheduling.support.PeriodicTrigger">
<beans:constructor-arg value="9999"/>

View File

@@ -42,7 +42,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ScheduledProducerParserTests {
public class InboundChannelAdapterExpressionTests {
@Autowired
private ApplicationContext context;