INT-4568: Add reactive MongoDbMH

JIRA: https://jira.spring.io/browse/INT-4568

* Refactor `AbstractMessageHandler` and implement `MongodbReactiveMessageHandler`

* Rename `AbstractBaseMessageHandler` to `MessageHandlerSupport`

* Clean up code style and resolve possible Sonar smells
This commit is contained in:
Artem Bilan
2019-11-27 17:51:35 -05:00
parent ebadfde609
commit 2263827b3f
10 changed files with 860 additions and 305 deletions

View File

@@ -17,12 +17,14 @@
package org.springframework.integration.mongodb.outbound;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.bson.conversions.Bson;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@@ -49,29 +51,41 @@ import org.springframework.messaging.Message;
*/
public class MongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
@Test(expected = IllegalArgumentException.class)
public void withNullMongoDBFactory() {
new MongoDbStoringMessageHandler((MongoDbFactory) null);
}
private MongoTemplate template;
@Test(expected = IllegalArgumentException.class)
public void withNullMongoTemplate() {
new MongoDbStoringMessageHandler((MongoOperations) null);
private MongoDbFactory mongoDbFactory;
@Before
public void setUp() {
mongoDbFactory = prepareMongoFactory("foo");
template = new MongoTemplate(mongoDbFactory);
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithDefaultCollection() throws Exception {
public void withNullMongoDBFactory() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new MongoDbStoringMessageHandler((MongoDbFactory) null));
}
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(mongoDbFactory);
@Test
@MongoDbAvailable
public void withNullMongoTemplate() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new MongoDbStoringMessageHandler((MongoOperations) null));
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithDefaultCollection() {
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
handler.handleMessage(message);
MongoTemplate template = new MongoTemplate(mongoDbFactory);
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = template.findOne(query, Person.class, "data");
@@ -81,17 +95,15 @@ public class MongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
@Test
@MongoDbAvailable
public void validateMessageHandlingWithNamedCollection() throws Exception {
public void validateMessageHandlingWithNamedCollection() {
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(mongoDbFactory);
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
handler.handleMessage(message);
MongoTemplate template = new MongoTemplate(mongoDbFactory);
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = template.findOne(query, Person.class, "foo");
@@ -101,10 +113,9 @@ public class MongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
@Test
@MongoDbAvailable
public void validateMessageHandlingWithMongoConverter() throws Exception {
public void validateMessageHandlingWithMongoConverter() {
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(mongoDbFactory);
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
MappingMongoConverter converter = new TestMongoConverter(mongoDbFactory, new MongoMappingContext());
converter.afterPropertiesSet();
@@ -115,7 +126,6 @@ public class MongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
handler.handleMessage(message);
MongoTemplate template = new MongoTemplate(mongoDbFactory);
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = template.findOne(query, Person.class, "foo");
@@ -126,27 +136,26 @@ public class MongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
@Test
@MongoDbAvailable
public void validateMessageHandlingWithMongoTemplate() throws Exception {
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MappingMongoConverter converter = new TestMongoConverter(mongoDbFactory, new MongoMappingContext());
public void validateMessageHandlingWithMongoTemplate() {
MappingMongoConverter converter = new TestMongoConverter(this.mongoDbFactory, new MongoMappingContext());
converter.afterPropertiesSet();
converter = spy(converter);
MongoTemplate template = new MongoTemplate(mongoDbFactory, converter);
MongoTemplate writingTemplate = new MongoTemplate(this.mongoDbFactory, converter);
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(template);
MongoDbStoringMessageHandler handler = new MongoDbStoringMessageHandler(writingTemplate);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
handler.handleMessage(message);
MongoTemplate readingTemplate = new MongoTemplate(mongoDbFactory);
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = readingTemplate.findOne(query, Person.class, "foo");
Person person = template.findOne(query, Person.class, "foo");
assertThat(person.getName()).isEqualTo("Bob");
assertThat(person.getAddress().getState()).isEqualTo("PA");
verify(converter, times(1)).write(Mockito.any(), Mockito.any(Bson.class));
}
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.mongodb.outbound;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import reactor.core.publisher.Mono;
/**
* @author Amol Nayak
* @author Oleg Zhurakousky
* @author Gary Russell
* @author David Turanski
*
* @since 5.3
*/
public class ReactiveMongoDbStoringMessageHandlerTests extends MongoDbAvailableTests {
private ReactiveMongoTemplate template;
private ReactiveMongoDatabaseFactory mongoDbFactory;
@Before
public void setUp() {
mongoDbFactory = this.prepareReactiveMongoFactory("foo");
template = new ReactiveMongoTemplate(mongoDbFactory);
}
@Test
@MongoDbAvailable
public void withNullMongoDBFactory() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new ReactiveMongoDbStoringMessageHandler((ReactiveMongoDatabaseFactory) null));
}
@Test
@MongoDbAvailable
public void withNullMongoTemplate() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new ReactiveMongoDbStoringMessageHandler((ReactiveMongoOperations) null));
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithDefaultCollection() {
ReactiveMongoDbStoringMessageHandler handler = new ReactiveMongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
waitFor(handler.handleMessage(message));
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = waitFor(this.template.findOne(query, Person.class, "data"));
assertThat(person.getName()).isEqualTo("Bob");
assertThat(person.getAddress().getState()).isEqualTo("PA");
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithNamedCollection() {
ReactiveMongoDbStoringMessageHandler handler = new ReactiveMongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
waitFor(handler.handleMessage(message));
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = waitFor(this.template.findOne(query, Person.class, "foo"));
assertThat(person.getName()).isEqualTo("Bob");
assertThat(person.getAddress().getState()).isEqualTo("PA");
}
@Test
@MongoDbAvailable
public void errorOnMessageHandlingWithNullValuedExpression() {
ReactiveMongoDbStoringMessageHandler handler = new ReactiveMongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setCollectionNameExpression(new LiteralExpression(null));
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(createPerson("Bob")).build();
AtomicBoolean errorOccurred = new AtomicBoolean();
handler.handleMessage(message)
.doOnError(e -> errorOccurred.set(true))
.subscribe(aVoid -> assertThat(errorOccurred.get()).isTrue());
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithMongoConverter() {
ReactiveMongoDbStoringMessageHandler handler = new ReactiveMongoDbStoringMessageHandler(this.mongoDbFactory);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
MappingMongoConverter converter =
new ReactiveTestMongoConverter(this.mongoDbFactory, new MongoMappingContext());
converter.afterPropertiesSet();
converter = spy(converter);
handler.setMongoConverter(converter);
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
waitFor(handler.handleMessage(message));
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = waitFor(this.template.findOne(query, Person.class, "foo"));
assertThat(person.getName()).isEqualTo("Bob");
assertThat(person.getAddress().getState()).isEqualTo("PA");
}
@Test
@MongoDbAvailable
public void validateMessageHandlingWithMongoTemplate() {
MappingMongoConverter converter =
new ReactiveTestMongoConverter(this.mongoDbFactory, new MongoMappingContext());
converter.afterPropertiesSet();
converter = spy(converter);
ReactiveMongoTemplate writingTemplate = new ReactiveMongoTemplate(this.mongoDbFactory, converter);
ReactiveMongoDbStoringMessageHandler handler = new ReactiveMongoDbStoringMessageHandler(writingTemplate);
handler.setCollectionNameExpression(new LiteralExpression("foo"));
handler.setBeanFactory(mock(BeanFactory.class));
handler.afterPropertiesSet();
Message<Person> message = MessageBuilder.withPayload(this.createPerson("Bob")).build();
waitFor(handler.handleMessage(message));
Query query = new BasicQuery("{'name' : 'Bob'}");
Person person = waitFor(this.template.findOne(query, Person.class, "foo"));
assertThat(person.getName()).isEqualTo("Bob");
assertThat(person.getAddress().getState()).isEqualTo("PA");
}
private static <T> T waitFor(Mono<T> mono) {
return mono.block(Duration.ofSeconds(3));
}
}

View File

@@ -16,6 +16,8 @@
package org.springframework.integration.mongodb.rules;
import java.time.Duration;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.junit.Rule;
@@ -23,10 +25,14 @@ import org.junit.Rule;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDbFactory;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.integration.mongodb.outbound.MessageCollectionCallback;
@@ -36,19 +42,21 @@ import com.mongodb.MongoException;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
/**
* Convenience base class that enables unit test methods to rely upon the {@link MongoDbAvailable} annotation.
*
* @author Oleg Zhurakousky
* @author Xavier Padro
* @author Artem Bilan
* @author David Turanski
*
* @since 2.1
*/
public abstract class MongoDbAvailableTests {
@Rule
public MongoDbAvailableRule redisAvailableRule = new MongoDbAvailableRule();
public MongoDbAvailableRule mongoDbAvailableRule = new MongoDbAvailableRule();
protected MongoDbFactory prepareMongoFactory(String... additionalCollectionsToDrop) {
@@ -57,6 +65,25 @@ public abstract class MongoDbAvailableTests {
return mongoDbFactory;
}
protected ReactiveMongoDatabaseFactory prepareReactiveMongoFactory(String... additionalCollectionsToDrop) {
ReactiveMongoDatabaseFactory mongoDbFactory = new SimpleReactiveMongoDatabaseFactory(
com.mongodb.reactivestreams.client.MongoClients.create(), "test");
cleanupCollections(mongoDbFactory, additionalCollectionsToDrop);
return mongoDbFactory;
}
protected void cleanupCollections(ReactiveMongoDatabaseFactory mongoDbFactory,
String... additionalCollectionsToDrop) {
ReactiveMongoTemplate template = new ReactiveMongoTemplate(mongoDbFactory);
template.dropCollection("messages").block(Duration.ofSeconds(3));
template.dropCollection("configurableStoreMessages").block(Duration.ofSeconds(3));
template.dropCollection("data").block(Duration.ofSeconds(3));
for (String additionalCollection : additionalCollectionsToDrop) {
template.dropCollection(additionalCollection).block(Duration.ofSeconds(3));
}
}
protected void cleanupCollections(MongoDbFactory mongoDbFactory, String... additionalCollectionsToDrop) {
MongoTemplate template = new MongoTemplate(mongoDbFactory);
template.dropCollection("messages");
@@ -170,6 +197,27 @@ public abstract class MongoDbAvailableTests {
}
public static class ReactiveTestMongoConverter extends MappingMongoConverter {
public ReactiveTestMongoConverter(
ReactiveMongoDatabaseFactory mongoDbFactory,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
super(NoOpDbRefResolver.INSTANCE, mappingContext);
}
@Override
public void write(Object source, Bson target) {
super.write(source, target);
}
@Override
public <S> S read(Class<S> clazz, Bson source) {
return super.read(clazz, source);
}
}
public static class TestCollectionCallback implements MessageCollectionCallback<Long> {
@Override