INT-650 added support for <publisher> in the core namespace (creates a TriggeredMessagePublisher)

This commit is contained in:
Mark Fisher
2010-07-20 21:12:34 +00:00
parent aed4b3bc26
commit 04585d1d22
5 changed files with 302 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,89 @@
/*
* 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 org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
/**
* Parser for the &lt;publisher&gt; element.
*
* @author Mark Fisher
* @since 2.0
*/
public class PublisherParser extends AbstractSingleBeanDefinitionParser {
@Override
protected String getBeanClassName(Element element) {
return IntegrationNamespaceUtils.BASE_PACKAGE + ".endpoint.TriggeredMessagePublisher";
}
@Override
protected boolean shouldGenerateIdAsFallback() {
return true;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String fixedDelay = element.getAttribute("fixed-delay");
String fixedRate = element.getAttribute("fixed-rate");
String cron = element.getAttribute("cron");
String trigger = element.getAttribute("trigger");
int numTriggers = 0;
if (StringUtils.hasText(fixedDelay)) {
RootBeanDefinition triggerDefinition = new RootBeanDefinition(
"org.springframework.scheduling.support.PeriodicTrigger");
triggerDefinition.getConstructorArgumentValues().addGenericArgumentValue(fixedDelay);
builder.addConstructorArgValue(triggerDefinition);
numTriggers++;
}
if (StringUtils.hasText(fixedRate)) {
RootBeanDefinition triggerDefinition = new RootBeanDefinition(
"org.springframework.scheduling.support.PeriodicTrigger");
triggerDefinition.getConstructorArgumentValues().addGenericArgumentValue(fixedRate);
triggerDefinition.getPropertyValues().add("fixedRate", Boolean.TRUE);
builder.addConstructorArgValue(triggerDefinition);
numTriggers++;
}
if (StringUtils.hasText(cron)) {
RootBeanDefinition triggerDefinition = new RootBeanDefinition(
"org.springframework.scheduling.support.CronTrigger");
triggerDefinition.getConstructorArgumentValues().addGenericArgumentValue(cron);
builder.addConstructorArgValue(triggerDefinition);
numTriggers++;
}
if (StringUtils.hasText(trigger)) {
builder.addConstructorArgReference(trigger);
numTriggers++;
}
if (numTriggers != 1) {
parserContext.getReaderContext().error("exactly one of the following trigger attributes must be provided: "
+ "fixed-delay, fixed-rate, cron, or trigger", parserContext.extractSource(element));
return;
}
builder.addPropertyReference("outputChannel", element.getAttribute("channel"));
builder.addConstructorArgValue(element.getAttribute("payload"));
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "auto-startup");
// TODO: add support for header expression sub-elements
}
}

View File

@@ -2246,6 +2246,77 @@ Name of the header whose value to use.
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="publisher">
<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: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" 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 publisher'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 publisher 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

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="fixedDelayChannel"/>
<channel id="fixedRateChannel"/>
<channel id="cronChannel"/>
<channel id="triggerRefChannel">
<queue/>
</channel>
<publisher id="fixedDelayPublisher" fixed-delay="1234" payload="'fixedDelayTest'" channel="fixedDelayChannel" auto-startup="false"/>
<publisher id="fixedRatePublisher" fixed-rate="5678" payload="'fixedRateTest'" channel="fixedRateChannel" auto-startup="false"/>
<publisher id="cronPublisher" cron="7 6 5 4 3 ?" payload="'cronTest'" channel="cronChannel" auto-startup="false"/>
<publisher id="triggerRefPublisher" trigger="customTrigger" payload="'triggerRefTest'" channel="triggerRefChannel"/>
<beans:bean id="customTrigger" class="org.springframework.scheduling.support.PeriodicTrigger">
<beans:constructor-arg value="9999"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,109 @@
/*
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.TriggeredMessagePublisher;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
* @since 2.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PublisherParserTests {
@Autowired
private ApplicationContext context;
@Test
public void fixedDelay() {
TriggeredMessagePublisher publisher = context.getBean("fixedDelayPublisher", TriggeredMessagePublisher.class);
assertFalse(publisher.isAutoStartup());
DirectFieldAccessor publisherAccessor = new DirectFieldAccessor(publisher);
Trigger trigger = (Trigger) publisherAccessor.getPropertyValue("trigger");
assertEquals(PeriodicTrigger.class, trigger.getClass());
DirectFieldAccessor triggerAccessor = new DirectFieldAccessor(trigger);
assertEquals(1234L, triggerAccessor.getPropertyValue("period"));
assertEquals(Boolean.FALSE, triggerAccessor.getPropertyValue("fixedRate"));
assertEquals(context.getBean("fixedDelayChannel"), publisherAccessor.getPropertyValue("outputChannel"));
Expression payloadExpression = (Expression) new DirectFieldAccessor(
publisherAccessor.getPropertyValue("task")).getPropertyValue("payloadExpression");
assertEquals("'fixedDelayTest'", payloadExpression.getExpressionString());
}
@Test
public void fixedRate() {
TriggeredMessagePublisher publisher = context.getBean("fixedRatePublisher", TriggeredMessagePublisher.class);
assertFalse(publisher.isAutoStartup());
DirectFieldAccessor publisherAccessor = new DirectFieldAccessor(publisher);
Trigger trigger = (Trigger) publisherAccessor.getPropertyValue("trigger");
assertEquals(PeriodicTrigger.class, trigger.getClass());
DirectFieldAccessor triggerAccessor = new DirectFieldAccessor(trigger);
assertEquals(5678L, triggerAccessor.getPropertyValue("period"));
assertEquals(Boolean.TRUE, triggerAccessor.getPropertyValue("fixedRate"));
assertEquals(context.getBean("fixedRateChannel"), publisherAccessor.getPropertyValue("outputChannel"));
Expression payloadExpression = (Expression) new DirectFieldAccessor(
publisherAccessor.getPropertyValue("task")).getPropertyValue("payloadExpression");
assertEquals("'fixedRateTest'", payloadExpression.getExpressionString());
}
@Test
public void cron() {
TriggeredMessagePublisher publisher = context.getBean("cronPublisher", TriggeredMessagePublisher.class);
assertFalse(publisher.isAutoStartup());
DirectFieldAccessor publisherAccessor = new DirectFieldAccessor(publisher);
Trigger trigger = (Trigger) publisherAccessor.getPropertyValue("trigger");
assertEquals(CronTrigger.class, trigger.getClass());
assertEquals("7 6 5 4 3 ?", new DirectFieldAccessor(new DirectFieldAccessor(
trigger).getPropertyValue("sequenceGenerator")).getPropertyValue("expression"));
assertEquals(context.getBean("cronChannel"), publisherAccessor.getPropertyValue("outputChannel"));
Expression payloadExpression = (Expression) new DirectFieldAccessor(
publisherAccessor.getPropertyValue("task")).getPropertyValue("payloadExpression");
assertEquals("'cronTest'", payloadExpression.getExpressionString());
}
@Test
public void triggerRef() {
TriggeredMessagePublisher publisher = context.getBean("triggerRefPublisher", TriggeredMessagePublisher.class);
assertTrue(publisher.isAutoStartup());
DirectFieldAccessor publisherAccessor = new DirectFieldAccessor(publisher);
Trigger trigger = (Trigger) publisherAccessor.getPropertyValue("trigger");
assertEquals(context.getBean("customTrigger"), trigger);
assertEquals(context.getBean("triggerRefChannel"), publisherAccessor.getPropertyValue("outputChannel"));
Expression payloadExpression = (Expression) new DirectFieldAccessor(
publisherAccessor.getPropertyValue("task")).getPropertyValue("payloadExpression");
assertEquals("'triggerRefTest'", payloadExpression.getExpressionString());
}
}