From 51b161ae4d24fee70b503e6f2ba5dc97a7339e65 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 12 Aug 2015 19:32:51 -0400 Subject: [PATCH] INT-3534:`MethodInvokingMLP` Don't Copy Collection JIRA: https://jira.spring.io/browse/INT-3534 Previously the `MethodInvokingMessageListProcessor` copied `Collection>` in its `process()` method for the `delegate` to a new `Collection` unconditionally. To improve performance, the collection is no longer copied unconditionally in the `ExpressionEvaluatingMessageGroupProcessor`. Also add an `important` note to the `aggregator.adoc` about the restriction with `unmodifiableCollection`. --- .../MethodInvokingMessageListProcessor.java | 7 +++---- src/reference/asciidoc/aggregator.adoc | 20 ++++++++++++++++--- src/reference/asciidoc/whats-new.adoc | 6 ++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MethodInvokingMessageListProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MethodInvokingMessageListProcessor.java index 2d24a589fa..4e6e2782b8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MethodInvokingMessageListProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/aggregator/MethodInvokingMessageListProcessor.java @@ -18,14 +18,13 @@ package org.springframework.integration.aggregator; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import org.springframework.beans.factory.BeanFactory; -import org.springframework.messaging.Message; import org.springframework.integration.util.AbstractExpressionEvaluator; import org.springframework.integration.util.MessagingMethodInvokerHelper; +import org.springframework.messaging.Message; /** * A MessageListProcessor implementation that invokes a method on a target POJO. @@ -69,9 +68,9 @@ public class MethodInvokingMessageListProcessor extends AbstractExpressionEva return delegate.toString(); } - public T process(Collection> messages, Map aggregateHeaders) { + public T process(Collection> messages, Map aggregateHeaders) { try { - return delegate.process(new ArrayList>(messages), aggregateHeaders); + return delegate.process(messages, aggregateHeaders); } catch (RuntimeException e) { throw e; diff --git a/src/reference/asciidoc/aggregator.adoc b/src/reference/asciidoc/aggregator.adoc index 30c2c00fc4..16156bc267 100644 --- a/src/reference/asciidoc/aggregator.adoc +++ b/src/reference/asciidoc/aggregator.adoc @@ -104,9 +104,11 @@ However, there are better solutions, less coupled to the API, for implementing t In general, any POJO can implement the aggregation algorithm if it provides a method that accepts a single `java.util.List` as an argument (parameterized lists are supported as well). This method will be invoked for aggregating messages as follows: -* if the argument is a `java.util.List`, and the parameter type T is assignable to `Message`, then the whole list of messages accumulated for aggregation will be sent to the aggregator +* if the argument is a `java.util.Collection`, and the parameter type T is assignable to `Message`, +then the whole list of messages accumulated for aggregation will be sent to the aggregator -* if the argument is a non-parameterized `java.util.List` or the parameter type is not assignable to `Message`, then the method will receive the payloads of the accumulated messages +* if the argument is a non-parameterized `java.util.Collection` or the parameter type is not assignable to `Message`, +then the method will receive the payloads of the accumulated messages * if the return type is not assignable to `Message`, then it will be treated as the payload for a Message that will be created automatically by the framework. @@ -114,8 +116,20 @@ This method will be invoked for aggregating messages as follows: NOTE: In the interest of code simplicity, and promoting best practices such as low coupling, testability, etc., the preferred way of implementing the aggregation logic is through a POJO, and using the XML or annotation support for configuring it in the application. +[[agg-message-collection]] +IMPORTANT: The `SimpleMessageGroup.getMessages()` method returns an `unmodifiableCollection`, therefore, +if your aggregating POJO method has a `Collection` parameter, the argument passed in will be exactly that +`Collection` instance and, when a `SimpleMessageStore` is used for the Aggregator, +that original `Collection` will be cleared after releasing the group. +Hence the `Collection` variable in the POJO will be cleared too, if passed out of the aggregator. +If you wish to simply release that collection as-is for further processing, +it is required that you build a new `Collection` (e.g. `new ArrayList(messages)`) +Starting with _version 4.3, the Framework no longer copies the messages to a new collection, to avoid undesired extra +object creation. + + If the `MessageGroupProcessor` 's `processMessageGroup` method returns a collection, it must be a collection of -`Messge` s. +`Message` s. In this case, the messages are released individually. Prior to _version 4.2_, it was not possible to provide a `MessageGroupProcessor` using XML configuration, only POJO methods could be used for aggregation. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index da43e0b3f0..acd600abd5 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -32,3 +32,9 @@ See <> for more information. The `DefaultJmsHeaderMapper` now maps the standard `correlationId` header as a message property by invoking its `toString()` method. See <> for more information. + +==== Aggregator Changes + +There is a change in behavior when a POJO aggregator releases a collection of `Message` objects; this is rare but if +your application does that, you will need to make a small change to your POJO. See this <> note +for more information.