From 467e0f803dbee323f987fbbd4d7c8802f3b12935 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Thu, 24 Apr 2008 14:25:34 +0000 Subject: [PATCH] PollingSourceEndpoint delegates to PollingDispatcher. Added sendTimeout to SimpleDispatcher. --- .../dispatcher/PollingDispatcher.java | 58 ++++++++++-- .../dispatcher/SimpleDispatcher.java | 14 ++- .../integration/dispatcher/SourcePoller.java | 90 ------------------- .../endpoint/PollingSourceEndpoint.java | 74 +++------------ .../config/EndpointParserTests.java | 2 +- ...Tests.java => PollingDispatcherTests.java} | 28 +++--- 6 files changed, 88 insertions(+), 178 deletions(-) delete mode 100644 spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SourcePoller.java rename spring-integration-core/src/test/java/org/springframework/integration/dispatcher/{SourcePollerTests.java => PollingDispatcherTests.java} (83%) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/PollingDispatcher.java b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/PollingDispatcher.java index 8b55f3509c..d718630009 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/PollingDispatcher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/PollingDispatcher.java @@ -16,11 +16,15 @@ package org.springframework.integration.dispatcher; +import java.util.LinkedList; import java.util.List; import org.springframework.integration.channel.DispatcherPolicy; import org.springframework.integration.channel.MessageChannel; +import org.springframework.integration.message.BlockingSource; import org.springframework.integration.message.Message; +import org.springframework.integration.message.MessageDeliveryAware; +import org.springframework.integration.message.MessageDeliveryException; import org.springframework.integration.message.PollableSource; import org.springframework.integration.scheduling.MessagingTask; import org.springframework.integration.scheduling.Schedule; @@ -28,13 +32,24 @@ import org.springframework.util.Assert; /** * A subclass of {@link SimpleDispatcher} that adds message retrieval - * capabilities and may be scheduled to run as a task. + * capabilities and may be scheduled to run as a task. It polls a source for + * {@link Message Messages}. The number of messages retrieved per poll is + * limited by the 'maxMessagesPerTask' property of the provided + * {@link DispatcherPolicy}, and the timeout for each receive call is + * determined by the policy's 'receiveTimeout' property. In + * general, it is recommended to use a value of 1 (the default) for + * 'maxMessagesPerTask' whenever a significant timeout is provided. Otherwise + * the poller may be holding on to available messages while waiting for + * additional messages. Note that the 'timeout' value is only relevant if the + * specified source is an implementation of {@link BlockingSource}. The default + * timeout value is 0 indicating that the method should return immediately + * rather than waiting for a {@link Message} to become available. * * @author Mark Fisher */ public class PollingDispatcher extends SimpleDispatcher implements MessagingTask { - private final SourcePoller poller; + private final PollableSource source; private final Schedule schedule; @@ -46,12 +61,9 @@ public class PollingDispatcher extends SimpleDispatcher implements MessagingTask public PollingDispatcher(PollableSource source, DispatcherPolicy dispatcherPolicy, Schedule schedule) { super(dispatcherPolicy); Assert.notNull(source, "source must not be null"); - this.poller = new SourcePoller(source); - if (!(source instanceof MessageChannel)) { - this.poller.setMaxMessagesPerTask(dispatcherPolicy.getMaxMessagesPerTask()); - this.poller.setTimeout(dispatcherPolicy.getReceiveTimeout()); - } + this.source = source; this.schedule = schedule; + this.dispatcherPolicy.setReceiveTimeout(0); } @@ -59,10 +71,38 @@ public class PollingDispatcher extends SimpleDispatcher implements MessagingTask return this.schedule; } + public List> poll() { + List> messages = new LinkedList>(); + int limit = this.dispatcherPolicy.getMaxMessagesPerTask(); + while (messages.size() < limit) { + Message message = null; + long timeout = this.dispatcherPolicy.getReceiveTimeout(); + if (this.source instanceof BlockingSource && timeout >= 0) { + message = ((BlockingSource) this.source).receive(timeout); + } + else { + message = this.source.receive(); + } + if (message == null) { + return messages; + } + messages.add(message); + } + return messages; + } + public void run() { - List> messages = this.poller.poll(); + List> messages = this.poll(); for (Message message : messages) { - this.dispatch(message); + boolean sent = this.dispatch(message); + if (this.source instanceof MessageDeliveryAware) { + if (sent) { + ((MessageDeliveryAware) this.source).onSend(message); + } + else { + ((MessageDeliveryAware) this.source).onFailure(new MessageDeliveryException(message, "failed to send message")); + } + } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SimpleDispatcher.java b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SimpleDispatcher.java index 953c9342ff..233645776e 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SimpleDispatcher.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SimpleDispatcher.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.integration.channel.DispatcherPolicy; import org.springframework.integration.handler.MessageHandlerNotRunningException; import org.springframework.integration.handler.MessageHandlerRejectedExecutionException; +import org.springframework.integration.message.BlockingTarget; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageDeliveryException; import org.springframework.integration.message.SubscribableSource; @@ -43,14 +44,20 @@ public class SimpleDispatcher implements MessageDispatcher, SubscribableSource { private final List targets = new CopyOnWriteArrayList(); - private final DispatcherPolicy dispatcherPolicy; + protected final DispatcherPolicy dispatcherPolicy; + + private volatile long sendTimeout; public SimpleDispatcher(DispatcherPolicy dispatcherPolicy) { - this.dispatcherPolicy = dispatcherPolicy; + this.dispatcherPolicy = (dispatcherPolicy != null) ? dispatcherPolicy : new DispatcherPolicy(); } + public void setSendTimeout(long sendTimeout) { + this.sendTimeout = sendTimeout; + } + public boolean subscribe(Target target) { return this.targets.add(target); } @@ -88,7 +95,8 @@ public class SimpleDispatcher implements MessageDispatcher, SubscribableSource { while (iter.hasNext()) { Target target = iter.next(); try { - boolean sent = target.send(message); + boolean sent = (target instanceof BlockingTarget && this.sendTimeout >= 0) ? + ((BlockingTarget) target).send(message, this.sendTimeout) : target.send(message); if (!this.dispatcherPolicy.isPublishSubscribe() && sent) { return true; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SourcePoller.java b/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SourcePoller.java deleted file mode 100644 index 3f3f9d701e..0000000000 --- a/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/SourcePoller.java +++ /dev/null @@ -1,90 +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 java.util.LinkedList; -import java.util.List; - -import org.springframework.integration.channel.DispatcherPolicy; -import org.springframework.integration.channel.MessageChannel; -import org.springframework.integration.message.BlockingSource; -import org.springframework.integration.message.Message; -import org.springframework.integration.message.PollableSource; -import org.springframework.util.Assert; - -/** - * Polls a source for {@link Message Messages}. The number of messages - * retrieved per poll is limited by the 'maxMessagesPerTask' - * property, and the timeout for each receive call is determined by the 'timeout' - * property. In general, it is recommended to use a value of 1 (the default) for - * 'maxMessagesPerTask' whenever a significant timeout is provided. Otherwise - * the poller may be holding on to available messages while waiting for - * additional messages. Note that the 'timeout' value is only relevant if the - * specified source is an implementation of {@link BlockingSource}. The default - * timeout value is 0 indicating that the method should return immediately - * rather than waiting for a {@link Message} to become available. - * - * @author Mark Fisher - */ -public class SourcePoller { - - private final PollableSource source; - - private volatile int maxMessagesPerTask = 1; - - private volatile long timeout = 0; - - - public SourcePoller(PollableSource source) { - Assert.notNull(source, "source must not be null"); - if (source instanceof MessageChannel) { - DispatcherPolicy dispatcherPolicy = ((MessageChannel) source).getDispatcherPolicy(); - this.setMaxMessagesPerTask(dispatcherPolicy.getMaxMessagesPerTask()); - this.setTimeout(dispatcherPolicy.getReceiveTimeout()); - } - this.source = source; - } - - - public void setMaxMessagesPerTask(int maxMessagesPerTask) { - Assert.isTrue(maxMessagesPerTask > 0, "'maxMessagesPerTask' must be a positive value"); - this.maxMessagesPerTask = maxMessagesPerTask; - } - - public void setTimeout(long timeout) { - this.timeout = timeout; - } - - public List> poll() { - List> messages = new LinkedList>(); - while (messages.size() < this.maxMessagesPerTask) { - Message message = null; - if (this.source instanceof BlockingSource && this.timeout >= 0) { - message = ((BlockingSource) this.source).receive(this.timeout); - } - else { - message = this.source.receive(); - } - if (message == null) { - return messages; - } - messages.add(message); - } - return messages; - } - -} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/PollingSourceEndpoint.java b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/PollingSourceEndpoint.java index 79c1690afc..179afd07d3 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/endpoint/PollingSourceEndpoint.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/endpoint/PollingSourceEndpoint.java @@ -16,13 +16,9 @@ package org.springframework.integration.endpoint; -import java.util.ArrayList; -import java.util.List; - +import org.springframework.integration.channel.DispatcherPolicy; import org.springframework.integration.channel.MessageChannel; -import org.springframework.integration.message.Message; -import org.springframework.integration.message.MessageDeliveryAware; -import org.springframework.integration.message.MessageDeliveryException; +import org.springframework.integration.dispatcher.PollingDispatcher; import org.springframework.integration.message.PollableSource; import org.springframework.integration.scheduling.MessagingTask; import org.springframework.integration.scheduling.PollingSchedule; @@ -37,77 +33,33 @@ import org.springframework.util.Assert; */ public class PollingSourceEndpoint extends AbstractSourceEndpoint implements MessagingTask { - private final PollingSchedule schedule; + private final DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(); - private volatile long sendTimeout = 0; - - private volatile int maxMessagesPerTask = 1; + private final PollingDispatcher dispatcher; public PollingSourceEndpoint(PollableSource source, MessageChannel channel, PollingSchedule schedule) { super(source, channel); Assert.notNull(schedule, "schedule must not be null"); - this.schedule = schedule; + this.dispatcher = new PollingDispatcher(source, this.dispatcherPolicy, schedule); + this.dispatcher.subscribe(this.getChannel()); } - public void setSendTimeout(long sendTimeout) { - this.sendTimeout = sendTimeout; - } - public void setMaxMessagesPerTask(int maxMessagesPerTask) { - Assert.isTrue(maxMessagesPerTask > 0, "'maxMessagesPerTask' must be at least one"); - this.maxMessagesPerTask = maxMessagesPerTask; + this.dispatcherPolicy.setMaxMessagesPerTask(maxMessagesPerTask); + } + + public void setSendTimeout(long sendTimeout) { + this.dispatcher.setSendTimeout(sendTimeout); } public Schedule getSchedule() { - return this.schedule; - } - - public List> poll(int limit) { - List> results = new ArrayList>(); - int count = 0; - while (count < limit) { - Message message = ((PollableSource) this.getSource()).receive(); - if (message == null) { - break; - } - results.add(message); - count++; - } - return results; - } - - protected boolean sendMessage(Message message) { - if (message == null) { - throw new IllegalArgumentException("message must not be null"); - } - boolean sent = (this.sendTimeout < 0) ? this.getChannel().send(message) : this.getChannel().send(message, this.sendTimeout); - if (this.getSource() instanceof MessageDeliveryAware) { - if (sent) { - ((MessageDeliveryAware) this.getSource()).onSend(message); - } - else { - ((MessageDeliveryAware) this.getSource()).onFailure(new MessageDeliveryException(message, "failed to send message")); - } - } - return sent; + return this.dispatcher.getSchedule(); } public void run() { - int messagesProcessed = 0; - List> messages = this.poll(this.maxMessagesPerTask); - for (Message message : messages) { - if (this.sendMessage(message)) { - messagesProcessed++; - } - else { - break; - } - } - if (logger.isDebugEnabled()) { - logger.debug("polling source task processed " + messagesProcessed + " messages"); - } + this.dispatcher.run(); } } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/EndpointParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/EndpointParserTests.java index 12e8815a50..6496db0003 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/EndpointParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/EndpointParserTests.java @@ -51,7 +51,7 @@ public class EndpointParserTests { TestHandler handler = (TestHandler) context.getBean("testHandler"); assertNull(handler.getMessageString()); channel.send(new GenericMessage(1, "test")); - handler.getLatch().await(50, TimeUnit.MILLISECONDS); + handler.getLatch().await(500, TimeUnit.MILLISECONDS); assertEquals("test", handler.getMessageString()); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/SourcePollerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/PollingDispatcherTests.java similarity index 83% rename from spring-integration-core/src/test/java/org/springframework/integration/dispatcher/SourcePollerTests.java rename to spring-integration-core/src/test/java/org/springframework/integration/dispatcher/PollingDispatcherTests.java index 53e893d0c0..a92106f0fc 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/SourcePollerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/dispatcher/PollingDispatcherTests.java @@ -33,22 +33,22 @@ import org.springframework.integration.message.StringMessage; /** * @author Mark Fisher */ -public class SourcePollerTests { +public class PollingDispatcherTests { @Test public void testSingleMessagePerRetrieval() { DispatcherPolicy dispatcherPolicy = new DispatcherPolicy(); dispatcherPolicy.setReceiveTimeout(0); MessageChannel channel = new QueueChannel(5, dispatcherPolicy); - SourcePoller poller = new SourcePoller(channel); - Collection> results = poller.poll(); + PollingDispatcher dispatcher = new PollingDispatcher(channel, null); + Collection> results = dispatcher.poll(); assertTrue(results.isEmpty()); channel.send(new StringMessage("test1"), 0); channel.send(new StringMessage("test2"), 0); - results = poller.poll(); + results = dispatcher.poll(); assertEquals(1, results.size()); assertEquals("test1", results.iterator().next().getPayload()); - results = poller.poll(); + results = dispatcher.poll(); assertEquals(1, results.size()); assertEquals("test2", results.iterator().next().getPayload()); } @@ -59,18 +59,18 @@ public class SourcePollerTests { dispatcherPolicy.setReceiveTimeout(0); dispatcherPolicy.setMaxMessagesPerTask(2); MessageChannel channel = new QueueChannel(5, dispatcherPolicy); - SourcePoller poller = new SourcePoller(channel); - Collection> results = poller.poll(); + PollingDispatcher dispatcher = new PollingDispatcher(channel, null); + Collection> results = dispatcher.poll(); assertTrue(results.isEmpty()); channel.send(new StringMessage("test1"), 0); channel.send(new StringMessage("test2"), 0); channel.send(new StringMessage("test3"), 0); - results = poller.poll(); + results = dispatcher.poll(); assertEquals(2, results.size()); Iterator> iter = results.iterator(); assertEquals("test1", iter.next().getPayload()); assertEquals("test2", iter.next().getPayload()); - results = poller.poll(); + results = dispatcher.poll(); assertEquals(1, results.size()); assertEquals("test3", results.iterator().next().getPayload()); } @@ -81,17 +81,17 @@ public class SourcePollerTests { dispatcherPolicy.setReceiveTimeout(0); dispatcherPolicy.setMaxMessagesPerTask(1); MessageChannel channel = new QueueChannel(5, dispatcherPolicy); - SourcePoller poller = new SourcePoller(channel); - Collection> results = poller.poll(); + PollingDispatcher dispatcher = new PollingDispatcher(channel, null); + Collection> results = dispatcher.poll(); assertTrue(results.isEmpty()); channel.send(new StringMessage("test1"), 0); channel.send(new StringMessage("test2"), 0); channel.send(new StringMessage("test3"), 0); - results = poller.poll(); + results = dispatcher.poll(); assertEquals(1, results.size()); assertEquals("test1", results.iterator().next().getPayload()); - poller.setMaxMessagesPerTask(5); - results = poller.poll(); + dispatcherPolicy.setMaxMessagesPerTask(5); + results = dispatcher.poll(); assertEquals(2, results.size()); Iterator> iter = results.iterator(); assertEquals("test2", iter.next().getPayload());