diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java index a8f106f846..391652028c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/AbstractMessageGroupStore.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 the original author or authors. - * + * Copyright 2002-2016 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. @@ -18,12 +18,13 @@ import java.util.LinkedHashSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedOperation; /** * @author Dave Syer * @author Oleg Zhurakousky - * + * * @since 2.0 * */ @@ -32,11 +33,11 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It protected final Log logger = LogFactory.getLog(getClass()); private Collection expiryCallbacks = new LinkedHashSet(); - + private volatile boolean timeoutOnIdle; /** - * + * */ public AbstractMessageGroupStore() { super(); @@ -45,7 +46,7 @@ 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 #registerMessageGroupExpiryCallback(MessageGroupCallback)}. - * + * * @param expiryCallbacks the expiry callbacks to add */ public void setExpiryCallbacks(Collection expiryCallbacks) { @@ -53,14 +54,14 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It registerMessageGroupExpiryCallback(callback); } } - + public boolean isTimeoutOnIdle() { return timeoutOnIdle; } /** * Allows you to override the rule for the timeout calculation. Typical timeout is based from the time - * the {@link MessageGroup} was created. If you want the timeout to be based on the time + * the {@link MessageGroup} was created. If you want the timeout to be based on the time * the {@link MessageGroup} was idling (e.g., inactive from the last update) invoke this method with 'true'. * Default is 'false'. */ @@ -69,10 +70,11 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It } public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) { - expiryCallbacks.add(callback); + this.expiryCallbacks.add(callback); } - public int expireMessageGroups(long timeout) { + @ManagedOperation + public synchronized int expireMessageGroups(long timeout) { int count = 0; long threshold = System.currentTimeMillis() - timeout; for (MessageGroup group : this) { @@ -81,7 +83,7 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It if (this.isTimeoutOnIdle() && group.getLastModified() > 0) { timestamp = group.getLastModified(); } - + if (timestamp <= threshold) { count++; expire(group); @@ -109,9 +111,9 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It } private void expire(MessageGroup group) { - + RuntimeException exception = null; - + for (MessageGroupCallback callback : expiryCallbacks) { try { callback.execute(this, group); @@ -122,10 +124,10 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It logger.error("Exception in expiry callback", e); } } - + if (exception != null) { throw exception; } } -} \ No newline at end of file +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java index 41026f3914..34ebc18dae 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/store/MessageGroupStore.java @@ -16,6 +16,7 @@ import java.util.Iterator; import org.springframework.integration.Message; import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedOperation; /** * Interface for storage operations on groups of messages linked by a group id. @@ -104,6 +105,7 @@ public interface MessageGroupStore { * * @see #registerMessageGroupExpiryCallback(MessageGroupCallback) */ + @ManagedOperation int expireMessageGroups(long timeout); /** @@ -134,7 +136,7 @@ public interface MessageGroupStore { /** * Invoked when a MessageGroupStore expires a group. */ - public interface MessageGroupCallback { + interface MessageGroupCallback { void execute(MessageGroupStore messageGroupStore, MessageGroup group); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml index 8a2781a4e0..c80a20a8ad 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests-context.xml @@ -14,10 +14,12 @@ - + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests.java index 2322dee5f4..7e1894ef5f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/AggregatorWithMessageStoreParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2016 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. @@ -26,31 +26,36 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.store.MessageGroupStore; import org.springframework.integration.support.MessageBuilder; +import org.springframework.integration.message.GenericMessage; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Dave Syer + * @author Artem Bilan */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) +@DirtiesContext public class AggregatorWithMessageStoreParserTests { - + @Autowired @Qualifier("input") private MessageChannel input; - + @Autowired private TestAggregatorBean aggregatorBean; - + @Autowired private MessageGroupStore messageGroupStore; + @Autowired + private MessageChannel controlBusChannel; + @Test @DirtiesContext public void testAggregation() { - input.send(createMessage("123", "id1", 3, 1, null)); assertEquals(1, messageGroupStore.getMessageGroup("id1").size()); input.send(createMessage("789", "id1", 3, 3, null)); @@ -67,12 +72,11 @@ public class AggregatorWithMessageStoreParserTests { @Test @DirtiesContext public void testExpiry() { - input.send(createMessage("123", "id1", 3, 1, null)); assertEquals(1, messageGroupStore.getMessageGroup("id1").size()); input.send(createMessage("456", "id1", 3, 2, null)); assertEquals(2, messageGroupStore.getMessageGroup("id1").size()); - messageGroupStore.expireMessageGroups(-10000); + this.controlBusChannel.send(new GenericMessage("@messageStore.expireMessageGroups(-10000)")); assertEquals("One and only one message should have been aggregated", 1, aggregatorBean .getAggregatedMessages().size()); Message aggregatedMessage = aggregatorBean.getAggregatedMessages().get("id1"); diff --git a/src/reference/docbook/aggregator.xml b/src/reference/docbook/aggregator.xml index f555908915..a98810a4bd 100644 --- a/src/reference/docbook/aggregator.xml +++ b/src/reference/docbook/aggregator.xml @@ -416,7 +416,7 @@ then you should simply provide an implementation of the ReleaseStrate once their containing MessageGroup is expired (see MessageGroupStore.expireMessageGroups(long)). One way of expiring MessageGroups is by configuring a MessageGroupStoreReaper. However MessageGroups can alternatively be expired by simply calling - MessageGroupStore.expireMessageGroup(groupId). That could be accomplished via a Control Bus operation + MessageGroupStore.expireMessageGroups(timeout). That could be accomplished via a Control Bus operation or by simply invoking that method if you have a reference to the MessageGroupStore instance. Otherwise by itself this attribute has no behavior. It only serves as an indicator of what to do (discard or send to the output/reply channel) with Messages that are still in the MessageGroup that is about to be expired.