diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/aggregator/CorrelatingMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/aggregator/CorrelatingMessageHandler.java index c4e7c432dc..7acab17a59 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/aggregator/CorrelatingMessageHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/aggregator/CorrelatingMessageHandler.java @@ -81,7 +81,7 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements Assert.notNull(store); Assert.notNull(processor); this.messageStore = store; - store.registerExpiryCallback(new MessageGroupCallback() { + store.registerMessageGroupExpiryCallback(new MessageGroupCallback() { public void execute(MessageGroup group) { forceComplete(group); } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java b/org.springframework.integration/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java index e53ff3d6c9..531a9ad073 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java @@ -41,17 +41,17 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It /** * Convenient injection point for expiry callbacks in the message store. Each of the callbacks provided will simply - * be registered with the store using {@link #registerExpiryCallback(MessageGroupCallback)}. + * be registered with the store using {@link #registerMessageGroupExpiryCallback(MessageGroupCallback)}. * * @param expiryCallbacks the expiry callbacks to add */ public void setExpiryCallbacks(Collection expiryCallbacks) { for (MessageGroupCallback callback : expiryCallbacks) { - registerExpiryCallback(callback); + registerMessageGroupExpiryCallback(callback); } } - public void registerExpiryCallback(MessageGroupCallback callback) { + public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) { expiryCallbacks.add(callback); } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStore.java b/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStore.java index c026f7f113..00942cd47a 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStore.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStore.java @@ -63,7 +63,7 @@ public interface MessageGroupStore { * * @param callback a callback to execute when a message group is cleaned up */ - void registerExpiryCallback(MessageGroupCallback callback); + void registerMessageGroupExpiryCallback(MessageGroupCallback callback); /** * Extract all expired groups (whose timestamp is older than the current time less the threshold provided) and call @@ -74,7 +74,7 @@ public interface MessageGroupStore { * @param timeout the timeout threshold to use * @return the number of message groups expired * - * @see #registerExpiryCallback(MessageGroupCallback) + * @see #registerMessageGroupExpiryCallback(MessageGroupCallback) */ int expireMessageGroups(long timeout); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java b/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java new file mode 100644 index 0000000000..176223ef15 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/store/MessageGroupStoreReaper.java @@ -0,0 +1,97 @@ +/* + * 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.store; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.util.Assert; + +/** + * Convenient configurable component to allow explicit timed expiry of {@link MessageGroup} instances in a + * {@link MessageGroupStore}. This component provides a no-args {@link #run()} method that is useful for remote or + * timed execution and a {@link #destroy()} method that can optionally be called on shutdown. + * + * @author Dave Syer + * + */ +public class MessageGroupStoreReaper implements Runnable, DisposableBean, InitializingBean { + + private static Log logger = LogFactory.getLog(MessageGroupStoreReaper.class); + + private MessageGroupStore messageGroupStore; + private boolean expireOnDestroy = false; + private long timeout = -1; + + public MessageGroupStoreReaper(MessageGroupStore messageGroupStore) { + this.messageGroupStore = messageGroupStore; + } + + public MessageGroupStoreReaper() { + } + + /** + * Flag to indicate that the stores should be expired when this component is destroyed (i.e. usuually when its + * enclosing {@link ApplicationContext} is closed). + * + * @param expireOnDestroy the flag value to set + */ + public void setExpireOnDestroy(boolean expireOnDestroy) { + this.expireOnDestroy = expireOnDestroy; + } + + /** + * @param timeout the timeout to set + */ + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + /** + * A message group store to expire according the the other configurations. + * + * @param messageGroupStore the {@link MessageGroupStore} to set + */ + public void setMessageGroupStore(MessageGroupStore messageGroupStore) { + this.messageGroupStore = messageGroupStore; + } + + public void afterPropertiesSet() throws Exception { + Assert.state(messageGroupStore != null, "A MessageGroupStore must be provided"); + } + + public void destroy() throws Exception { + if (expireOnDestroy) { + logger.info("Expiring all messages from message group store: " + messageGroupStore); + messageGroupStore.expireMessageGroups(0); + } + } + + /** + * Expire all message groups older than the {@link #setTimeout(long) timeout} provided. Normally this method would + * be executed by a scheduled task. + */ + public void run() { + if (timeout >= 0) { + if (logger.isDebugEnabled()) { + logger.debug("Expiring all messages older than timeout=" + timeout + " from message group store: " + + messageGroupStore); + } + messageGroupStore.expireMessageGroups(timeout); + } + } + +} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml new file mode 100644 index 0000000000..8f1299d6d0 --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests-context.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java new file mode 100644 index 0000000000..75f88b05aa --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreReaperTests.java @@ -0,0 +1,66 @@ +/* + * 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.store; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.message.StringMessage; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + + +/** + * @author Dave Syer + * + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class MessageStoreReaperTests { + + @Autowired + private MessageGroupStore messageStore; + + @Before + public void init() { + ExpiryCallback.groups.clear(); + } + + @Test + public void testExpiry() throws Exception { + messageStore.addMessageToGroup("FOO", new StringMessage("foo")); + assertEquals(1, messageStore.getMessageGroup("FOO").size()); + // wait for expiry... + Thread.sleep(200L); + assertEquals(0, messageStore.getMessageGroup("FOO").size()); + assertEquals(1, ExpiryCallback.groups.size()); + } + + public static class ExpiryCallback implements MessageGroupCallback { + + private static final List groups = new ArrayList(); + + public void execute(MessageGroup group) { + groups.add(group); + } + + } + +} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreTests.java index 7e9f54394b..44ee5d832d 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/store/MessageStoreTests.java @@ -50,7 +50,7 @@ public class MessageStoreTests { TestMessageStore store = new TestMessageStore(); final List list = new ArrayList(); - store.registerExpiryCallback(new MessageGroupCallback() { + store.registerMessageGroupExpiryCallback(new MessageGroupCallback() { public void execute(MessageGroup group) { list.add(group.getOne().getPayload().toString()); } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/store/SimpleMessageStoreTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/store/SimpleMessageStoreTests.java index 3ac26355c2..db3afe9e42 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/store/SimpleMessageStoreTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/store/SimpleMessageStoreTests.java @@ -105,7 +105,7 @@ public class SimpleMessageStoreTests { SimpleMessageStore store = new SimpleMessageStore(); final List list = new ArrayList(); - store.registerExpiryCallback(new MessageGroupCallback() { + store.registerMessageGroupExpiryCallback(new MessageGroupCallback() { public void execute(MessageGroup group) { list.add(group.getOne().getPayload().toString()); }