Refactored PollingDispatcher into separate ChannelPoller and SourcePoller implementations with an AbstractPoller base class.
This commit is contained in:
@@ -44,13 +44,14 @@ import org.springframework.integration.channel.ChannelRegistryAware;
|
||||
import org.springframework.integration.channel.DefaultChannelRegistry;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.MessagePublishingErrorHandler;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcher;
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.endpoint.AbstractPoller;
|
||||
import org.springframework.integration.endpoint.ChannelPoller;
|
||||
import org.springframework.integration.endpoint.DefaultEndpointRegistry;
|
||||
import org.springframework.integration.endpoint.EndpointRegistry;
|
||||
import org.springframework.integration.endpoint.MessageEndpoint;
|
||||
import org.springframework.integration.endpoint.MessagingGateway;
|
||||
import org.springframework.integration.message.MessageSource;
|
||||
import org.springframework.integration.message.PollableSource;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
@@ -77,7 +78,7 @@ public class DefaultMessageBus implements MessageBus, ApplicationContextAware, A
|
||||
|
||||
private final EndpointRegistry endpointRegistry = new DefaultEndpointRegistry();
|
||||
|
||||
private final Set<PollingDispatcher> pollingDispatchers = new CopyOnWriteArraySet<PollingDispatcher>();
|
||||
private final Set<AbstractPoller> pollers = new CopyOnWriteArraySet<AbstractPoller>();
|
||||
|
||||
private volatile Schedule defaultPollerSchedule = new PollingSchedule(0);
|
||||
|
||||
@@ -273,17 +274,17 @@ public class DefaultMessageBus implements MessageBus, ApplicationContextAware, A
|
||||
}
|
||||
if (source instanceof SubscribableSource) {
|
||||
((SubscribableSource) source).subscribe(endpoint);
|
||||
if (source instanceof PollingDispatcher) {
|
||||
PollingDispatcher poller = (PollingDispatcher) source;
|
||||
this.pollingDispatchers.add(poller);
|
||||
if (source instanceof AbstractPoller) {
|
||||
AbstractPoller poller = (AbstractPoller) source;
|
||||
this.pollers.add(poller);
|
||||
this.taskScheduler.schedule(poller);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (source instanceof PollableSource) {
|
||||
PollingDispatcher poller = new PollingDispatcher((PollableSource<?>) source, this.defaultPollerSchedule);
|
||||
else if (source instanceof PollableChannel) {
|
||||
ChannelPoller poller = new ChannelPoller((PollableChannel) source, this.defaultPollerSchedule);
|
||||
poller.subscribe(endpoint);
|
||||
this.pollingDispatchers.add(poller);
|
||||
this.pollers.add(poller);
|
||||
this.taskScheduler.schedule(poller);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
@@ -306,10 +307,10 @@ public class DefaultMessageBus implements MessageBus, ApplicationContextAware, A
|
||||
|
||||
public void deactivateEndpoint(MessageEndpoint endpoint) {
|
||||
Assert.notNull(endpoint, "'endpoint' must not be null");
|
||||
for (PollingDispatcher poller : this.pollingDispatchers) {
|
||||
boolean removed = poller.unsubscribe(endpoint);
|
||||
for (AbstractPoller poller : this.pollers) {
|
||||
boolean removed = ((AbstractPoller) poller).unsubscribe(endpoint);
|
||||
if (removed && this.logger.isInfoEnabled()) {
|
||||
logger.info("removed endpoint '" + endpoint + "' from dispatcher '" + poller + "'");
|
||||
logger.info("unsubscribed endpoint '" + endpoint + "' from poller '" + poller + "'");
|
||||
}
|
||||
}
|
||||
if (endpoint instanceof Lifecycle) {
|
||||
|
||||
@@ -43,7 +43,7 @@ public class DirectChannel extends AbstractMessageChannel implements Subscribabl
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
return this.dispatcher.send(message);
|
||||
return this.dispatcher.dispatch(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class PublishSubscribeChannel extends AbstractMessageChannel implements S
|
||||
|
||||
@Override
|
||||
protected boolean doSend(Message<?> message, long timeout) {
|
||||
return this.dispatcher.send(message);
|
||||
return this.dispatcher.dispatch(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public abstract class AbstractEndpointParser extends AbstractSingleBeanDefinitio
|
||||
}
|
||||
Element pollerElement = DomUtils.getChildElementByTagName(element, POLLER_ELEMENT);
|
||||
if (pollerElement != null) {
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parsePoller(inputChannel, pollerElement, parserContext);
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parseChannelPoller(inputChannel, pollerElement, parserContext);
|
||||
builder.addPropertyReference("source", pollerBeanName);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -74,7 +74,7 @@ public class ChannelAdapterParser extends AbstractBeanDefinitionParser {
|
||||
}
|
||||
adapterBuilder = BeanDefinitionBuilder.genericBeanDefinition(InboundChannelAdapter.class);
|
||||
if (pollerElement != null) {
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parsePoller(source, pollerElement, parserContext);
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parseSourcePoller(source, pollerElement, parserContext);
|
||||
adapterBuilder.addPropertyReference("source", pollerBeanName);
|
||||
}
|
||||
else {
|
||||
@@ -100,7 +100,7 @@ public class ChannelAdapterParser extends AbstractBeanDefinitionParser {
|
||||
if (!StringUtils.hasText(channelName)) {
|
||||
throw new ConfigurationException("outbound channel-adapter with a 'poller' requires a 'channel' to poll");
|
||||
}
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parsePoller(channelName, pollerElement, parserContext);
|
||||
String pollerBeanName = IntegrationNamespaceUtils.parseChannelPoller(channelName, pollerElement, parserContext);
|
||||
adapterBuilder.addPropertyReference("source", pollerBeanName);
|
||||
}
|
||||
else if (StringUtils.hasText(channelName)) {
|
||||
|
||||
@@ -26,11 +26,14 @@ import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.endpoint.ChannelPoller;
|
||||
import org.springframework.integration.endpoint.SourcePoller;
|
||||
import org.springframework.integration.scheduling.CronSchedule;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* Shared utility methods for integration namespace parsers.
|
||||
@@ -135,15 +138,32 @@ public abstract class IntegrationNamespaceUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a "poller" element and return the bean name of the poller instance.
|
||||
* Parse a "poller" element to create a ChannelPoller and return the bean name of the poller instance.
|
||||
*
|
||||
* @param channelBeanName the name of the PollableChannel bean
|
||||
* @param element the "poller" element to parse
|
||||
* @param parserContext the parserContext for registering a newly created bean definition
|
||||
* @return the name of the ChannelPoller bean definition
|
||||
*/
|
||||
public static String parseChannelPoller(String channelBeanName, Element element, ParserContext parserContext) {
|
||||
return parsePoller(channelBeanName, element, parserContext, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a "poller" element to create a SourcePoller and return the bean name of the poller instance.
|
||||
*
|
||||
* @param sourceBeanName the name of the PollableSource bean
|
||||
* @param element the "poller" element to parse
|
||||
* @param parserContext the parserContext for registering a newly created bean definition
|
||||
* @return the name of the poller bean definition
|
||||
*/
|
||||
public static String parsePoller(String sourceBeanName, Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PollingDispatcherFactoryBean.class);
|
||||
public static String parseSourcePoller(String sourceBeanName, Element element, ParserContext parserContext) {
|
||||
return parsePoller(sourceBeanName, element, parserContext, false);
|
||||
}
|
||||
|
||||
private static String parsePoller(String sourceBeanName, Element element, ParserContext parserContext, boolean isChannel) {
|
||||
Class<?> beanClass = isChannel ? ChannelPoller.class : SourcePoller.class;
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
|
||||
Schedule schedule = null;
|
||||
if (!(StringUtils.hasText(element.getAttribute("period")) ^ StringUtils.hasText(element.getAttribute("cron")))) {
|
||||
throw new ConfigurationException("A <poller> element must define either a period "
|
||||
@@ -170,13 +190,15 @@ public abstract class IntegrationNamespaceUtils {
|
||||
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
|
||||
if (txElement != null) {
|
||||
builder.addPropertyReference("transactionManager", txElement.getAttribute("transaction-manager"));
|
||||
builder.addPropertyValue("propagationBehaviorName", txElement.getAttribute("propagation"));
|
||||
builder.addPropertyValue("isolationLevelName", txElement.getAttribute("isolation"));
|
||||
builder.addPropertyValue("propagationBehaviorName",
|
||||
DefaultTransactionDefinition.PREFIX_PROPAGATION + txElement.getAttribute("propagation"));
|
||||
builder.addPropertyValue("isolationLevelName",
|
||||
DefaultTransactionDefinition.PREFIX_ISOLATION + txElement.getAttribute("isolation"));
|
||||
builder.addPropertyValue("transactionTimeout", txElement.getAttribute("timeout"));
|
||||
builder.addPropertyValue("transactionReadOnly", txElement.getAttribute("read-only"));
|
||||
}
|
||||
builder.addPropertyReference("source", sourceBeanName);
|
||||
builder.addPropertyValue("schedule", schedule);
|
||||
builder.addConstructorArgReference(sourceBeanName);
|
||||
builder.addConstructorArgValue(schedule);
|
||||
setValueIfAttributeDefined(builder, element, "receive-timeout");
|
||||
setValueIfAttributeDefined(builder, element, "send-timeout");
|
||||
setValueIfAttributeDefined(builder, element, "max-messages-per-poll");
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* 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.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.integration.ConfigurationException;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcher;
|
||||
import org.springframework.integration.dispatcher.SimpleDispatcher;
|
||||
import org.springframework.integration.message.AsyncMessageExchangeTemplate;
|
||||
import org.springframework.integration.message.MessageExchangeTemplate;
|
||||
import org.springframework.integration.message.MessageSource;
|
||||
import org.springframework.integration.message.PollableSource;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PollingDispatcherFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
private volatile PollingDispatcher poller;
|
||||
|
||||
private volatile MessageSource<?> source;
|
||||
|
||||
private volatile Schedule schedule;
|
||||
|
||||
private volatile long receiveTimeout = -1;
|
||||
|
||||
private volatile long sendTimeout = -1;
|
||||
|
||||
private volatile int maxMessagesPerPoll = -1;
|
||||
|
||||
private volatile TaskExecutor taskExecutor;
|
||||
|
||||
private volatile PlatformTransactionManager transactionManager;
|
||||
|
||||
private volatile String propagationBehaviorName;
|
||||
|
||||
private volatile String isolationLevelName;
|
||||
|
||||
private volatile int transactionTimeout;
|
||||
|
||||
private volatile boolean transactionReadOnly;
|
||||
|
||||
private volatile boolean validated;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public void setSource(MessageSource<?> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public void setSchedule(Schedule schedule) {
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
public void setSendTimeout(long sendTimeout) {
|
||||
this.sendTimeout = sendTimeout;
|
||||
}
|
||||
|
||||
public void setMaxMessagesPerPoll(int maxMessagesPerPoll) {
|
||||
this.maxMessagesPerPoll = maxMessagesPerPoll;
|
||||
}
|
||||
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setPropagationBehaviorName(String propagationBehaviorName) {
|
||||
this.propagationBehaviorName = propagationBehaviorName;
|
||||
}
|
||||
|
||||
public void setIsolationLevelName(String isolationLevelName) {
|
||||
this.isolationLevelName = isolationLevelName;
|
||||
}
|
||||
|
||||
public void setTransactionTimeout(int transactionTimeout) {
|
||||
this.transactionTimeout = transactionTimeout;
|
||||
}
|
||||
|
||||
public void setTransactionReadOnly(boolean transactionReadOnly) {
|
||||
this.transactionReadOnly = transactionReadOnly;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.source == null) {
|
||||
throw new ConfigurationException("source is required");
|
||||
}
|
||||
if (!(this.source instanceof PollableSource)) {
|
||||
throw new BeanCreationException("Poller requires a PollableSource, but actual type of '"
|
||||
+ this.source + "' is [" + this.source.getClass() + "]");
|
||||
}
|
||||
this.validated = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
if (!this.initialized) {
|
||||
this.initPoller();
|
||||
}
|
||||
return this.poller;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return PollingDispatcher.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initPoller() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
if (!this.validated) {
|
||||
this.afterPropertiesSet();
|
||||
}
|
||||
if (this.schedule == null) {
|
||||
this.schedule = new PollingSchedule(0);
|
||||
}
|
||||
MessageExchangeTemplate template = this.createMessageExchangeTemplate();
|
||||
this.poller = new PollingDispatcher((PollableSource<?>) this.source, this.schedule, new SimpleDispatcher(), template);
|
||||
this.poller.setMaxMessagesPerPoll(this.maxMessagesPerPoll);
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private MessageExchangeTemplate createMessageExchangeTemplate() {
|
||||
MessageExchangeTemplate template = (this.taskExecutor != null) ?
|
||||
new AsyncMessageExchangeTemplate(this.taskExecutor) : new MessageExchangeTemplate();
|
||||
template.setTransactionManager(this.transactionManager);
|
||||
template.setPropagationBehaviorName(DefaultTransactionDefinition.PREFIX_PROPAGATION + this.propagationBehaviorName);
|
||||
template.setIsolationLevelName(DefaultTransactionDefinition.PREFIX_ISOLATION + this.isolationLevelName);
|
||||
template.setTransactionTimeout(this.transactionTimeout);
|
||||
template.setTransactionReadOnly(this.transactionReadOnly);
|
||||
template.setReceiveTimeout(this.receiveTimeout);
|
||||
template.setSendTimeout(this.sendTimeout);
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,9 +26,9 @@ import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.channel.ChannelRegistry;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcher;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.AbstractInOutEndpoint;
|
||||
import org.springframework.integration.endpoint.ChannelPoller;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -93,7 +93,7 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
schedule.setInitialDelay(pollerAnnotation.initialDelay());
|
||||
schedule.setFixedRate(pollerAnnotation.fixedRate());
|
||||
schedule.setTimeUnit(pollerAnnotation.timeUnit());
|
||||
PollingDispatcher poller = new PollingDispatcher((PollableChannel) inputChannel, schedule);
|
||||
ChannelPoller poller = new ChannelPoller((PollableChannel) inputChannel, schedule);
|
||||
poller.setMaxMessagesPerPoll(pollerAnnotation.maxMessagesPerPoll());
|
||||
endpoint.setSource(poller);
|
||||
}
|
||||
|
||||
@@ -26,14 +26,15 @@ import org.springframework.integration.bus.MessageBus;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.dispatcher.PollingDispatcher;
|
||||
import org.springframework.integration.endpoint.ChannelPoller;
|
||||
import org.springframework.integration.endpoint.InboundChannelAdapter;
|
||||
import org.springframework.integration.endpoint.MessageEndpoint;
|
||||
import org.springframework.integration.endpoint.OutboundChannelAdapter;
|
||||
import org.springframework.integration.endpoint.SourcePoller;
|
||||
import org.springframework.integration.handler.MethodInvokingTarget;
|
||||
import org.springframework.integration.message.MethodInvokingSource;
|
||||
import org.springframework.integration.message.PollableSource;
|
||||
import org.springframework.integration.scheduling.PollingSchedule;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -88,7 +89,15 @@ public class ChannelAdapterAnnotationPostProcessor implements MethodAnnotationPo
|
||||
throw new ConfigurationException("The @Poller annotation is required (at method-level) "
|
||||
+ "when using the @ChannelAdapter annotation with a no-arg method.");
|
||||
}
|
||||
PollingDispatcher poller = this.createPoller(source, pollerAnnotation);
|
||||
Schedule schedule = this.createSchedule(pollerAnnotation);
|
||||
SourcePoller poller = new SourcePoller(source, schedule);
|
||||
int maxMessagesPerPoll = pollerAnnotation.maxMessagesPerPoll();
|
||||
if (maxMessagesPerPoll == -1) {
|
||||
// the default is 1 since a MethodInvokingSource might return a non-null value
|
||||
// every time it is invoked, thus producing an infinite number of messages per poll
|
||||
maxMessagesPerPoll = 1;
|
||||
}
|
||||
poller.setMaxMessagesPerPoll(maxMessagesPerPoll);
|
||||
InboundChannelAdapter adapter = new InboundChannelAdapter();
|
||||
adapter.setSource(poller);
|
||||
adapter.setChannel(channel);
|
||||
@@ -99,9 +108,10 @@ public class ChannelAdapterAnnotationPostProcessor implements MethodAnnotationPo
|
||||
private OutboundChannelAdapter createOutboundChannelAdapter(MethodInvokingTarget target, MessageChannel channel, Poller pollerAnnotation) {
|
||||
OutboundChannelAdapter adapter = new OutboundChannelAdapter(target);
|
||||
if (channel instanceof PollableChannel) {
|
||||
PollingDispatcher poller = (pollerAnnotation != null)
|
||||
? this.createPoller((PollableChannel) channel, pollerAnnotation)
|
||||
: new PollingDispatcher((PollableSource<?>) channel, new PollingSchedule(0));
|
||||
Schedule schedule = (pollerAnnotation != null)
|
||||
? this.createSchedule(pollerAnnotation)
|
||||
: new PollingSchedule(0);
|
||||
ChannelPoller poller = new ChannelPoller((PollableChannel) channel, schedule);
|
||||
adapter.setSource(poller);
|
||||
}
|
||||
else {
|
||||
@@ -111,20 +121,12 @@ public class ChannelAdapterAnnotationPostProcessor implements MethodAnnotationPo
|
||||
return adapter;
|
||||
}
|
||||
|
||||
private PollingDispatcher createPoller(PollableSource<?> source, Poller pollerAnnotation) {
|
||||
private Schedule createSchedule(Poller pollerAnnotation) {
|
||||
PollingSchedule schedule = new PollingSchedule(pollerAnnotation.period());
|
||||
schedule.setInitialDelay(pollerAnnotation.initialDelay());
|
||||
schedule.setFixedRate(pollerAnnotation.fixedRate());
|
||||
schedule.setTimeUnit(pollerAnnotation.timeUnit());
|
||||
PollingDispatcher poller = new PollingDispatcher((PollableSource<?>) source, schedule);
|
||||
int maxMessagesPerPoll = pollerAnnotation.maxMessagesPerPoll();
|
||||
if (maxMessagesPerPoll == -1) {
|
||||
// the default is 1 since a MethodInvokingSource might return a non-null value
|
||||
// every time it is invoked, thus producing an infinite number of messages per poll
|
||||
maxMessagesPerPoll = 1;
|
||||
}
|
||||
poller.setMaxMessagesPerPoll(maxMessagesPerPoll);
|
||||
return poller;
|
||||
return schedule;
|
||||
}
|
||||
|
||||
private boolean hasReturnValue(Method method) {
|
||||
|
||||
@@ -40,12 +40,6 @@ public abstract class AbstractDispatcher implements MessageDispatcher {
|
||||
private volatile TaskExecutor taskExecutor;
|
||||
|
||||
|
||||
// TODO: dispatcher should not implement channel, need to move TX support into the poller
|
||||
// so that the messageExchangeTemplate is not required for sending to a dispatcher
|
||||
public String getName() {
|
||||
return "dispatcher";
|
||||
}
|
||||
|
||||
public boolean subscribe(MessageEndpoint endpoint) {
|
||||
return this.endpoints.add(endpoint);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BroadcastingDispatcher extends AbstractDispatcher {
|
||||
this.applySequence = applySequence;
|
||||
}
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
public boolean dispatch(Message<?> message) {
|
||||
int sequenceNumber = 1;
|
||||
int sequenceSize = this.endpoints.size();
|
||||
for (final MessageEndpoint endpoint : this.endpoints) {
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.integration.dispatcher;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.endpoint.MessageEndpoint;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
|
||||
@@ -26,12 +24,8 @@ import org.springframework.integration.message.SubscribableSource;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public interface MessageDispatcher extends MessageChannel, SubscribableSource {
|
||||
public interface MessageDispatcher extends SubscribableSource {
|
||||
|
||||
boolean send(Message<?> message);
|
||||
|
||||
boolean subscribe(MessageEndpoint endpoint);
|
||||
|
||||
boolean unsubscribe(MessageEndpoint endpoint);
|
||||
boolean dispatch(Message<?> message);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* 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.dispatcher;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.endpoint.MessageEndpoint;
|
||||
import org.springframework.integration.message.BlockingSource;
|
||||
import org.springframework.integration.message.MessageExchangeTemplate;
|
||||
import org.springframework.integration.message.PollableSource;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
import org.springframework.integration.scheduling.SchedulableTask;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class PollingDispatcher implements SchedulableTask, SubscribableSource {
|
||||
|
||||
public final static int MAX_MESSAGES_UNBOUNDED = -1;
|
||||
|
||||
public final static long DEFAULT_RECEIVE_TIMEOUT = 1000;
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final PollableSource<?> source;
|
||||
|
||||
private final MessageDispatcher dispatcher;
|
||||
|
||||
private final Schedule schedule;
|
||||
|
||||
private final MessageExchangeTemplate messageExchangeTemplate;
|
||||
|
||||
private volatile int maxMessagesPerPoll = MAX_MESSAGES_UNBOUNDED;
|
||||
|
||||
|
||||
/**
|
||||
* Create a PollingDispatcher for the provided {@link PollableSource}.
|
||||
* It can be scheduled according to the specified {@link Schedule}.
|
||||
*/
|
||||
public PollingDispatcher(PollableSource<?> source, Schedule schedule) {
|
||||
this(source, schedule, null, null);
|
||||
}
|
||||
|
||||
public PollingDispatcher(PollableSource<?> source, Schedule schedule, MessageDispatcher dispatcher) {
|
||||
this(source, schedule, dispatcher, null);
|
||||
}
|
||||
|
||||
public PollingDispatcher(PollableSource<?> source, Schedule schedule, MessageDispatcher dispatcher, MessageExchangeTemplate messageExchangeTemplate) {
|
||||
Assert.notNull(source, "source must not be null");
|
||||
this.source = source;
|
||||
this.schedule = schedule;
|
||||
this.dispatcher = (dispatcher != null)
|
||||
? dispatcher : new SimpleDispatcher();
|
||||
this.messageExchangeTemplate = (messageExchangeTemplate != null)
|
||||
? messageExchangeTemplate : createDefaultTemplate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the timeout to use when receiving from the source (in milliseconds).
|
||||
* Note that this value will only be applicable if the source is an instance
|
||||
* of {@link BlockingSource}.
|
||||
* <p/>
|
||||
* A negative value indicates that receive calls should block indefinitely,
|
||||
* and that is the default behavior.
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.messageExchangeTemplate.setReceiveTimeout(receiveTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of messages to receive for each poll.
|
||||
* A non-positive value indicates that polling should repeat as long
|
||||
* as non-null messages are being received and successfully sent.
|
||||
*
|
||||
* <p>The default is unbounded.
|
||||
*
|
||||
* @see #MAX_MESSAGES_UNBOUNDED
|
||||
*/
|
||||
public void setMaxMessagesPerPoll(int maxMessagesPerPoll) {
|
||||
this.maxMessagesPerPoll = maxMessagesPerPoll;
|
||||
}
|
||||
|
||||
public boolean subscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.subscribe(endpoint);
|
||||
}
|
||||
|
||||
public boolean unsubscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.unsubscribe(endpoint);
|
||||
}
|
||||
|
||||
public Schedule getSchedule() {
|
||||
return this.schedule;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
int count = 0;
|
||||
while (this.maxMessagesPerPoll < 0 || count < this.maxMessagesPerPoll) {
|
||||
if (!this.messageExchangeTemplate.receiveAndForward(this.source, this.dispatcher)) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("poller for source '" + this.source + "' sent " + count
|
||||
+ " messages to target '" + this.dispatcher + "'");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + " [source = " + this.source
|
||||
+ ", dispatcher = [" + this.dispatcher + "]";
|
||||
}
|
||||
|
||||
private MessageExchangeTemplate createDefaultTemplate() {
|
||||
MessageExchangeTemplate template = new MessageExchangeTemplate();
|
||||
template.setReceiveTimeout(DEFAULT_RECEIVE_TIMEOUT);
|
||||
template.setSendTimeout(-1);
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import org.springframework.integration.message.MessageRejectedException;
|
||||
*/
|
||||
public class SimpleDispatcher extends AbstractDispatcher {
|
||||
|
||||
public boolean send(Message<?> message) {
|
||||
public boolean dispatch(Message<?> message) {
|
||||
if (this.endpoints.size() == 0) {
|
||||
throw new MessageDeliveryException(message, "Dispatcher has no subscribers.");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.endpoint;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
import org.springframework.integration.scheduling.SchedulableTask;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class AbstractPoller implements SubscribableSource, SchedulableTask, InitializingBean {
|
||||
|
||||
public static final int MAX_MESSAGES_UNBOUNDED = -1;
|
||||
|
||||
|
||||
private final Schedule schedule;
|
||||
|
||||
private volatile long maxMessagesPerPoll = MAX_MESSAGES_UNBOUNDED;
|
||||
|
||||
private volatile TaskExecutor taskExecutor;
|
||||
|
||||
private volatile PlatformTransactionManager transactionManager;
|
||||
|
||||
private volatile TransactionTemplate transactionTemplate;
|
||||
|
||||
private volatile String propagationBehaviorName = "PROPAGATION_REQUIRED";
|
||||
|
||||
private volatile String isolationLevelName = "ISOLATION_DEFAULT";
|
||||
|
||||
private volatile int transactionTimeout = -1;
|
||||
|
||||
private volatile boolean readOnly = false;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
private final Object initializationMonitor = new Object();
|
||||
|
||||
|
||||
public AbstractPoller(Schedule schedule) {
|
||||
Assert.notNull(schedule, "schedule must not be null");
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
|
||||
public Schedule getSchedule() {
|
||||
return this.schedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of messages to receive for each poll.
|
||||
* A non-positive value indicates that polling should repeat as long
|
||||
* as non-null messages are being received and successfully sent.
|
||||
*
|
||||
* <p>The default is unbounded.
|
||||
*
|
||||
* @see #MAX_MESSAGES_UNBOUNDED
|
||||
*/
|
||||
public void setMaxMessagesPerPoll(int maxMessagesPerPoll) {
|
||||
this.maxMessagesPerPoll = maxMessagesPerPoll;
|
||||
}
|
||||
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a transaction manager to use for all exchange operations.
|
||||
* If none is provided, then the operations will occur without any
|
||||
* transactional behavior (i.e. there is no default transaction manager).
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setPropagationBehaviorName(String propagationBehaviorName) {
|
||||
this.propagationBehaviorName = propagationBehaviorName;
|
||||
}
|
||||
|
||||
public void setIsolationLevelName(String isolationLevelName) {
|
||||
this.isolationLevelName = isolationLevelName;
|
||||
}
|
||||
|
||||
public void setTransactionTimeout(int transactionTimeout) {
|
||||
this.transactionTimeout = transactionTimeout;
|
||||
}
|
||||
|
||||
public void setTransactionReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
private TransactionTemplate getTransactionTemplate() {
|
||||
if (!this.initialized) {
|
||||
this.afterPropertiesSet();
|
||||
}
|
||||
return this.transactionTemplate;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
if (this.transactionManager != null) {
|
||||
TransactionTemplate template = new TransactionTemplate(this.transactionManager);
|
||||
template.setPropagationBehaviorName(this.propagationBehaviorName);
|
||||
template.setIsolationLevelName(this.isolationLevelName);
|
||||
template.setTimeout(this.transactionTimeout);
|
||||
template.setReadOnly(this.readOnly);
|
||||
this.transactionTemplate = template;
|
||||
}
|
||||
this.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (this.taskExecutor != null) {
|
||||
this.taskExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
poll();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
poll();
|
||||
}
|
||||
}
|
||||
|
||||
private void poll() {
|
||||
int count = 0;
|
||||
while (this.maxMessagesPerPoll < 0 || count < this.maxMessagesPerPoll) {
|
||||
if (!this.pollWithinTransaction()) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean pollWithinTransaction() {
|
||||
TransactionTemplate txTemplate = this.getTransactionTemplate();
|
||||
if (txTemplate != null) {
|
||||
return (Boolean) txTemplate.execute(new TransactionCallback() {
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
return doPoll();
|
||||
}
|
||||
});
|
||||
}
|
||||
return doPoll();
|
||||
}
|
||||
|
||||
protected abstract boolean doPoll();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.endpoint;
|
||||
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.dispatcher.SimpleDispatcher;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class ChannelPoller extends AbstractPoller implements SubscribableSource {
|
||||
|
||||
private final PollableChannel channel;
|
||||
|
||||
private volatile long receiveTimeout = 1000;
|
||||
|
||||
private final SimpleDispatcher dispatcher = new SimpleDispatcher();
|
||||
|
||||
|
||||
public ChannelPoller(PollableChannel channel, Schedule schedule) {
|
||||
super(schedule);
|
||||
Assert.notNull(channel, "channel must not be null");
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the timeout to use when receiving from the channel (in milliseconds).
|
||||
* A negative value indicates that receive calls should block indefinitely.
|
||||
* The default value is 1000 (1 second).
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
public boolean subscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.subscribe(endpoint);
|
||||
}
|
||||
|
||||
public boolean unsubscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.unsubscribe(endpoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doPoll() {
|
||||
Message<?> message = (this.receiveTimeout >= 0)
|
||||
? this.channel.receive(this.receiveTimeout)
|
||||
: this.channel.receive();
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
return this.dispatcher.dispatch(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.endpoint;
|
||||
|
||||
import org.springframework.integration.dispatcher.SimpleDispatcher;
|
||||
import org.springframework.integration.message.BlockingSource;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.PollableSource;
|
||||
import org.springframework.integration.message.SubscribableSource;
|
||||
import org.springframework.integration.scheduling.Schedule;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class SourcePoller extends AbstractPoller implements SubscribableSource {
|
||||
|
||||
private final PollableSource<?> source;
|
||||
|
||||
private final SimpleDispatcher dispatcher = new SimpleDispatcher();
|
||||
|
||||
private volatile long receiveTimeout = 1000;
|
||||
|
||||
|
||||
public SourcePoller(PollableSource<?> source, Schedule schedule) {
|
||||
super(schedule);
|
||||
Assert.notNull(source, "source must not be null");
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the timeout to use when receiving from the source (in milliseconds).
|
||||
* This value will only apply if the source is a {@link BlockingSource}.
|
||||
* <p>
|
||||
* A negative value indicates that receive calls should block indefinitely.
|
||||
* The default value is 1000 (1 second).
|
||||
*/
|
||||
public void setReceiveTimeout(long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
public boolean subscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.subscribe(endpoint);
|
||||
}
|
||||
|
||||
public boolean unsubscribe(MessageEndpoint endpoint) {
|
||||
return this.dispatcher.unsubscribe(endpoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doPoll() {
|
||||
Message<?> message = (this.receiveTimeout >= 0 && this.source instanceof BlockingSource)
|
||||
? ((BlockingSource<?>) this.source).receive(this.receiveTimeout)
|
||||
: this.source.receive();
|
||||
if (message == null) {
|
||||
return false;
|
||||
}
|
||||
return this.dispatcher.dispatch(message);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user