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:
@@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
@@ -35,11 +36,16 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.file.FileHeaders;
|
||||
import org.springframework.integration.file.dsl.Files;
|
||||
import org.springframework.integration.file.splitter.FileSplitter;
|
||||
import org.springframework.integration.jdbc.store.JdbcMessageStore;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
@@ -57,6 +63,8 @@ import org.springframework.util.FileCopyUtils;
|
||||
@DirtiesContext
|
||||
public class FileAggregatorTests {
|
||||
|
||||
static EmbeddedDatabase dataSource;
|
||||
|
||||
@TempDir
|
||||
static File tmpDir;
|
||||
|
||||
@@ -66,6 +74,10 @@ public class FileAggregatorTests {
|
||||
@Qualifier("fileSplitterAggregatorFlow.input")
|
||||
MessageChannel fileSplitterAggregatorFlow;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("jdbcMessageStoreAggregatorFlow.input")
|
||||
MessageChannel jdbcMessageStoreAggregatorFlow;
|
||||
|
||||
@Autowired
|
||||
PollableChannel resultChannel;
|
||||
|
||||
@@ -84,6 +96,17 @@ public class FileAggregatorTests {
|
||||
"second line\n" +
|
||||
"last line";
|
||||
FileCopyUtils.copy(content.getBytes(StandardCharsets.UTF_8), new FileOutputStream(file, false));
|
||||
|
||||
dataSource = new EmbeddedDatabaseBuilder()
|
||||
.setType(EmbeddedDatabaseType.H2)
|
||||
.addScript("classpath:/org/springframework/integration/jdbc/schema-drop-h2.sql")
|
||||
.addScript("classpath:/org/springframework/integration/jdbc/schema-h2.sql")
|
||||
.build();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void destroy() {
|
||||
dataSource.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,6 +127,25 @@ public class FileAggregatorTests {
|
||||
.contains("SECOND LINE", "LAST LINE", "FIRST LINE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEmptyFileAggregator() throws IOException {
|
||||
File file = new File(tmpDir, "empty.txt");
|
||||
file.createNewFile();
|
||||
this.jdbcMessageStoreAggregatorFlow.send(new GenericMessage<>(file));
|
||||
|
||||
Message<?> receive = this.resultChannel.receive(10_000);
|
||||
assertThat(receive).isNotNull();
|
||||
assertThat(receive.getHeaders())
|
||||
.containsEntry(FileHeaders.FILENAME, "empty.txt")
|
||||
.containsEntry(FileHeaders.LINE_COUNT, 0L)
|
||||
.doesNotContainKey(IntegrationMessageHeaderAccessor.CORRELATION_ID);
|
||||
|
||||
assertThat(receive.getPayload())
|
||||
.isInstanceOf(List.class)
|
||||
.asList()
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFileAggregatorXmlConfig() {
|
||||
this.input.send(new GenericMessage<>(file));
|
||||
@@ -138,7 +180,22 @@ public class FileAggregatorTests {
|
||||
.<String, String>transform(String::toUpperCase)
|
||||
.channel("aggregatorChannel")
|
||||
.aggregate(new FileAggregator())
|
||||
.channel(c -> c.queue("resultChannel"));
|
||||
.channel(resultChannel());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow jdbcMessageStoreAggregatorFlow() {
|
||||
return f -> f
|
||||
.split(Files.splitter().markers())
|
||||
.aggregate(aggregator ->
|
||||
aggregator.processor(new FileAggregator())
|
||||
.messageStore(new JdbcMessageStore(dataSource)))
|
||||
.channel(resultChannel());
|
||||
}
|
||||
|
||||
@Bean
|
||||
PollableChannel resultChannel() {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,13 +3,23 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-file="http://www.springframework.org/schema/integration/file"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:gfe="http://www.springframework.org/schema/geode"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
|
||||
http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
|
||||
http://www.springframework.org/schema/geode https://www.springframework.org/schema/geode/spring-geode.xsd">
|
||||
|
||||
<gfe:cache />
|
||||
|
||||
<gfe:local-region id="region1"/>
|
||||
|
||||
<bean id="gemfireMessageStore" class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="region1"/>
|
||||
</bean>
|
||||
|
||||
<int:chain input-channel="input" output-channel="output">
|
||||
<int-file:splitter markers="true"/>
|
||||
<int:aggregator>
|
||||
<int:aggregator message-store="gemfireMessageStore">
|
||||
<bean class="org.springframework.integration.file.aggregator.FileAggregator"/>
|
||||
</int:aggregator>
|
||||
</int:chain>
|
||||
|
||||
Reference in New Issue
Block a user