INT-3420 Aggregator expire-groups-upon-timeout
JIRA: https://jira.spring.io/browse/INT-3420 Add option to allow the empty group to remain after timeout so late arriving messages can be discarded. INT-3420 Add Callout Hyperlinks The aggregator configuration documentation had hyperlinks from the attribute descriptions to the attribute in the XML, but not vice-versa. For a large number of attributes such as this, bi-directional hyperlinks are useful. INT-3420 Doc Polishing
This commit is contained in:
committed by
Artem Bilan
parent
e21d32f4fd
commit
40f1122df2
@@ -128,6 +128,8 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageH
|
||||
|
||||
private volatile ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
private volatile boolean expireGroupsUponTimeout = true;
|
||||
|
||||
public AbstractCorrelatingMessageHandler(MessageGroupProcessor processor, MessageGroupStore store,
|
||||
CorrelationStrategy correlationStrategy, ReleaseStrategy releaseStrategy) {
|
||||
Assert.notNull(processor);
|
||||
@@ -300,6 +302,18 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageH
|
||||
this.releasePartialSequences = releasePartialSequences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire (completely remove) a group if it is completed due to timeout.
|
||||
* Subclasses setting this to false MUST handle null in the messages
|
||||
* argument to {@link #afterRelease(MessageGroup, Collection)}.
|
||||
* Default true.
|
||||
* @param expireGroupsUponTimeout the expireGroupsOnTimeout to set
|
||||
* @since 4.1
|
||||
*/
|
||||
protected void setExpireGroupsUponTimeout(boolean expireGroupsUponTimeout) {
|
||||
this.expireGroupsUponTimeout = expireGroupsUponTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return "aggregator";
|
||||
@@ -462,7 +476,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageH
|
||||
*/
|
||||
protected abstract void afterRelease(MessageGroup group, Collection<Message<?>> completedMessages);
|
||||
|
||||
private void forceComplete(MessageGroup group) {
|
||||
protected void forceComplete(MessageGroup group) {
|
||||
|
||||
Object correlationKey = group.getGroupId();
|
||||
// UUIDConverter is no-op if already converted
|
||||
@@ -505,10 +519,14 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageH
|
||||
&& group.getTimestamp() == groupNow.getTimestamp()) {
|
||||
if (groupSize > 0) {
|
||||
if (releaseStrategy.canRelease(groupNow)) {
|
||||
this.completeGroup(correlationKey, groupNow);
|
||||
completeGroup(correlationKey, groupNow);
|
||||
}
|
||||
else {
|
||||
this.expireGroup(correlationKey, groupNow);
|
||||
expireGroup(correlationKey, groupNow);
|
||||
}
|
||||
if (!this.expireGroupsUponTimeout) {
|
||||
afterRelease(groupNow, null);
|
||||
removeGroup = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -56,6 +56,11 @@ public class AggregatingMessageHandler extends AbstractCorrelatingMessageHandler
|
||||
this.expireGroupsUponCompletion = expireGroupsUponCompletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpireGroupsUponTimeout(boolean expireGroupsOnTimeout) {
|
||||
super.setExpireGroupsUponTimeout(expireGroupsOnTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterRelease(MessageGroup messageGroup, Collection<Message<?>> completedMessages) {
|
||||
this.messageStore.completeGroup(messageGroup.getGroupId());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.integration.config.xml;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
@@ -26,7 +28,6 @@ import org.springframework.integration.aggregator.DefaultAggregatingMessageGroup
|
||||
import org.springframework.integration.aggregator.ExpressionEvaluatingMessageGroupProcessor;
|
||||
import org.springframework.integration.aggregator.MethodInvokingMessageGroupProcessor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Parser for the <em>aggregator</em> element of the integration namespace. Registers the annotation-driven
|
||||
@@ -37,11 +38,14 @@ import org.w3c.dom.Element;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Dave Syer
|
||||
* @author Stefan Ferstl
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class AggregatorParser extends AbstractCorrelatingMessageHandlerParser {
|
||||
|
||||
private static final String EXPIRE_GROUPS_UPON_COMPLETION = "expire-groups-upon-completion";
|
||||
|
||||
private static final String EXPIRE_GROUPS_UPON_TIMEOUT = "expire-groups-upon-timeout";
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
|
||||
BeanComponentDefinition innerHandlerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element,
|
||||
@@ -86,6 +90,7 @@ public class AggregatorParser extends AbstractCorrelatingMessageHandlerParser {
|
||||
this.doParse(builder, element, processor, parserContext);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, EXPIRE_GROUPS_UPON_COMPLETION);
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, EXPIRE_GROUPS_UPON_TIMEOUT);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -3376,6 +3376,16 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="expire-groups-upon-timeout" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Boolean flag specifying, if a group is completed due to timeout (reaper or
|
||||
'group-timeout(-expression)'), whether the group should be removed.
|
||||
When true, late arriving messages will form a new group. When false, they
|
||||
will be discarded. Default is 'true'
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -13,9 +13,13 @@
|
||||
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -237,6 +241,38 @@ public class AggregatorTests {
|
||||
assertEquals("ABC", this.expiryEvents.get(0).getGroupId());
|
||||
assertEquals(2, this.expiryEvents.get(0).getMessageCount());
|
||||
assertEquals(false, this.expiryEvents.get(0).isDiscarded());
|
||||
Message<?> message3 = createMessage(5, "ABC", 3, 3, replyChannel, null);
|
||||
this.aggregator.handleMessage(message3);
|
||||
assertEquals(1, this.store.getMessageGroup("ABC").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupRemainsAfterTimeout() throws InterruptedException {
|
||||
this.aggregator.setSendPartialResultOnExpiry(true);
|
||||
this.aggregator.setExpireGroupsUponTimeout(false);
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
QueueChannel discardChannel = new QueueChannel();
|
||||
this.aggregator.setDiscardChannel(discardChannel);
|
||||
Message<?> message1 = createMessage(3, "ABC", 3, 1, replyChannel, null);
|
||||
Message<?> message2 = createMessage(5, "ABC", 3, 2, replyChannel, null);
|
||||
this.aggregator.handleMessage(message1);
|
||||
this.aggregator.handleMessage(message2);
|
||||
this.store.expireMessageGroups(-10000);
|
||||
Message<?> reply = replyChannel.receive(1000);
|
||||
assertNotNull("A reply message should have been received", reply);
|
||||
assertEquals(15, reply.getPayload());
|
||||
assertEquals(1, expiryEvents.size());
|
||||
assertSame(this.aggregator, expiryEvents.get(0).getSource());
|
||||
assertEquals("ABC", this.expiryEvents.get(0).getGroupId());
|
||||
assertEquals(2, this.expiryEvents.get(0).getMessageCount());
|
||||
assertEquals(false, this.expiryEvents.get(0).isDiscarded());
|
||||
assertEquals(0, this.store.getMessageGroup("ABC").size());
|
||||
Message<?> message3 = createMessage(5, "ABC", 3, 3, replyChannel, null);
|
||||
this.aggregator.handleMessage(message3);
|
||||
assertEquals(0, this.store.getMessageGroup("ABC").size());
|
||||
Message<?> discardedMessage = discardChannel.receive(1000);
|
||||
assertNotNull("A message should have been discarded", discardedMessage);
|
||||
assertSame(message3, discardedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.config;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
@@ -121,6 +122,7 @@ public class AggregatorParserTests {
|
||||
Object handler = context.getBean("aggregatorWithExpressions.handler");
|
||||
assertSame(mbf, TestUtils.getPropertyValue(handler, "outputProcessor.messageBuilderFactory"));
|
||||
assertSame(mbf, TestUtils.getPropertyValue(handler, "outputProcessor.processor.messageBuilderFactory"));
|
||||
assertTrue(TestUtils.getPropertyValue(handler, "expireGroupsUponTimeout", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -145,15 +147,16 @@ public class AggregatorParserTests {
|
||||
releaseStrategy, accessor.getPropertyValue("releaseStrategy"));
|
||||
assertEquals("The AggregatorEndpoint is not injected with the appropriate CorrelationStrategy instance",
|
||||
correlationStrategy, accessor.getPropertyValue("correlationStrategy"));
|
||||
Assert.assertEquals("The AggregatorEndpoint is not injected with the appropriate output channel",
|
||||
assertEquals("The AggregatorEndpoint is not injected with the appropriate output channel",
|
||||
outputChannel, accessor.getPropertyValue("outputChannel"));
|
||||
Assert.assertEquals("The AggregatorEndpoint is not injected with the appropriate discard channel",
|
||||
assertEquals("The AggregatorEndpoint is not injected with the appropriate discard channel",
|
||||
discardChannel, accessor.getPropertyValue("discardChannel"));
|
||||
Assert.assertEquals("The AggregatorEndpoint is not set with the appropriate timeout value", 86420000l,
|
||||
assertEquals("The AggregatorEndpoint is not set with the appropriate timeout value", 86420000l,
|
||||
TestUtils.getPropertyValue(consumer, "messagingTemplate.sendTimeout"));
|
||||
Assert.assertEquals(
|
||||
assertEquals(
|
||||
"The AggregatorEndpoint is not configured with the appropriate 'send partial results on timeout' flag",
|
||||
true, accessor.getPropertyValue("sendPartialResultOnExpiry"));
|
||||
assertFalse(TestUtils.getPropertyValue(consumer, "expireGroupsUponTimeout", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
release-strategy="releaseStrategy"
|
||||
correlation-strategy="correlationStrategy"
|
||||
send-timeout="86420000"
|
||||
send-partial-result-on-expiry="true"/>
|
||||
send-partial-result-on-expiry="true"
|
||||
expire-groups-upon-timeout="false"/>
|
||||
|
||||
<channel id="aggregatorWithExpressionsInput"/>
|
||||
<channel id="aggregatorWithExpressionsOutput"/>
|
||||
|
||||
@@ -346,35 +346,37 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
|
||||
<programlisting language="xml"><![CDATA[<channel id="inputChannel"/>
|
||||
|
||||
<int:aggregator id="myAggregator" ]]><co id="aggxml01" /><![CDATA[
|
||||
auto-startup="true" ]]><co id="aggxml02" /><![CDATA[
|
||||
input-channel="inputChannel" ]]><co id="aggxml03" /><![CDATA[
|
||||
output-channel="outputChannel" ]]><co id="aggxml04" /><![CDATA[
|
||||
discard-channel="throwAwayChannel" ]]><co id="aggxml05" /><![CDATA[
|
||||
message-store="persistentMessageStore" ]]><co id="aggxml06" /><![CDATA[
|
||||
order="1" ]]><co id="aggxml07" /><![CDATA[
|
||||
send-partial-result-on-expiry="false" ]]><co id="aggxml08" /><![CDATA[
|
||||
send-timeout="1000" ]]><co id="aggxml09" /><![CDATA[
|
||||
<int:aggregator id=""myAggregator" ]]><co id="aggxml01" linkends="aggxml01-txt" /><![CDATA[
|
||||
auto-startup="true" ]]><co id="aggxml02" linkends="aggxml02-txt" /><![CDATA[
|
||||
input-channel="inputChannel" ]]><co id="aggxml03" linkends="aggxml03-txt" /><![CDATA[
|
||||
output-channel="outputChannel" ]]><co id="aggxml04" linkends="aggxml04-txt" /><![CDATA[
|
||||
discard-channel="throwAwayChannel" ]]><co id="aggxml05" linkends="aggxml05-txt" /><![CDATA[
|
||||
message-store="persistentMessageStore" ]]><co id="aggxml06" linkends="aggxml06-txt" /><![CDATA[
|
||||
order="1" ]]><co id="aggxml07" linkends="aggxml07-txt" /><![CDATA[
|
||||
send-partial-result-on-expiry="false" ]]><co id="aggxml08" linkends="aggxml08-txt" /><![CDATA[
|
||||
send-timeout="1000" ]]><co id="aggxml09" linkends="aggxml09-txt" /><![CDATA[
|
||||
|
||||
correlation-strategy="correlationStrategyBean" ]]><co id="aggxml10" /><![CDATA[
|
||||
correlation-strategy-method="correlate" ]]><co id="aggxml11" /><![CDATA[
|
||||
correlation-strategy-expression="headers['foo']" ]]><co id="aggxml12" /><![CDATA[
|
||||
correlation-strategy="correlationStrategyBean" ]]><co id="aggxml10" linkends="aggxml10-txt" /><![CDATA[
|
||||
correlation-strategy-method="correlate" ]]><co id="aggxml11" linkends="aggxml11-txt" /><![CDATA[
|
||||
correlation-strategy-expression="headers['foo']" ]]><co id="aggxml12" linkends="aggxml12-txt" /><![CDATA[
|
||||
|
||||
ref="aggregatorBean" ]]><co id="aggxml13" /><![CDATA[
|
||||
method="aggregate" ]]><co id="aggxml14" /><![CDATA[
|
||||
ref="aggregatorBean" ]]><co id="aggxml13" linkends="aggxml13-txt" /><![CDATA[
|
||||
method="aggregate" ]]><co id="aggxml14" linkends="aggxml14-txt" /><![CDATA[
|
||||
|
||||
release-strategy="releaseStrategyBean" ]]><co id="aggxml15" /><![CDATA[
|
||||
release-strategy-method="release" ]]><co id="aggxml16" /><![CDATA[
|
||||
release-strategy-expression="size() == 5" ]]><co id="aggxml17" /><![CDATA[
|
||||
release-strategy="releaseStrategyBean" ]]><co id="aggxml15" linkends="aggxml15-txt" /><![CDATA[
|
||||
release-strategy-method="release" ]]><co id="aggxml16" linkends="aggxml16-txt" /><![CDATA[
|
||||
release-strategy-expression="size() == 5" ]]><co id="aggxml17" linkends="aggxml17-txt" /><![CDATA[
|
||||
|
||||
expire-groups-upon-completion="false" ]]><co id="aggxml18" /><![CDATA[
|
||||
empty-group-min-timeout="60000" ]]><co id="aggxml19" /><![CDATA[
|
||||
expire-groups-upon-completion="false" ]]><co id="aggxml18" linkends="aggxml18-txt" /><![CDATA[
|
||||
empty-group-min-timeout="60000" ]]><co id="aggxml19" linkends="aggxml19-txt" /><![CDATA[
|
||||
|
||||
lock-registry="lockRegistry" ]]><co id="aggxml191" /><![CDATA[
|
||||
lock-registry="lockRegistry" ]]><co id="aggxml191" linkends="aggxml191-txt" /><![CDATA[
|
||||
|
||||
group-timeout="60000" ]]><co id="aggxml20" /><![CDATA[
|
||||
group-timeout-expression="size() ge 2 ? 100 : -1" ]]><co id="aggxml21" /><![CDATA[
|
||||
scheduler="taskScheduler" /> ]]><co id="aggxml22" /><![CDATA[
|
||||
group-timeout="60000" ]]><co id="aggxml20" linkends="aggxml20-txt" /><![CDATA[
|
||||
group-timeout-expression="size() ge 2 ? 100 : -1" ]]><co id="aggxml21" linkends="aggxml21-txt" /><![CDATA[
|
||||
expire-groups-on-timeout="true" ]]><co id="aggxml22" linkends="aggxml22-txt" /><![CDATA[
|
||||
|
||||
scheduler="taskScheduler" /> ]]><co id="aggxml23" linkends="aggxml23-txt" /><![CDATA[
|
||||
|
||||
<int:channel id="outputChannel"/>
|
||||
|
||||
@@ -391,47 +393,47 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
<bean id="correlationStrategyBean" class="sample.PojoCorrelationStrategy"/>]]></programlisting>
|
||||
|
||||
<calloutlist>
|
||||
<callout arearefs="aggxml01">
|
||||
<callout arearefs="aggxml01" id="aggxml01-txt">
|
||||
<para>The id of the aggregator is
|
||||
<emphasis>0ptional</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml02">
|
||||
<callout arearefs="aggxml02" id="aggxml02-txt">
|
||||
<para>Lifecycle attribute signaling if aggregator should be started during Application Context startup.
|
||||
<emphasis>Optional (default is 'true')</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml03">
|
||||
<callout arearefs="aggxml03" id="aggxml03-txt">
|
||||
<para>The channel from which where aggregator will receive messages.
|
||||
<emphasis>Required</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml04">
|
||||
<callout arearefs="aggxml04" id="aggxml04-txt">
|
||||
<para>The channel to which the aggregator will send the aggregation
|
||||
results. <emphasis>Optional (because incoming messages can specify a
|
||||
reply channel themselves via 'replyChannel' Message Header)</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml05">
|
||||
<callout arearefs="aggxml05" id="aggxml05-txt">
|
||||
<para>The channel to which the aggregator will send the messages that
|
||||
timed out (if <code>send-partial-result-on-expiry</code> is
|
||||
<emphasis>false</emphasis>). <emphasis>Optional</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml06">
|
||||
<callout arearefs="aggxml06" id="aggxml06-txt">
|
||||
<para>A reference to a <code>MessageGroupStore</code> used
|
||||
to store groups of messages under their correlation key until they are
|
||||
complete. <emphasis>Optional</emphasis>, by default a volatile
|
||||
in-memory store.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml07">
|
||||
<callout arearefs="aggxml07" id="aggxml07-txt">
|
||||
<para>Order of this aggregator when more than one handle is subscribed to the same DirectChannel
|
||||
(use for load balancing purposes).
|
||||
<emphasis>Optional</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml08">
|
||||
<callout arearefs="aggxml08" id="aggxml08-txt">
|
||||
<para>
|
||||
Indicates that expired messages should be aggregated and sent to the 'output-channel' or 'replyChannel'
|
||||
once their containing <classname>MessageGroup</classname> is expired (see <code>MessageGroupStore.expireMessageGroups(long)</code>).
|
||||
@@ -441,15 +443,20 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
or by simply invoking that method if you have a reference to the <classname>MessageGroupStore</classname> 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 <classname>MessageGroup</classname> that is about to be expired.
|
||||
<emphasis>Optional</emphasis>. <emphasis>Default - 'false'</emphasis>.</para>
|
||||
<emphasis>Optional</emphasis>. <emphasis>Default - 'false'</emphasis>.
|
||||
<note>
|
||||
This attribute is more properly 'send-partial-result-on-timeout' because the group may not actually expire
|
||||
if <code>expire-groups-on-timeout</code> is set to <code>false</code>.
|
||||
</note>
|
||||
</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml09">
|
||||
<callout arearefs="aggxml09" id="aggxml09-txt">
|
||||
<para>The timeout interval for sending the aggregated messages to the output or
|
||||
reply channel. <emphasis>Optional</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml10">
|
||||
<callout arearefs="aggxml10" id="aggxml10-txt">
|
||||
<para>A reference to a bean that implements the message correlation (grouping)
|
||||
algorithm. The bean can be an implementation of the <interfacename>CorrelationStrategy</interfacename>
|
||||
interface or a POJO. In the latter case the correlation-strategy-method attribute must be defined
|
||||
@@ -457,7 +464,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
the <code>IntegrationMessageHeaderAccessor.CORRELATION_ID</code> header) </emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml11">
|
||||
<callout arearefs="aggxml11" id="aggxml11-txt">
|
||||
<para>A method defined on the bean referenced by
|
||||
<code>correlation-strategy</code>, that implements the
|
||||
correlation decision algorithm. <emphasis>Optional, with
|
||||
@@ -465,25 +472,25 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
present).</emphasis></para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml12">
|
||||
<callout arearefs="aggxml12" id="aggxml12-txt">
|
||||
<para>A SpEL expression representing the correlation strategy.
|
||||
Example: <code>"headers['foo']"</code>. Only one of
|
||||
<code>correlation-strategy</code>
|
||||
or <code>correlation-strategy-expression</code> is allowed.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml13">
|
||||
<callout arearefs="aggxml13" id="aggxml13-txt">
|
||||
<para>A reference to a bean defined in the application context. The bean must implement the aggregation logic
|
||||
as described above. <emphasis>Optional (by default the list of aggregated Messages will become a
|
||||
payload of the output message).</emphasis></para>
|
||||
</callout>
|
||||
<callout arearefs="aggxml14">
|
||||
<callout arearefs="aggxml14" id="aggxml14-txt">
|
||||
<para>A method defined on the bean referenced by <code>ref</code>,
|
||||
that implements the message aggregation
|
||||
algorithm. <emphasis>Optional, depends on <code>ref</code> attribute being defined.</emphasis></para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml15">
|
||||
<callout arearefs="aggxml15" id="aggxml15-txt">
|
||||
<para>A reference to a bean that implements the release strategy.
|
||||
The bean can be an implementation of the <interfacename>ReleaseStrategy</interfacename> interface
|
||||
or a POJO. In the latter case the release-strategy-method
|
||||
@@ -491,7 +498,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
aggregator will use the <code>IntegrationMessageHeaderAccessor.SEQUENCE_SIZE</code> header attribute)</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml16">
|
||||
<callout arearefs="aggxml16" id="aggxml16-txt">
|
||||
<para>A method defined on the bean referenced by
|
||||
<code>release-strategy</code>, that implements the
|
||||
completion decision algorithm. <emphasis>Optional, with
|
||||
@@ -499,7 +506,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
present).</emphasis></para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml17">
|
||||
<callout arearefs="aggxml17" id="aggxml17-txt">
|
||||
<para>A SpEL expression representing the release strategy; the root object for the
|
||||
expression is a <code>Collection</code> of <code>Message</code>s.
|
||||
Example: <code>"size() == 5"</code>. Only one of
|
||||
@@ -507,7 +514,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
or <code>release-strategy-expression</code> is allowed.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml18">
|
||||
<callout arearefs="aggxml18" id="aggxml18-txt">
|
||||
<para>When set to true (default false), completed groups are
|
||||
removed from the message store, allowing subsequent messages with
|
||||
the same correlation to form a new group. The default behavior
|
||||
@@ -515,7 +522,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
group to the <emphasis>discard-channel</emphasis>.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml19">
|
||||
<callout arearefs="aggxml19" id="aggxml19-txt">
|
||||
<para>Only applies if a <classname>MessageGroupStoreReaper</classname> is configured
|
||||
for the <code><aggregator></code>'s <classname>MessageStore</classname>.
|
||||
By default, when a <classname>MessageGroupStoreReaper</classname> is configured to expire partial
|
||||
@@ -529,7 +536,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
property and it could be as much as this value plus the timeout.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml191">
|
||||
<callout arearefs="aggxml191" id="aggxml191-txt">
|
||||
<para>
|
||||
A reference to a <interfacename>org.springframework.integration.util.LockRegistry</interfacename> bean;
|
||||
used to obtain a <interfacename>Lock</interfacename> based on the <code>groupId</code> for
|
||||
@@ -541,7 +548,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="aggxml20">
|
||||
<callout arearefs="aggxml20" id="aggxml20-txt">
|
||||
<para>
|
||||
A timeout in milliseconds to force the <code>MessageGroup</code> complete,
|
||||
when the <interfacename>ReleaseStrategy</interfacename> doesn't <emphasis>release</emphasis>
|
||||
@@ -563,7 +570,7 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
Mutually exclusive with 'group-timeout-expression' attribute.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="aggxml21">
|
||||
<callout arearefs="aggxml21" id="aggxml21-txt">
|
||||
<para>
|
||||
The SpEL expression that evaluates to a <code>groupTimeout</code> with the <code>MessageGroup</code>
|
||||
as the <code>#root</code> evaluation context object. Used for scheduling the <code>MessageGroup</code> to
|
||||
@@ -574,7 +581,17 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
Mutually exclusive with 'group-timeout' attribute.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="aggxml22">
|
||||
<callout arearefs="aggxml22" id="aggxml22-txt">
|
||||
<para>
|
||||
When a group is completed due to a timeout (or by a <classname>MessageGroupStoreReaper</classname>), the group
|
||||
is expired (completely removed) by default. Late arriving messages will start a new group. Set this
|
||||
to <code>false</code> to complete the group but have its metadata remain so that late
|
||||
arriving messages will be discarded. Empty groups can be expired later using a
|
||||
<classname>MessageGroupStoreReaper</classname> together with the <code>empty-group-min-timeout</code>
|
||||
attribute. Default: 'true'.
|
||||
</para>
|
||||
</callout>
|
||||
<callout arearefs="aggxml23" id="aggxml23-txt">
|
||||
<para>
|
||||
A <interfacename>TaskScheduler</interfacename> bean reference to schedule
|
||||
the <code>MessageGroup</code> to be forced complete
|
||||
@@ -589,6 +606,37 @@ then you should simply provide an implementation of the <classname>ReleaseStrate
|
||||
|
||||
</calloutlist>
|
||||
|
||||
<important>
|
||||
<title>Expiring Groups</title>
|
||||
<para>
|
||||
There are two attributes related to expiring (completely removing) groups. When a group
|
||||
is expired, there is no record of it and if a new message arrives with the same correlation,
|
||||
a new group is started. When a group is completed (without expiry), the empty group remains
|
||||
and late arriving messages are discarded. Empty groups can be removed later using a
|
||||
<classname>MessageGroupStoreReaper</classname> in combination with the
|
||||
<code>empty-group-min-timeout</code> attribute.
|
||||
</para>
|
||||
<para>
|
||||
<code>expire-groups-upon-completion</code> relates to "normal" completion - when the
|
||||
<interfacename>ReleaseStrategy</interfacename> releases the group. This defaults to
|
||||
<code>false</code>.
|
||||
</para>
|
||||
<para>
|
||||
If a group is not completed normally, but is released or discarded because of a timeout,
|
||||
the group is normally expired. Since <emphasis>version 4.1</emphasis>, you can now control
|
||||
this behavior using <code>expire-groups-upon-timeout</code>; this defaults to
|
||||
<code>true</code> for backwards compatibility.
|
||||
</para>
|
||||
<note>
|
||||
When a group is timed out, the <interfacename>ReleaseStrategy</interfacename> is given
|
||||
one more opportunity to release the group; if it does so, then expiration is controlled
|
||||
by <code>expire-groups-upon-completion</code>. If the group is not released by the release
|
||||
strategy during timeout, then the expiration is controlled by the
|
||||
<code>expire-groups-upon-timeout</code>. Timed-out groups are either discarded, or a
|
||||
partial release occurs (based on <code>send-partial-result-on-expiry</code>).
|
||||
</note>
|
||||
</important>
|
||||
|
||||
<para>Using a <code>ref</code> attribute is generally recommended if a custom
|
||||
aggregator handler implementation may be referenced in other
|
||||
<code><aggregator></code> definitions. However if a custom
|
||||
|
||||
@@ -82,5 +82,12 @@
|
||||
See <xref linkend="splitter"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-aggregator">
|
||||
<title>Aggregator</title>
|
||||
<para>
|
||||
<code>Aggregator</code>s now support a new attribute <code>expire-groups-on-timeout</code>.
|
||||
See <xref linkend="aggregator-config"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user