diff --git a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/dsl/MongoDbOutboundGatewaySpec.java b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/dsl/MongoDbOutboundGatewaySpec.java index 97d99fa3d6..1e2d89131f 100644 --- a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/dsl/MongoDbOutboundGatewaySpec.java +++ b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/dsl/MongoDbOutboundGatewaySpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2018 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. @@ -26,6 +26,7 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.expression.common.LiteralExpression; import org.springframework.integration.dsl.MessageHandlerSpec; import org.springframework.integration.expression.FunctionExpression; +import org.springframework.integration.mongodb.outbound.MessageCollectionCallback; import org.springframework.integration.mongodb.outbound.MongoDbOutboundGateway; import org.springframework.messaging.Message; @@ -33,6 +34,8 @@ import org.springframework.messaging.Message; * A {@link MessageHandlerSpec} extension for the MongoDb Outbound endpoint {@link MongoDbOutboundGateway} * * @author Xavier Padró + * @author Artem Bilan + * * @since 5.0 */ public class MongoDbOutboundGatewaySpec @@ -149,10 +152,26 @@ public class MongoDbOutboundGatewaySpec * @param collectionCallback the {@link CollectionCallback} instance * @param

the type of the message payload. * @return the spec + * @deprecated in favor of {@link #collectionCallback(MessageCollectionCallback)} */ + @Deprecated public

MongoDbOutboundGatewaySpec collectionCallback(CollectionCallback

collectionCallback) { this.target.setCollectionCallback(collectionCallback); return this; } + /** + * Reference to an instance of {@link MessageCollectionCallback} + * which specifies the database operation to execute in the request message context. + * This property is mutually exclusive with {@link #query} and {@link #queryExpression} properties. + * @param collectionCallback the {@link MessageCollectionCallback} instance + * @param

the type of the message payload. + * @return the spec + * @since 5.0.11 + */ + public

MongoDbOutboundGatewaySpec collectionCallback(MessageCollectionCallback

collectionCallback) { + this.target.setMessageCollectionCallback(collectionCallback); + return this; + } + } diff --git a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MessageCollectionCallback.java b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MessageCollectionCallback.java new file mode 100644 index 0000000000..f64d491f3f --- /dev/null +++ b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MessageCollectionCallback.java @@ -0,0 +1,64 @@ +/* + * Copyright 2018 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 + * + * http://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 org.bson.Document; + +import org.springframework.dao.DataAccessException; +import org.springframework.data.mongodb.core.CollectionCallback; +import org.springframework.lang.Nullable; +import org.springframework.messaging.Message; + +import com.mongodb.MongoException; +import com.mongodb.client.MongoCollection; + +/** + * The callback to be used with the {@link MongoDbOutboundGateway} + * as an alternative to other query options on the gateway. + *

+ * Plays the same role as standard {@link CollectionCallback}, + * but with {@code Message requestMessage} context during {@code handleMessage()} + * process in the {@link MongoDbOutboundGateway}. + * + * @author Artem Bilan + * + * @since 5.0.11 + * + * @see CollectionCallback + */ +@FunctionalInterface +public interface MessageCollectionCallback extends CollectionCallback { + + /** + * Perform a Mongo operation in the collection using request message as a context. + * @param collection never {@literal null}. + * @param requestMessage the request message ot use for operations + * @return can be {@literal null}. + * @throws MongoException the MongoDB-specific exception + * @throws DataAccessException the data access exception + */ + @Nullable + T doInCollection(MongoCollection collection, Message requestMessage) + throws MongoException, DataAccessException; + + @Override + default T doInCollection(MongoCollection collection) throws MongoException, DataAccessException { + throw new UnsupportedOperationException("The 'doInCollection(MongoCollection, Message)' " + + "must be implemented instead."); + } + +} diff --git a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGateway.java b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGateway.java index 22b7e763a0..5d0a93e54f 100644 --- a/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGateway.java +++ b/spring-integration-mongodb/src/main/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2018 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. @@ -41,6 +41,8 @@ import org.springframework.util.Assert; * Makes outbound operations to query a MongoDb database using a {@link MongoOperations} * * @author Xavier Padró + * @author Artem Bilan + * * @since 5.0 */ public class MongoDbOutboundGateway extends AbstractReplyProducingMessageHandler { @@ -55,7 +57,7 @@ public class MongoDbOutboundGateway extends AbstractReplyProducingMessageHandler private Expression queryExpression; - private CollectionCallback collectionCallback; + private MessageCollectionCallback collectionCallback; private boolean expectSingleResult = false; @@ -90,8 +92,29 @@ public class MongoDbOutboundGateway extends AbstractReplyProducingMessageHandler this.queryExpression = EXPRESSION_PARSER.parseExpression(queryExpressionString); } + /** + * Specify a {@link CollectionCallback} to perform against MongoDB collection. + * @param collectionCallback the callback to perform against MongoDB collection. + * @deprecated in favor of {@link #setMessageCollectionCallback(MessageCollectionCallback)}. + * Will be removed in 5.2 + */ + @Deprecated public void setCollectionCallback(CollectionCallback collectionCallback) { - Assert.notNull(collectionCallback, "collectionCallback must not be null."); + Assert.notNull(collectionCallback, "'collectionCallback' must not be null."); + this.collectionCallback = + collectionCallback instanceof MessageCollectionCallback + ? (MessageCollectionCallback) collectionCallback + : (collection, requestMessage) -> collectionCallback.doInCollection(collection); + } + + /** + * Specify a {@link MessageCollectionCallback} to perform against MongoDB collection + * in the request message context. + * @param collectionCallback the callback to perform against MongoDB collection. + * @since 5.0.11 + */ + public void setMessageCollectionCallback(MessageCollectionCallback collectionCallback) { + Assert.notNull(collectionCallback, "'collectionCallback' must not be null."); this.collectionCallback = collectionCallback; } @@ -152,7 +175,8 @@ public class MongoDbOutboundGateway extends AbstractReplyProducingMessageHandler Object result; if (this.collectionCallback != null) { - result = this.mongoTemplate.execute(collectionName, this.collectionCallback); + result = this.mongoTemplate.execute(collectionName, // NOSONAR + collection -> this.collectionCallback.doInCollection(collection, requestMessage)); } else { Query query = buildQuery(requestMessage); diff --git a/spring-integration-mongodb/src/main/resources/org/springframework/integration/mongodb/config/spring-integration-mongodb-5.0.xsd b/spring-integration-mongodb/src/main/resources/org/springframework/integration/mongodb/config/spring-integration-mongodb-5.0.xsd index 80e176ff22..6135884619 100644 --- a/spring-integration-mongodb/src/main/resources/org/springframework/integration/mongodb/config/spring-integration-mongodb-5.0.xsd +++ b/spring-integration-mongodb/src/main/resources/org/springframework/integration/mongodb/config/spring-integration-mongodb-5.0.xsd @@ -239,7 +239,10 @@ Reference to an instance of - org.springframework.data.mongodb.core.CollectionCallback + org.springframework.data.mongodb.core.CollectionCallback, preferable an + instance of + org.springframework.integration.mongodb.outbound.MessageCollectionCallback + with the request message context. f - .handle(collectionCallbackOutboundGateway(MongoCollection::count)) + .handle(collectionCallbackOutboundGateway( + (collection, requestMessage) -> collection.count())) .channel(getResultChannel()); } @@ -387,7 +390,9 @@ public class MongoDbTests extends MongoDbAvailableTests { .entityClass(Person.class); } - private MongoDbOutboundGatewaySpec collectionCallbackOutboundGateway(CollectionCallback collectionCallback) { + private MongoDbOutboundGatewaySpec collectionCallbackOutboundGateway( + MessageCollectionCallback collectionCallback) { + return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter()) .collectionCallback(collectionCallback) .collectionName(COLLECTION_NAME) diff --git a/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGatewayTests.java b/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGatewayTests.java index 397b4d1cce..ef936b1b9a 100644 --- a/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGatewayTests.java +++ b/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/outbound/MongoDbOutboundGatewayTests.java @@ -37,6 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.BulkOperations; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; @@ -49,17 +50,16 @@ import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.mongodb.client.MongoCollection; +import org.springframework.test.context.junit4.SpringRunner; /** * @author Xavier Padró + * @author Gary Rssell + * @author Artem Bilan + * * @since 5.0 */ -@ContextConfiguration -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(SpringRunner.class) @DirtiesContext public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @@ -95,14 +95,12 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { mongoTemplate.dropCollection(COLLECTION_NAME); } - @SuppressWarnings("ConstantConditions") @Test @MongoDbAvailable public void testNoFactorySpecified() { - MongoDbFactory nullFactory = null; try { - new MongoDbOutboundGateway(nullFactory); + new MongoDbOutboundGateway((MongoDbFactory) null); Assert.fail("Expected the test case to throw an IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -110,14 +108,11 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { } } - @SuppressWarnings("ConstantConditions") @Test @MongoDbAvailable public void testNoTemplateSpecified() { - MongoOperations mongoTemplate = null; - try { - new MongoDbOutboundGateway(mongoTemplate); + new MongoDbOutboundGateway((MongoTemplate) null); Assert.fail("Expected the test case to throw an IllegalArgumentException"); } catch (IllegalArgumentException e) { @@ -193,7 +188,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testListOfResultsWithQueryExpression() throws Exception { + public void testListOfResultsWithQueryExpression() { Message message = MessageBuilder.withPayload("{}").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setEntityClass(Person.class); @@ -208,7 +203,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testListOfResultsWithQueryExpressionReturningOneResult() throws Exception { + public void testListOfResultsWithQueryExpressionReturningOneResult() { Message message = MessageBuilder.withPayload("{name : 'Xavi'}").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setEntityClass(Person.class); @@ -224,7 +219,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testSingleResultWithQueryExpressionAsString() throws Exception { + public void testSingleResultWithQueryExpressionAsString() { Message message = MessageBuilder.withPayload("{name : 'Artem'}").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setQueryExpression(PARSER.parseExpression("payload")); @@ -240,7 +235,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testSingleResultWithQueryExpressionAsQuery() throws Exception { + public void testSingleResultWithQueryExpressionAsQuery() { Message message = MessageBuilder.withPayload("").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setQueryExpression(PARSER.parseExpression("new BasicQuery('{''name'' : ''Gary''}')")); @@ -271,7 +266,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testWithNullCollectionNameExpression() throws Exception { + public void testWithNullCollectionNameExpression() { MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(mongoDbFactory); gateway.setBeanFactory(beanFactory); gateway.setQueryExpression(new LiteralExpression("{name : 'Xavi'}")); @@ -288,7 +283,7 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testWithCollectionNameExpressionSpecified() throws Exception { + public void testWithCollectionNameExpressionSpecified() { Message message = MessageBuilder.withPayload("").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setQueryExpression(new LiteralExpression("{name : 'Xavi'}")); @@ -307,13 +302,13 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testWithCollectionCallbackCount() throws Exception { + public void testWithCollectionCallbackCount() { Message message = MessageBuilder.withPayload("").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setEntityClass(Person.class); gateway.setCollectionNameExpression(new LiteralExpression("data")); - gateway.setCollectionCallback(MongoCollection::count); + gateway.setMessageCollectionCallback((collection, requestMessage) -> collection.count()); gateway.afterPropertiesSet(); long result = (long) gateway.handleRequestMessage(message); @@ -323,15 +318,15 @@ public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests { @Test @MongoDbAvailable - public void testWithCollectionCallbackFindOne() throws Exception { - Message message = MessageBuilder.withPayload("").build(); + public void testWithCollectionCallbackFindOne() { + Message message = MessageBuilder.withPayload("Mike").build(); MongoDbOutboundGateway gateway = createGateway(); gateway.setEntityClass(Person.class); gateway.setCollectionNameExpression(new LiteralExpression("data")); gateway.setRequiresReply(false); - gateway.setCollectionCallback(collection -> { - collection.insertOne(new Document("name", "Mike")); + gateway.setMessageCollectionCallback((collection, requestMessage) -> { + collection.insertOne(new Document("name", requestMessage.getPayload())); return null; }); gateway.afterPropertiesSet(); diff --git a/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/rules/MongoDbAvailableTests.java b/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/rules/MongoDbAvailableTests.java index f8aa7a2727..4700f856c0 100644 --- a/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/rules/MongoDbAvailableTests.java +++ b/spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/rules/MongoDbAvailableTests.java @@ -23,13 +23,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.core.CollectionCallback; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.SimpleMongoDbFactory; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.integration.mongodb.outbound.MessageCollectionCallback; +import org.springframework.messaging.Message; import com.mongodb.MongoClient; import com.mongodb.MongoException; @@ -64,7 +65,7 @@ public abstract class MongoDbAvailableTests { } } - public Person createPerson() { + protected Person createPerson() { Address address = new Address(); address.setCity("Philadelphia"); address.setStreet("2121 Rawn street"); @@ -76,7 +77,7 @@ public abstract class MongoDbAvailableTests { return person; } - public Person createPerson(String name) { + protected Person createPerson(String name) { Address address = new Address(); address.setCity("Philadelphia"); address.setStreet("2121 Rawn street"); @@ -166,10 +167,12 @@ public abstract class MongoDbAvailableTests { } - public static class TestCollectionCallback implements CollectionCallback { + public static class TestCollectionCallback implements MessageCollectionCallback { @Override - public Long doInCollection(MongoCollection collection) throws MongoException, DataAccessException { + public Long doInCollection(MongoCollection collection, Message message) + throws MongoException, DataAccessException { + return collection.count(); } diff --git a/src/reference/asciidoc/mongodb.adoc b/src/reference/asciidoc/mongodb.adoc index b4ca7a0cdb..3ef7e371a1 100644 --- a/src/reference/asciidoc/mongodb.adoc +++ b/src/reference/asciidoc/mongodb.adoc @@ -187,12 +187,14 @@ The _MongoDb Inbound Channel Adapter_ is a polling consumer that reads data from As you can see from the configuration above, you configure a _MongoDb Inbound Channel Adapter_ using the `inbound-channel-adapter` element, providing values for various attributes such as: -* `query` - a JSON query (see http://www.mongodb.org/display/DOCS/Querying[MongoDb Querying]) -* `query-expression` - A SpEL expression that is evaluated to a JSON query String (as the `query` attribute above), or to an instance of `o.s.data.mongodb.core.query.Query`. Mutually exclusive with `query` attribute. -* `entity-class` - the type of the payload object; if not supplied, a `com.mongodb.DBObject` will be returned. -* `collection-name` or `collection-name-expression` - Identifies the name of the MongoDb collection to use. -* `mongodb-factory` - reference to an instance of `o.s.data.mongodb.MongoDbFactory` -* `mongo-template` - reference to an instance of `o.s.data.mongodb.core.MongoTemplate` +* `query`: A JSON query (see http://www.mongodb.org/display/DOCS/Querying[MongoDB Querying]) +* `query-expression`: A SpEL expression that is evaluated to a JSON query string (as the `query` attribute above) or to an instance of `o.s.data.mongodb.core.query.Query`. +Mutually exclusive with the `query` attribute. +* `entity-class`: The type of the payload object. If not supplied, a `com.mongodb.DBObject` is returned. +* `collection-name` or `collection-name-expression`: Identifies the name of the MongoDB collection to use. +* `mongodb-factory`: Reference to an instance of `o.s.data.mongodb.MongoDbFactory` +* `mongo-template`: Reference to an instance of `o.s.data.mongodb.core.MongoTemplate` +* Other attributes that are common across all other inbound adapters (such as 'channel'). @@ -321,6 +323,20 @@ Please refer to http://www.mongodb.org/display/DOCS/Querying[MongoDB documentati * `collection-callback` - reference to an instance of `org.springframework.data.mongodb.core.CollectionCallback` (NOTE: you can not have both collection-callback and any of the query attributes). +* `collection-name` or `collection-name-expression`: Identifies the name of the MongoDB collection to use. +* `mongo-converter`: Reference to an instance of `o.s.data.mongodb.core.convert.MongoConverter` that assists with converting a raw Java object to a JSON document representation. +* `mongodb-factory`: Reference to an instance of `o.s.data.mongodb.MongoDbFactory`. +* `mongo-template`: Reference to an instance of `o.s.data.mongodb.core.MongoTemplate`. +NOTE: you can not set both `mongo-template` and `mongodb-factory`. +* `entity-class`: The fully qualified name of the entity class to be passed to the `find(..)` and `findOne(..)` methods in MongoTemplate. +If this attribute is not provided, the default value is `org.bson.Document`. +* `query` or `query-expression`: Specifies the MongoDB query. +See the http://www.mongodb.org/display/DOCS/Querying[MongoDB documentation] for more query samples. +* `collection-callback`: Reference to an instance of `org.springframework.data.mongodb.core.CollectionCallback`. +Preferable an instance of `org.springframework.integration.mongodb.outbound.MessageCollectionCallback` since 5.0.11 with the request message context. +See its Javadocs for more information. +NOTE: You can not have both `collection-callback` and any of the query attributes. + ==== Configuring with Java Configuration The following Spring Boot application provides an example of configuring the outbound gateway using Java configuration: @@ -399,15 +415,14 @@ public class MongoDbJavaApplication { } ---- -Alternatively to the `query` and `query-expression` properties, you can specify other database operations through -the `collectionCallback` property. +As an alternate to the `query` and `query-expression` properties, you can specify other database operations by using the `collectionCallback` property as a reference to the `MessageCollectionCallback` functional interface implementation. The following example specifies a count operation: [source, java] ---- private MongoDbOutboundGatewaySpec collectionCallbackOutboundGateway() { return MongoDb.outboundGateway(this.mongoDbFactory, this.mongoConverter) - .collectionCallback(MongoCollection::count) + .collectionCallback((collection, requestMessage) -> collection.count()) .collectionName("foo"); } ----