Skip to content

Commit 2756819

Browse files
committed
GH-2936: Add custom converters for MongoDbMS
Fixes #2936 * Add `MongoDbMessageStore.setCustomConverters()` to allow to inject any custom converters for `payload` as well as header values **Cherry-pick to 5.1.x**
1 parent 8ce16b9 commit 2756819

File tree

4 files changed

+105
-17
lines changed

4 files changed

+105
-17
lines changed

spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/store/MongoDbMessageStore.java

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package org.springframework.integration.mongodb.store;
1818

1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Collection;
22+
import java.util.Collections;
2123
import java.util.HashMap;
2224
import java.util.Iterator;
2325
import java.util.List;
@@ -101,14 +103,14 @@
101103
public class MongoDbMessageStore extends AbstractMessageGroupStore
102104
implements MessageStore, BeanClassLoaderAware, ApplicationContextAware, InitializingBean {
103105

106+
public static final String SEQUENCE_NAME = "messagesSequence";
107+
104108
private static final String HEADERS = "headers";
105109

106110
private static final String UNCHECKED = "unchecked";
107111

108112
private static final String GROUP_ID_MUST_NOT_BE_NULL = "'groupId' must not be null";
109113

110-
public static final String SEQUENCE_NAME = "messagesSequence";
111-
112114
private static final String DEFAULT_COLLECTION_NAME = "messages";
113115

114116
private static final String GROUP_ID_KEY = "_groupId";
@@ -132,13 +134,12 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
132134

133135
private final String collectionName;
134136

135-
private volatile ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
137+
private ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
136138

137139
private ApplicationContext applicationContext;
138140

139141
private String[] whiteListPatterns;
140142

141-
142143
/**
143144
* Create a MongoDbMessageStore using the provided {@link MongoDbFactory}.and the default collection name.
144145
* @param mongoDbFactory The mongodb factory.
@@ -178,14 +179,28 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
178179
* @param patterns the patterns.
179180
*/
180181
public void addWhiteListPatterns(String... patterns) {
181-
this.whiteListPatterns = patterns;
182+
this.whiteListPatterns = patterns != null ? Arrays.copyOf(patterns, patterns.length) : null;
183+
}
184+
185+
/**
186+
* Configure a set of converters to use in the {@link MappingMongoConverter}.
187+
* Must be instances of {@code org.springframework.core.convert.converter.Converter},
188+
* {@code org.springframework.core.convert.converter.ConverterFactory},
189+
* {@code org.springframework.core.convert.converter.GenericConverter} or
190+
* {@code org.springframework.data.convert.ConverterBuilder.ConverterAware}.
191+
* @param customConverters the converters to use.
192+
* @since 5.1.6
193+
*/
194+
public void setCustomConverters(Object... customConverters) {
195+
this.converter.setCustomConverters(customConverters);
182196
}
183197

184198
@Override
185199
public void afterPropertiesSet() {
186200
if (this.applicationContext != null) {
187201
this.converter.setApplicationContext(this.applicationContext);
188202
}
203+
189204
this.converter.afterPropertiesSet();
190205

191206
IndexOperations indexOperations = this.template.indexOps(this.collectionName);
@@ -506,26 +521,38 @@ private final class MessageReadingMongoConverter extends MappingMongoConverter {
506521

507522
private static final String CLASS = "_class";
508523

524+
private Object[] customConverters;
525+
509526
MessageReadingMongoConverter(MongoDbFactory mongoDbFactory,
510527
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
511528
super(new DefaultDbRefResolver(mongoDbFactory), mappingContext);
512529
}
513530

531+
void setCustomConverters(Object... customConverters) {
532+
this.customConverters =
533+
customConverters != null ? Arrays.copyOf(customConverters, customConverters.length) : null;
534+
}
535+
514536
@Override
515537
public void afterPropertiesSet() {
516-
List<Object> customConverters = new ArrayList<>();
517-
customConverters.add(new MessageHistoryToDocumentConverter());
518-
customConverters.add(new DocumentToGenericMessageConverter());
519-
customConverters.add(new DocumentToMutableMessageConverter());
538+
List<Object> converters = new ArrayList<>();
539+
converters.add(new MessageHistoryToDocumentConverter());
540+
converters.add(new DocumentToGenericMessageConverter());
541+
converters.add(new DocumentToMutableMessageConverter());
520542
DocumentToErrorMessageConverter docToErrorMessageConverter = new DocumentToErrorMessageConverter();
521543
if (MongoDbMessageStore.this.whiteListPatterns != null) {
522544
docToErrorMessageConverter.deserializingConverter
523545
.addWhiteListPatterns(MongoDbMessageStore.this.whiteListPatterns);
524546
}
525-
customConverters.add(docToErrorMessageConverter);
526-
customConverters.add(new DocumentToAdviceMessageConverter());
527-
customConverters.add(new ThrowableToBytesConverter());
528-
this.setCustomConversions(new MongoCustomConversions(customConverters));
547+
converters.add(docToErrorMessageConverter);
548+
converters.add(new DocumentToAdviceMessageConverter());
549+
converters.add(new ThrowableToBytesConverter());
550+
551+
if (this.customConverters != null) {
552+
Collections.addAll(converters, this.customConverters);
553+
}
554+
555+
setCustomConversions(new MongoCustomConversions(converters));
529556
super.afterPropertiesSet();
530557
}
531558

spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/store/MongoDbMessageStoreTests.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,18 @@
1616

1717
package org.springframework.integration.mongodb.store;
1818

19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.TimeUnit;
23+
24+
import org.junit.Test;
25+
26+
import org.springframework.core.convert.converter.Converter;
27+
import org.springframework.data.convert.WritingConverter;
1928
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
2029
import org.springframework.integration.store.MessageStore;
30+
import org.springframework.messaging.support.GenericMessage;
2131

2232
import com.mongodb.MongoClient;
2333

@@ -30,11 +40,56 @@
3040
public class MongoDbMessageStoreTests extends AbstractMongoDbMessageStoreTests {
3141

3242
@Override
33-
protected MessageStore getMessageStore() throws Exception {
43+
protected MessageStore getMessageStore() {
3444
MongoDbMessageStore mongoDbMessageStore =
3545
new MongoDbMessageStore(new SimpleMongoDbFactory(new MongoClient(), "test"));
3646
mongoDbMessageStore.afterPropertiesSet();
3747
return mongoDbMessageStore;
3848
}
3949

50+
@Test
51+
public void testCustomConverter() throws InterruptedException {
52+
MongoDbMessageStore mongoDbMessageStore =
53+
new MongoDbMessageStore(new SimpleMongoDbFactory(new MongoClient(), "test"));
54+
FooToBytesConverter fooToBytesConverter = new FooToBytesConverter();
55+
mongoDbMessageStore.setCustomConverters(fooToBytesConverter);
56+
mongoDbMessageStore.afterPropertiesSet();
57+
58+
mongoDbMessageStore.addMessage(new GenericMessage<>(new Foo("foo")));
59+
60+
assertThat(fooToBytesConverter.called.await(10, TimeUnit.SECONDS)).isTrue();
61+
}
62+
63+
private static class Foo {
64+
65+
String foo;
66+
67+
Foo(String foo) {
68+
this.foo = foo;
69+
}
70+
71+
@Override
72+
public String toString() {
73+
return foo;
74+
}
75+
76+
}
77+
78+
@WritingConverter
79+
private static class FooToBytesConverter implements Converter<Foo, byte[]> {
80+
81+
private CountDownLatch called = new CountDownLatch(1);
82+
83+
@Override
84+
public byte[] convert(Foo source) {
85+
try {
86+
return source.toString().getBytes();
87+
}
88+
finally {
89+
this.called.countDown();
90+
}
91+
}
92+
93+
}
94+
4095
}

src/reference/asciidoc/mongodb.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ The `MongoDbMessageStore` expands the `Message` as a Mongo document with all nes
123123
It is useful when you need to have access to the `payload` or `headers` for auditing or analytics -- for example, against stored messages.
124124

125125
IMPORTANT: The `MongoDbMessageStore` uses a custom `MappingMongoConverter` implementation to store `Message` instances as MongoDB documents, and there are some limitations for the properties (`payload` and `header` values) of the `Message`.
126-
For example, there is no ability to configure custom converters for complex domain `payload` instances or `header` values.
127-
There is also no way to provide a custom `MongoTemplate` (or `MappingMongoConverter`).
128-
To achieve these capabilities, an alternative MongoDB `MessageStore` implementation has been introduced (we describe it next).
126+
127+
Starting with version 5.1.6, the `MongoDbMessageStore` can be configured with custom converters which are propagated into an internal `MappingMongoConverter` implementation.
128+
See `MongoDbMessageStore.setCustomConverters(Object... customConverters)` JavaDocs for more information.
129129

130130
Spring Integration 3.0 introduced the `ConfigurableMongoDbMessageStore`.
131131
It implements both the `MessageStore` and `MessageGroupStore` interfaces.

src/reference/asciidoc/whats-new.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,9 @@ See <<mail-inbound>> for more information.
6969

7070
The `WebFluxRequestExecutingMessageHandler` now supports a `Publisher`, `Resource` and `MultiValueMap` as a request message `payload`.
7171
See <<webflux>> for more information.
72+
73+
[[x5.2-mongodb]]
74+
==== MongoDb Changes
75+
76+
The `MongoDbMessageStore` can now be configured with custom converters.
77+
See <<mongodb>> for more information.

0 commit comments

Comments
 (0)