Introduce a MessageGroup.condition (#3517)

* Introduce a `groupConditionSupplier` for MGS

* Add a `MessageGroup.condition` option
* Add a `MessageGroupStore.conditionSupplier` option
* Use it from the `SimpleMessageStore.addMessagesToGroup()` API
to populate `condition` (if any) into a `MessageGroup`
* Introduce a `GroupConditionProvider` contract to be implemented
on those `ReleaseStrategy` contracts which could be aware of group condition
* Populated a `GroupConditionProvider.getGroupConditionSupplier()`
into a `MessageGroupStore` from the `AbstractCorrelatingMessageHandler`
for end-user convenience
* Rework a `FileMarkerReleaseStrategy` to implement a `GroupConditionProvider`
to provide a function which produces a condition from `file_lineCount` header
of the `END` marker message
* Make the `FileMarkerReleaseStrategy` logic already based on the condition from a group
* Delegate `GroupConditionProvider` from the `FileAggregator`
* Add test for empty file aggregation

* * Implement `condition` in the `AbstractKeyValueMessageStore` and `MongoDbMessageStore`
* Test `condition` for `mongo-aggregator-config.xml` and `FileAggregatorTests` against GemFire

* * Implement `condition` in the `ConfigurableMongoDbMessageStore`

* * Implement `condition` in the `JdbcMessageStore`
* `FileAggregatorTests` against `JdbcMessageStore`
* Refactor `JdbcMessageStore` for better handling of message group metadata
* Remove unused `MARKED` column in the DDL in favor of newly introduced `CONDITION`

* * Add docs for message group condition

* * Move `conditionSupplier` option from MGS to AbstractCorrelatingMH
* Make it as a `BiFunction` to propagate existing condition alongside with the
message to consult
* Expose `groupConditionSupplier` in Java & XML DSLs

* * Fix language in docs
This commit is contained in:
Artem Bilan
2021-03-23 14:23:46 -04:00
committed by GitHub
parent 7ee3db9ca5
commit 7e9552974c
39 changed files with 533 additions and 151 deletions

View File

@@ -16,10 +16,13 @@
package org.springframework.integration.file.aggregator;
import java.util.function.BiFunction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.integration.aggregator.CorrelationStrategy;
import org.springframework.integration.aggregator.GroupConditionProvider;
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
import org.springframework.integration.aggregator.MessageGroupProcessor;
import org.springframework.integration.aggregator.ReleaseStrategy;
@@ -35,7 +38,7 @@ import org.springframework.messaging.Message;
* Delegates to {@link HeaderAttributeCorrelationStrategy} with {@link FileHeaders#FILENAME} attribute,
* {@link FileMarkerReleaseStrategy} and {@link FileAggregatingMessageGroupProcessor}, respectively.
* <p>
* The default {@link FileSplitter} behavior with markers enabled is do not provide a sequence details
* The default {@link FileSplitter} behavior with markers enabled is about do not provide a sequence details
* headers, therefore correlation in this aggregator implementation is done by the {@link FileHeaders#FILENAME}
* header which is still populated by the {@link FileSplitter} for each line emitted, including
* {@link FileSplitter.FileMarker} messages.
@@ -47,7 +50,8 @@ import org.springframework.messaging.Message;
*
* @since 5.5
*/
public class FileAggregator implements CorrelationStrategy, ReleaseStrategy, MessageGroupProcessor, BeanFactoryAware {
public class FileAggregator implements CorrelationStrategy, ReleaseStrategy, GroupConditionProvider,
MessageGroupProcessor, BeanFactoryAware {
private final CorrelationStrategy correlationStrategy = new HeaderAttributeCorrelationStrategy(FileHeaders.FILENAME);
@@ -70,6 +74,11 @@ public class FileAggregator implements CorrelationStrategy, ReleaseStrategy, Mes
return this.releaseStrategy.canRelease(group);
}
@Override
public BiFunction<Message<?>, String, String> getGroupConditionSupplier() {
return this.releaseStrategy.getGroupConditionSupplier();
}
@Override
public Object processMessageGroup(MessageGroup group) {
return this.groupProcessor.processMessageGroup(group);

View File

@@ -16,8 +16,10 @@
package org.springframework.integration.file.aggregator;
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.springframework.integration.aggregator.GroupConditionProvider;
import org.springframework.integration.aggregator.ReleaseStrategy;
import org.springframework.integration.file.FileHeaders;
import org.springframework.integration.file.splitter.FileSplitter;
@@ -29,35 +31,46 @@ import org.springframework.messaging.MessageHeaders;
* A {@link ReleaseStrategy} which makes a decision based on the presence of
* {@link org.springframework.integration.file.splitter.FileSplitter.FileMarker.Mark#END}
* message in the group and its {@link org.springframework.integration.file.FileHeaders#LINE_COUNT} header.
* <p>
* The logic of this strategy is based on the {@link FileMarkerReleaseStrategy#GROUP_CONDITION}
* function populated to the {@link org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler}.
*
* @author Artem Bilan
*
* @since 5.5
*/
public class FileMarkerReleaseStrategy implements ReleaseStrategy {
public class FileMarkerReleaseStrategy implements ReleaseStrategy, GroupConditionProvider {
/**
* The {@link Function} for
* {@link org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler#setGroupConditionSupplier(BiFunction)}.
*/
public static final BiFunction<Message<?>, String, String> GROUP_CONDITION =
(message, existingCondition) -> {
MessageHeaders headers = message.getHeaders();
if (FileSplitter.FileMarker.Mark.END.name().equals(headers.get(FileHeaders.MARKER))) {
Long lineCount = headers.get(FileHeaders.LINE_COUNT, Long.class);
return lineCount != null ? "" + lineCount : existingCondition;
}
return existingCondition;
};
@Override
public boolean canRelease(MessageGroup group) {
int size = group.size();
if (size > 1) { // Need more than only a START marker
Collection<Message<?>> messages = group.getMessages();
for (Message<?> message : messages) {
if (checkForEndMarker(size, message.getHeaders())) {
return true;
}
String condition = group.getCondition();
if (condition != null) {
long lineCount = Long.parseLong(condition);
return lineCount == size - 2; // line count doesn't include START/END markers.
}
}
return false;
}
private boolean checkForEndMarker(int groupSize, MessageHeaders headers) {
if (FileSplitter.FileMarker.Mark.END.name().equals(headers.get(FileHeaders.MARKER))) {
Long lineCount = headers.get(FileHeaders.LINE_COUNT, Long.class);
return lineCount != null && lineCount == groupSize - 2;
}
else {
return false;
}
@Override
public BiFunction<Message<?>, String, String> getGroupConditionSupplier() {
return GROUP_CONDITION;
}
}