INT-3335: Implement MongoDb Outbound Gateway

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

INT-3335: PR fixes

INT-3335: fix code style

INT-3335: Add queryExpressionString + minor fixes

* Polishing. Mostly code style
This commit is contained in:
Xavier Padro
2016-12-09 00:02:45 +01:00
committed by Artem Bilan
parent 318bb4c4b7
commit 0fa8849f7f
18 changed files with 1857 additions and 16 deletions

View File

@@ -31,5 +31,6 @@ public class MongoDbNamespaceHandler extends AbstractIntegrationNamespaceHandler
public void init() {
registerBeanDefinitionParser("inbound-channel-adapter", new MongoDbInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new MongoDbOutboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-gateway", new MongoDbOutboundGatewayParser());
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2016 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.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractConsumerEndpointParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.mongodb.outbound.MongoDbOutboundGateway;
import org.springframework.util.StringUtils;
/**
* Parser for MongoDb outbound gateways
*
* @author Xavier Padró
* @since 5.0
*/
public class MongoDbOutboundGatewayParser extends AbstractConsumerEndpointParser {
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
final BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(MongoDbOutboundGateway.class);
MongoParserUtils.processCommonAttributes(element, parserContext, builder);
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout");
String replyChannel = element.getAttribute("reply-channel");
if (StringUtils.hasText(replyChannel)) {
builder.addPropertyReference("outputChannel", replyChannel);
}
BeanDefinition queryExpressionDef =
IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("query",
"query-expression", parserContext, element, true);
if (queryExpressionDef != null) {
builder.addPropertyValue("queryExpression", queryExpressionDef);
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "entity-class");
return builder;
}
@Override
protected String getInputChannelAttributeName() {
return "request-channel";
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2016 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.dsl;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
/**
* Factory class for building MongoDb components
*
* @author Xavier Padró
* @since 5.0
*/
public final class MongoDb {
public static MongoDbOutboundGatewaySpec outboundGateway(
MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
return new MongoDbOutboundGatewaySpec(mongoDbFactory, mongoConverter);
}
public static MongoDbOutboundGatewaySpec outboundGateway(MongoOperations mongoTemplate) {
return new MongoDbOutboundGatewaySpec(mongoTemplate);
}
private MongoDb() {
super();
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2016 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.dsl;
import java.util.function.Function;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.convert.MongoConverter;
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.MongoDbOutboundGateway;
import org.springframework.messaging.Message;
/**
* A {@link MessageHandlerSpec} extension for the MongoDb Outbound endpoint {@link MongoDbOutboundGateway}
*
* @author Xavier Padró
* @since 5.0
*/
public class MongoDbOutboundGatewaySpec
extends MessageHandlerSpec<MongoDbOutboundGatewaySpec, MongoDbOutboundGateway> {
MongoDbOutboundGatewaySpec(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
this.target = new MongoDbOutboundGateway(mongoDbFactory, mongoConverter);
this.target.setRequiresReply(true);
}
MongoDbOutboundGatewaySpec(MongoOperations mongoTemplate) {
this.target = new MongoDbOutboundGateway(mongoTemplate);
this.target.setRequiresReply(true);
}
public MongoDbOutboundGatewaySpec expectSingleResult(boolean expectSingleResult) {
this.target.setExpectSingleResult(expectSingleResult);
return this;
}
public MongoDbOutboundGatewaySpec query(String query) {
this.target.setQueryExpression(new LiteralExpression(query));
return this;
}
public MongoDbOutboundGatewaySpec queryExpression(String queryExpression) {
this.target.setQueryExpressionString(queryExpression);
return this;
}
public <P> MongoDbOutboundGatewaySpec queryFunction(Function<Message<P>, Query> queryFunction) {
this.target.setQueryExpression(new FunctionExpression<>(queryFunction));
return this;
}
public MongoDbOutboundGatewaySpec entityClass(Class<?> entityClass) {
this.target.setEntityClass(entityClass);
return this;
}
public MongoDbOutboundGatewaySpec collectionName(String collectionName) {
this.target.setCollectionNameExpression(new LiteralExpression(collectionName));
return this;
}
public MongoDbOutboundGatewaySpec collectionNameExpression(String collectionNameExpression) {
this.target.setCollectionNameExpressionString(collectionNameExpression);
return this;
}
public <P> MongoDbOutboundGatewaySpec collectionNameFunction(Function<Message<P>, String> collectionNameFunction) {
this.target.setCollectionNameExpression(new FunctionExpression<>(collectionNameFunction));
return this;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provides MongoDB Components support for Java DSL.
*/
package org.springframework.integration.mongodb.dsl;

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2016 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.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
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.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
/**
* Makes outbound operations to query a MongoDb database using a {@link MongoOperations}
*
* @author Xavier Padró
* @since 5.0
*/
public class MongoDbOutboundGateway extends AbstractReplyProducingMessageHandler {
private MongoDbFactory mongoDbFactory;
private MongoConverter mongoConverter;
private MongoOperations mongoTemplate;
private EvaluationContext evaluationContext;
private Expression queryExpression;
private boolean expectSingleResult = false;
private Class<?> entityClass = Document.class;
private Expression collectionNameExpression;
public MongoDbOutboundGateway(MongoDbFactory mongoDbFactory) {
this(mongoDbFactory, new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory),
new MongoMappingContext()));
}
public MongoDbOutboundGateway(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) {
Assert.notNull(mongoDbFactory, "mongoDbFactory must not be null.");
Assert.notNull(mongoConverter, "mongoConverter must not be null.");
this.mongoDbFactory = mongoDbFactory;
this.mongoConverter = mongoConverter;
}
public MongoDbOutboundGateway(MongoOperations mongoTemplate) {
Assert.notNull(mongoTemplate, "mongoTemplate must not be null.");
this.mongoTemplate = mongoTemplate;
}
public void setQueryExpression(Expression queryExpression) {
Assert.notNull(queryExpression, "queryExpression must not be null.");
this.queryExpression = queryExpression;
}
public void setQueryExpressionString(String queryExpressionString) {
Assert.notNull(queryExpressionString, "queryExpressionString must not be null.");
this.queryExpression = EXPRESSION_PARSER.parseExpression(queryExpressionString);
}
public void setExpectSingleResult(boolean expectSingleResult) {
this.expectSingleResult = expectSingleResult;
}
public void setEntityClass(Class<?> entityClass) {
Assert.notNull(entityClass, "entityClass must not be null.");
this.entityClass = entityClass;
}
public void setCollectionNameExpression(Expression collectionNameExpression) {
Assert.notNull(collectionNameExpression, "collectionNameExpression must not be null.");
this.collectionNameExpression = collectionNameExpression;
}
public void setCollectionNameExpressionString(String collectionNameExpressionString) {
Assert.notNull(collectionNameExpressionString, "collectionNameExpressionString must not be null.");
this.collectionNameExpression = EXPRESSION_PARSER.parseExpression(collectionNameExpressionString);
}
public void setMongoConverter(MongoConverter mongoConverter) {
Assert.notNull(mongoConverter, "mongoConverter cannot be null");
Assert.isNull(this.mongoTemplate,
"'mongoConverter' can not be set when instance was constructed with MongoTemplate");
this.mongoConverter = mongoConverter;
}
@Override
protected void doInit() {
Assert.state(this.queryExpression != null, "no query specified");
Assert.state(this.collectionNameExpression != null, "no collection name specified");
if (this.evaluationContext == null) {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
TypeLocator typeLocator = this.evaluationContext.getTypeLocator();
if (typeLocator instanceof StandardTypeLocator) {
((StandardTypeLocator) typeLocator).registerImport(Query.class.getPackage().getName());
}
}
if (this.mongoTemplate == null) {
this.mongoTemplate = new MongoTemplate(this.mongoDbFactory, this.mongoConverter);
}
}
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
String collectionName =
this.collectionNameExpression.getValue(this.evaluationContext, requestMessage, String.class);
Query query = buildQuery(requestMessage);
Object result;
if (this.expectSingleResult) {
result = this.mongoTemplate.findOne(query, this.entityClass, collectionName);
}
else {
result = this.mongoTemplate.find(query, this.entityClass, collectionName);
}
return result;
}
private Query buildQuery(Message<?> requestMessage) {
Query query;
Object expressionValue =
this.queryExpression.getValue(this.evaluationContext, requestMessage, Object.class);
if (expressionValue instanceof String) {
query = new BasicQuery((String) expressionValue);
}
else if (expressionValue instanceof Query) {
query = ((Query) expressionValue);
}
else {
throw new IllegalStateException("'queryExpression' must evaluate to " +
"String or org.springframework.data.mongodb.core.query.Query");
}
return query;
}
}

View File

@@ -102,6 +102,143 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-gateway">
<xsd:annotation>
<xsd:documentation>
Configures a Consumer Endpoint for the
'org.springframework.integration.mongodb.outbound.MongoDbOutboundGateway' for
querying a MongoDb database in response to a message on the request channel.
The response received from the database will be used to create the response
Message on the reply channel.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="mongodbAdapterType">
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The Message Channel where messages will be sent in order
to query the database.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
The Message Channel to which the database response will be sent.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Allows you to specify how long this gateway will wait for
the reply message to be sent successfully to the reply channel
before throwing an exception. This attribute only applies when the
channel might block, for example when using a bounded queue channel that
is currently full.
Also, keep in mind that when sending to a DirectChannel, the
invocation will occur in the sender's thread. Therefore,
the failing of the send operation may be caused by other
components further downstream.
The "reply-timeout" attribute maps to the "sendTimeout" property of the
underlying 'MessagingTemplate' instance (org.springframework.integration.core.MessagingTemplate).
The attribute will default, if not specified, to '-1', meaning that
by default, the Gateway will wait indefinitely. The value is
specified in milliseconds.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="requires-reply" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether this outbound gateway must return a non-null value. This value is
'true' by default, and a ReplyRequiredException will be thrown when
the underlying service returns a null value.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="order">
<xsd:annotation>
<xsd:documentation>
Specifies the order for invocation when this endpoint is connected as a
subscriber to a SubscribableChannel.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expect-single-result">
<xsd:annotation>
<xsd:documentation><![CDATA[
This parameter indicates that only one result object will be
returned from the database by using a findOne query.
If set to 'false', the complete result list is returned
as the payload.
]]>
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:union memberTypes="xsd:boolean xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="query" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
String representation of a MongoDb Query (e.g.,
query="{'name' : 'Bob'}").
Please refer to MongoDb documentation for more query samples
http://www.mongodb.org/display/DOCS/Querying
This attribute is
mutually exclusive with 'query-expression' attribute.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="query-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
SpEL expression which should resolve to a String query (please refer to the 'query'
attribute), or to an instance of MongoDb Query (e.q.,
query-expression="new BasicQuery('{''name'' : ''Bob''}').limit(2)").
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="entity-class" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<xsd:documentation>
The fully qualified name of the entity class to be passed to
find(..) or findOne(..) method MongoTemplate.
If this attribute is not provided the default value is org.bson.Document
</xsd:documentation>
<tool:annotation kind="direct">
<tool:expected-type type="java.lang.Class" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="mongodbAdapterType">
<xsd:annotation>
<xsd:documentation>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/mongodb http://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb.xsd">
<int:channel id="in"/>
<int:channel id="out"/>
<int-mongodb:outbound-gateway id="minimalConfig"
query="{'name' : 'foo'}"
collection-name="foo"
request-channel="in"
reply-channel="out"/>
<int-mongodb:outbound-gateway id="fullConfigWithCollectionExpression"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
collection-name-expression="headers.collectionName"
query="{'name' : 'foo'}"
request-channel="in"
reply-channel="out"/>
<int-mongodb:outbound-gateway id="fullConfigWithCollection"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query="{'name' : 'foo'}"
collection-name="foo"
request-channel="in"
reply-channel="out"/>
<int-mongodb:outbound-gateway id="fullConfigWithTemplate"
mongo-template="mongoDbTemplate"
collection-name="foo"
query="{'name' : 'foo'}"
request-channel="in"
reply-channel="out"/>
<bean id="mongoDbFactory" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.data.mongodb.MongoDbFactory"/>
</bean>
<bean id="mongoConverter" class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.TestMongoConverter">
<constructor-arg ref="mongoDbFactory"/>
<constructor-arg>
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
</constructor-arg>
</bean>
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory"/>
</bean>
</beans>

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2016 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.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.integration.mongodb.outbound.MongoDbOutboundGateway;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Xavier Padró
* @since 5.0
*/
@ContextConfiguration
@RunWith(SpringRunner.class)
@DirtiesContext
public class MongoDbOutboundGatewayParserTests {
@Autowired
private ApplicationContext context;
@Autowired
private MongoDbFactory mongoDbFactory;
@Autowired
private MongoConverter mongoConverter;
@Test
public void minimalConfig() {
MongoDbOutboundGateway gateway =
TestUtils.getPropertyValue(context.getBean("minimalConfig"), "handler", MongoDbOutboundGateway.class);
assertNotNull(TestUtils.getPropertyValue(gateway, "mongoTemplate"));
assertSame(this.mongoDbFactory, TestUtils.getPropertyValue(gateway, "mongoDbFactory"));
assertNotNull(TestUtils.getPropertyValue(gateway, "evaluationContext"));
assertTrue(TestUtils.getPropertyValue(gateway, "collectionNameExpression") instanceof LiteralExpression);
assertEquals("foo", TestUtils.getPropertyValue(gateway, "collectionNameExpression.literalValue"));
}
@Test
public void fullConfigWithCollectionExpression() {
MongoDbOutboundGateway gateway = TestUtils.getPropertyValue(
context.getBean("fullConfigWithCollectionExpression"), "handler", MongoDbOutboundGateway.class);
assertNotNull(TestUtils.getPropertyValue(gateway, "mongoTemplate"));
assertSame(this.mongoDbFactory, TestUtils.getPropertyValue(gateway, "mongoDbFactory"));
assertSame(this.mongoConverter, TestUtils.getPropertyValue(gateway, "mongoConverter"));
assertNotNull(TestUtils.getPropertyValue(gateway, "evaluationContext"));
assertTrue(TestUtils.getPropertyValue(gateway, "collectionNameExpression") instanceof SpelExpression);
assertEquals("headers.collectionName",
TestUtils.getPropertyValue(gateway, "collectionNameExpression.expression"));
}
@Test
public void fullConfigWithCollection() {
MongoDbOutboundGateway gateway = TestUtils.getPropertyValue(
context.getBean("fullConfigWithCollection"), "handler", MongoDbOutboundGateway.class);
assertNotNull(TestUtils.getPropertyValue(gateway, "mongoTemplate"));
assertSame(this.mongoDbFactory, TestUtils.getPropertyValue(gateway, "mongoDbFactory"));
assertSame(this.mongoConverter, TestUtils.getPropertyValue(gateway, "mongoConverter"));
assertNotNull(TestUtils.getPropertyValue(gateway, "evaluationContext"));
assertTrue(TestUtils.getPropertyValue(gateway, "collectionNameExpression") instanceof LiteralExpression);
assertEquals("foo", TestUtils.getPropertyValue(gateway, "collectionNameExpression.literalValue"));
}
@Test
public void fullConfigWithMongoTemplate() {
MongoDbOutboundGateway gateway = TestUtils.getPropertyValue(
context.getBean("fullConfigWithTemplate"), "handler", MongoDbOutboundGateway.class);
assertEquals(context.getBean("mongoDbTemplate"), TestUtils.getPropertyValue(gateway, "mongoTemplate"));
assertNull(TestUtils.getPropertyValue(gateway, "mongoDbFactory"));
assertNull(TestUtils.getPropertyValue(gateway, "mongoConverter"));
assertNotNull(TestUtils.getPropertyValue(gateway, "evaluationContext"));
assertTrue(TestUtils.getPropertyValue(gateway, "collectionNameExpression") instanceof LiteralExpression);
assertEquals("foo", TestUtils.getPropertyValue(gateway, "collectionNameExpression.literalValue"));
}
@Test(expected = BeanDefinitionParsingException.class)
public void templateAndFactoryFail() {
new ClassPathXmlApplicationContext("outbound-gateway-fail-template-factory-config.xml", this.getClass())
.close();
}
@Test(expected = BeanDefinitionParsingException.class)
public void templateAndConverterFail() {
new ClassPathXmlApplicationContext("outbound-gateway-fail-template-converter-config.xml",
this.getClass()).close();
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/mongodb http://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb.xsd">
<int:channel id="in"/>
<int:channel id="out"/>
<int-mongodb:outbound-gateway id="gatewayWithConverterAndTemplate"
mongo-converter="mongoConverter"
mongo-template="mongoDbTemplate"
collection-name="foo"
request-channel="in"
reply-channel="out"/>
<bean id="mongoDbFactory" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.data.mongodb.MongoDbFactory"/>
</bean>
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory"/>
</bean>
<bean id="mongoConverter" class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.TestMongoConverter">
<constructor-arg ref="mongoDbFactory"/>
<constructor-arg>
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
</constructor-arg>
</bean>
</beans>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/mongodb http://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb.xsd">
<int:channel id="in"/>
<int:channel id="out"/>
<int-mongodb:outbound-gateway id="gatewayWithFactoryAndTemplate"
mongodb-factory="mongoDbFactory"
mongo-template="mongoDbTemplate"
collection-name="foo"
request-channel="in"
reply-channel="out"/>
<bean id="mongoDbFactory" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.data.mongodb.MongoDbFactory"/>
</bean>
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory"/>
</bean>
</beans>

View File

@@ -0,0 +1,365 @@
/*
* Copyright 2016 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.dsl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.SimpleMongoDbFactory;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.handler.ReplyRequiredException;
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 org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.mongodb.MongoClient;
/**
* @author Xavier Padró
* @since 5.0
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class MongoDbTests extends MongoDbAvailableTests {
private static final String COLLECTION_NAME = "data";
@Autowired
private PollableChannel getResultChannel;
@Autowired
@Qualifier("gatewaySingleQueryFlow.input")
private MessageChannel gatewaySingleQueryFlow;
@Autowired
@Qualifier("gatewaySingleQueryWithTemplateFlow.input")
private MessageChannel gatewaySingleQueryWithTemplateFlow;
@Autowired
@Qualifier("gatewaySingleQueryExpressionFlow.input")
private MessageChannel gatewaySingleQueryExpressionFlow;
@Autowired
@Qualifier("gatewayQueryExpressionFlow.input")
private MessageChannel gatewayQueryExpressionFlow;
@Autowired
@Qualifier("gatewayQueryExpressionLimitFlow.input")
private MessageChannel gatewayQueryExpressionLimitFlow;
@Autowired
@Qualifier("gatewayQueryFunctionFlow.input")
private MessageChannel gatewayQueryFunctionFlow;
@Autowired
@Qualifier("gatewayCollectionNameFunctionFlow.input")
private MessageChannel gatewayCollectionNameFunctionFlow;
@Autowired
private MongoOperations mongoTemplate;
@Before
public void setUp() throws Exception {
createPersons();
}
@After
public void cleanUp() {
mongoTemplate.dropCollection(COLLECTION_NAME);
}
@Test
@MongoDbAvailable
public void testGatewayWithSingleQuery() {
gatewaySingleQueryFlow.send(MessageBuilder
.withPayload("Xavi")
.setHeader("collection", "data")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
Person retrievedPerson = (Person) result.getPayload();
assertEquals("Xavi", retrievedPerson.getName());
}
@Test
@MongoDbAvailable
public void testGatewayWithSingleQueryWithTemplate() {
gatewaySingleQueryWithTemplateFlow.send(MessageBuilder.withPayload("Xavi").build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
Person retrievedPerson = (Person) result.getPayload();
assertEquals("Xavi", retrievedPerson.getName());
}
@Test
@MongoDbAvailable
public void testGatewayWithSingleQueryExpression() {
gatewaySingleQueryExpressionFlow.send(MessageBuilder
.withPayload("")
.setHeader("query", "{'name' : 'Artem'}")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
Person retrievedPerson = (Person) result.getPayload();
assertEquals("Artem", retrievedPerson.getName());
}
@Test(expected = ReplyRequiredException.class)
@MongoDbAvailable
public void testGatewayWithSingleQueryExpressionNoPersonFound() {
gatewaySingleQueryExpressionFlow.send(MessageBuilder
.withPayload("")
.setHeader("query", "{'name' : 'NonExisting'}")
.build());
this.getResultChannel.receive(10_000);
}
@Test
@MongoDbAvailable
public void testGatewayWithQueryExpression() {
gatewayQueryExpressionFlow.send(MessageBuilder
.withPayload("")
.setHeader("query", "{}")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
List<Person> retrievedPersons = getPersons(result);
assertEquals(4, retrievedPersons.size());
}
@Test
@MongoDbAvailable
public void testGatewayWithQueryExpressionAndLimit() {
gatewayQueryExpressionLimitFlow.send(MessageBuilder
.withPayload("")
.setHeader("query", "{}")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
List<Person> retrievedPersons = getPersons(result);
assertEquals(2, retrievedPersons.size());
}
@Test
@MongoDbAvailable
public void testGatewayWithQueryFunction() {
gatewayQueryFunctionFlow.send(MessageBuilder
.withPayload("Gary")
.setHeader("collection", "data")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
Person person = (Person) result.getPayload();
assertEquals("Gary", person.getName());
}
@Test
@MongoDbAvailable
public void testGatewayWithCollectionNameFunction() {
gatewayCollectionNameFunctionFlow.send(MessageBuilder
.withPayload("data")
.setHeader("query", "{'name' : 'Gary'}")
.build());
Message<?> result = this.getResultChannel.receive(10_000);
assertNotNull(result);
Person person = (Person) result.getPayload();
assertEquals("Gary", person.getName());
}
@SuppressWarnings("unchecked")
private List<Person> getPersons(Message<?> message) {
return (List<Person>) message.getPayload();
}
private void createPersons() {
BulkOperations bulkOperations = this.mongoTemplate.bulkOps(BulkOperations.BulkMode.ORDERED, COLLECTION_NAME);
bulkOperations.insert(Arrays.asList(
this.createPerson("Artem"),
this.createPerson("Gary"),
this.createPerson("Oleg"),
this.createPerson("Xavi")));
bulkOperations.execute();
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public IntegrationFlow gatewaySingleQueryFlow() {
return f -> f
.handle(queryOutboundGateway("{name: 'Xavi'}", true))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewaySingleQueryWithTemplateFlow() {
return f -> f
.handle(queryOutboundGatewayWithTemplate("{name: 'Xavi'}", true))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewaySingleQueryExpressionFlow() {
return f -> f
.handle(queryExpressionOutboundGateway(true))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewayQueryExpressionFlow() {
return f -> f
.handle(queryExpressionOutboundGateway(false))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewayQueryExpressionLimitFlow() {
return f -> f
.handle(queryExpressionOutboundGateway(false, 2))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewayQueryFunctionFlow() {
return f -> f
.handle(queryFunctionOutboundGateway(true))
.channel(getResultChannel());
}
@Bean
public IntegrationFlow gatewayCollectionNameFunctionFlow() {
return f -> f
.handle(collectionNameFunctionOutboundGateway(true))
.channel(getResultChannel());
}
@Bean
public MessageChannel getResultChannel() {
return MessageChannels.queue().get();
}
@Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(new MongoClient(), "test");
}
@Bean
public MongoConverter mongoConverter() {
return new TestMongoConverter(mongoDbFactory(), new MongoMappingContext());
}
@Bean
public MongoOperations mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
private MongoDbOutboundGatewaySpec queryOutboundGateway(String query, boolean expectSingleResult) {
return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter())
.query(query)
.collectionNameExpression("headers.collection")
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
private MongoDbOutboundGatewaySpec queryOutboundGatewayWithTemplate(String query, boolean expectSingleResult) {
return MongoDb.outboundGateway(mongoTemplate())
.query(query)
.collectionName(COLLECTION_NAME)
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
private MongoDbOutboundGatewaySpec queryExpressionOutboundGateway(boolean expectSingleResult) {
return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter())
.queryExpression("headers.query")
.collectionName(COLLECTION_NAME)
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
private MongoDbOutboundGatewaySpec queryExpressionOutboundGateway(boolean expectSingleResult, int maxResults) {
return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter())
.queryExpression("new BasicQuery('{''address.state'' : ''PA''}').limit(" + maxResults + ")")
.collectionName(COLLECTION_NAME)
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
private MongoDbOutboundGatewaySpec queryFunctionOutboundGateway(boolean expectSingleResult) {
return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter())
.queryFunction(msg ->
Query.query(Criteria.where("name")
.is(msg.getPayload())))
.collectionNameExpression("headers.collection")
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
private MongoDbOutboundGatewaySpec collectionNameFunctionOutboundGateway(boolean expectSingleResult) {
return MongoDb.outboundGateway(mongoDbFactory(), mongoConverter())
.queryExpression("headers.query")
.<String>collectionNameFunction(Message::getPayload)
.expectSingleResult(expectSingleResult)
.entityClass(Person.class);
}
}
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<mongo:db-factory id="mongoDbFactory" dbname="test" />
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<bean id="mongoConverter"
class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.TestMongoConverter">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg>
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
</constructor-arg>
</bean>
</beans>

View File

@@ -0,0 +1,318 @@
/*
* Copyright 2016 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import org.bson.Document;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
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.convert.MongoConverter;
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.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.expression.FunctionExpression;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
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;
/**
* @author Xavier Padró
* @since 5.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class MongoDbOutboundGatewayTests extends MongoDbAvailableTests {
private static final String COLLECTION_NAME = "data";
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
@Autowired
private BeanFactory beanFactory;
@Autowired
private MongoOperations mongoTemplate;
@Autowired
private MongoConverter mongoConverter;
@Autowired
private MongoDbFactory mongoDbFactory;
@Before
public void setUp() {
BulkOperations bulkOperations = this.mongoTemplate.bulkOps(BulkOperations.BulkMode.ORDERED, COLLECTION_NAME);
bulkOperations.insert(Arrays.asList(
this.createPerson("Artem"),
this.createPerson("Gary"),
this.createPerson("Oleg"),
this.createPerson("Xavi")));
bulkOperations.execute();
}
@After
public void cleanUp() {
mongoTemplate.dropCollection(COLLECTION_NAME);
}
@SuppressWarnings("ConstantConditions")
@Test
@MongoDbAvailable
public void testNoFactorySpecified() {
MongoDbFactory nullFactory = null;
try {
new MongoDbOutboundGateway(nullFactory);
Assert.fail("Expected the test case to throw an IllegalArgumentException");
}
catch (IllegalArgumentException e) {
assertEquals("MongoDbFactory translator must not be null!", e.getMessage());
}
}
@SuppressWarnings("ConstantConditions")
@Test
@MongoDbAvailable
public void testNoTemplateSpecified() {
MongoOperations mongoTemplate = null;
try {
new MongoDbOutboundGateway(mongoTemplate);
Assert.fail("Expected the test case to throw an IllegalArgumentException");
}
catch (IllegalArgumentException e) {
assertEquals("mongoTemplate must not be null.", e.getMessage());
}
}
@Test
@MongoDbAvailable
public void testNoQuerySpecified() {
Message<String> message = MessageBuilder.withPayload("test").build();
MongoDbOutboundGateway gateway = createGateway();
try {
gateway.afterPropertiesSet();
gateway.handleRequestMessage(message);
Assert.fail("Expected the test case to throw an IllegalArgumentException");
}
catch (IllegalStateException e) {
assertEquals("no query specified", e.getMessage());
}
}
@Test
@MongoDbAvailable
public void testListOfResultsWithQueryExpressionAndLimit() {
Message<String> message = MessageBuilder.withPayload("").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setQueryExpression(
PARSER.parseExpression("new BasicQuery('{''address.state'' : ''PA''}').limit(2)"));
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
List<Person> persons = getPersonsFromResult(result);
assertEquals(2, persons.size());
}
@Test
@MongoDbAvailable
public void testListOfResultsWithQueryFunction() {
Message<String> message = MessageBuilder.withPayload("Xavi").build();
MongoDbOutboundGateway gateway = createGateway();
Function<Message<String>, Query> queryFunction =
msg -> new BasicQuery("{'name' : '" + msg.getPayload() + "'}");
FunctionExpression<Message<String>> functionExpression = new FunctionExpression<>(queryFunction);
gateway.setQueryExpression(functionExpression);
gateway.setExpectSingleResult(true);
gateway.setEntityClass(Person.class);
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
Person person = (Person) result;
assertEquals("Xavi", person.getName());
}
@Test
@MongoDbAvailable
public void testListOfResultsWithQueryExpressionNotInitialized() {
MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(mongoDbFactory);
gateway.setBeanFactory(beanFactory);
gateway.setMongoConverter(mongoConverter);
try {
gateway.afterPropertiesSet();
Assert.fail("Expected the test case to throw an IllegalStateException");
}
catch (IllegalStateException e) {
assertEquals("no query specified", e.getMessage());
}
}
@Test
@MongoDbAvailable
public void testListOfResultsWithQueryExpression() throws Exception {
Message<String> message = MessageBuilder.withPayload("{}").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setEntityClass(Person.class);
gateway.setQueryExpression(PARSER.parseExpression("payload"));
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
List<Person> persons = getPersonsFromResult(result);
assertEquals(4, persons.size());
}
@Test
@MongoDbAvailable
public void testListOfResultsWithQueryExpressionReturningOneResult() throws Exception {
Message<String> message = MessageBuilder.withPayload("{name : 'Xavi'}").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setEntityClass(Person.class);
gateway.setQueryExpression(PARSER.parseExpression("payload"));
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
List<Person> persons = getPersonsFromResult(result);
assertEquals(1, persons.size());
assertEquals("Xavi", persons.get(0).getName());
}
@Test
@MongoDbAvailable
public void testSingleResultWithQueryExpressionAsString() throws Exception {
Message<String> message = MessageBuilder.withPayload("{name : 'Artem'}").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setQueryExpression(PARSER.parseExpression("payload"));
gateway.setExpectSingleResult(true);
gateway.setEntityClass(Person.class);
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
Person person = (Person) result;
assertEquals("Artem", person.getName());
}
@Test
@MongoDbAvailable
public void testSingleResultWithQueryExpressionAsQuery() throws Exception {
Message<String> message = MessageBuilder.withPayload("").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setQueryExpression(PARSER.parseExpression("new BasicQuery('{''name'' : ''Gary''}')"));
gateway.setExpectSingleResult(true);
gateway.setEntityClass(Person.class);
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
Person person = (Person) result;
assertEquals("Gary", person.getName());
}
@Test
@MongoDbAvailable
public void testSingleResultWithQueryExpressionAndNoEntityClass() {
Message<String> message = MessageBuilder.withPayload("").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setQueryExpression(new LiteralExpression("{name : 'Xavi'}"));
gateway.setExpectSingleResult(true);
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
Document person = (Document) result;
assertEquals("Xavi", person.get("name"));
}
@Test
@MongoDbAvailable
public void testWithNullCollectionNameExpression() throws Exception {
MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(mongoDbFactory);
gateway.setBeanFactory(beanFactory);
gateway.setQueryExpression(new LiteralExpression("{name : 'Xavi'}"));
gateway.setExpectSingleResult(true);
try {
gateway.afterPropertiesSet();
Assert.fail("Expected the test case to throw an IllegalArgumentException");
}
catch (IllegalStateException e) {
assertEquals("no collection name specified", e.getMessage());
}
}
@Test
@MongoDbAvailable
public void testWithCollectionNameExpressionSpecified() throws Exception {
Message<String> message = MessageBuilder.withPayload("").build();
MongoDbOutboundGateway gateway = createGateway();
gateway.setQueryExpression(new LiteralExpression("{name : 'Xavi'}"));
gateway.setExpectSingleResult(true);
gateway.setCollectionNameExpression(new LiteralExpression("anotherCollection"));
gateway.afterPropertiesSet();
Object result = gateway.handleRequestMessage(message);
assertNull(result);
LiteralExpression collectionNameExpression =
(LiteralExpression) TestUtils.getPropertyValue(gateway, "collectionNameExpression");
assertNotNull(collectionNameExpression);
assertEquals("anotherCollection", collectionNameExpression.getValue());
}
@SuppressWarnings("unchecked")
private List<Person> getPersonsFromResult(Object result) {
return (List<Person>) result;
}
private MongoDbOutboundGateway createGateway() {
MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(mongoDbFactory);
gateway.setBeanFactory(beanFactory);
gateway.setCollectionNameExpression(new LiteralExpression("data"));
return gateway;
}
}

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/mongodb http://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb.xsd">
<int:channel id="in"/>
<int:channel id="out">
<int:queue capacity="5"/>
</int:channel>
<int-mongodb:outbound-gateway id="gatewaySingleQuery"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query="{name: 'Xavi'}"
collection-name="data"
expect-single-result="true"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests$Person"/>
<int-mongodb:outbound-gateway id="gatewayWithTemplate"
mongo-template="mongoDbTemplate"
query="{name: 'Xavi'}"
collection-name="data"
expect-single-result="true"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests$Person"/>
<int-mongodb:outbound-gateway id="gatewaySingleQueryExpression"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query-expression="headers.query"
collection-name-expression="headers.collectionName"
expect-single-result="true"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests$Person"/>
<int-mongodb:outbound-gateway id="gatewayQueryExpression"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query-expression="headers.query"
collection-name-expression="headers.collectionName"
expect-single-result="false"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests$Person"/>
<int-mongodb:outbound-gateway id="gatewayQueryExpressionLimit"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query-expression="new BasicQuery('{''address.state'' : ''PA''}').limit(2)"
collection-name-expression="headers.collectionName"
expect-single-result="false"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests$Person"/>
<mongo:db-factory id="mongoDbFactory" dbname="test" />
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
</bean>
<bean id="mongoConverter"
class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.TestMongoConverter">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg>
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
</constructor-arg>
</bean>
</beans>

View File

@@ -0,0 +1,167 @@
/*
* Copyright 2016 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 static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
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 org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Xavier Padró
* @since 5.0
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class MongoDbOutboundGatewayXmlTests extends MongoDbAvailableTests {
private static final String COLLECTION_NAME = "data";
@Autowired
private ApplicationContext context;
@Before
public void setUp() throws Exception {
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
mongoTemplate.save(this.createPerson("Artem"), COLLECTION_NAME);
mongoTemplate.save(this.createPerson("Gary"), COLLECTION_NAME);
mongoTemplate.save(this.createPerson("Oleg"), COLLECTION_NAME);
mongoTemplate.save(this.createPerson("Xavi"), COLLECTION_NAME);
}
@After
public void cleanUp() throws Exception {
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
mongoTemplate.dropCollection(COLLECTION_NAME);
}
@Test
@MongoDbAvailable
public void testSingleQuery() throws Exception {
EventDrivenConsumer consumer = context.getBean("gatewaySingleQuery", EventDrivenConsumer.class);
PollableChannel outChannel = context.getBean("out", PollableChannel.class);
Message<String> message = MessageBuilder.withPayload("").build();
consumer.getHandler().handleMessage(message);
Message<?> result = outChannel.receive(10000);
Person person = getPerson(result);
assertEquals("Xavi", person.getName());
}
@Test
@MongoDbAvailable
public void testSingleQueryWithTemplate() throws Exception {
EventDrivenConsumer consumer = context.getBean("gatewayWithTemplate", EventDrivenConsumer.class);
PollableChannel outChannel = context.getBean("out", PollableChannel.class);
Message<String> message = MessageBuilder.withPayload("").build();
consumer.getHandler().handleMessage(message);
Message<?> result = outChannel.receive(10000);
Person person = getPerson(result);
assertEquals("Xavi", person.getName());
}
@Test
@MongoDbAvailable
public void testSingleQueryExpression() throws Exception {
EventDrivenConsumer consumer = context.getBean("gatewaySingleQueryExpression", EventDrivenConsumer.class);
PollableChannel outChannel = context.getBean("out", PollableChannel.class);
Message<String> message = MessageBuilder
.withPayload("")
.setHeader("query", "{'name' : 'Gary'}")
.setHeader("collectionName", "data")
.build();
consumer.getHandler().handleMessage(message);
Message<?> result = outChannel.receive(10000);
Person person = getPerson(result);
assertEquals("Gary", person.getName());
}
@Test
@MongoDbAvailable
public void testQueryExpression() throws Exception {
EventDrivenConsumer consumer = context.getBean("gatewayQueryExpression", EventDrivenConsumer.class);
PollableChannel outChannel = context.getBean("out", PollableChannel.class);
Message<String> message = MessageBuilder
.withPayload("")
.setHeader("query", "{}")
.setHeader("collectionName", "data")
.build();
consumer.getHandler().handleMessage(message);
Message<?> result = outChannel.receive(10000);
List<Person> persons = getPersons(result);
assertEquals(4, persons.size());
}
@Test
@MongoDbAvailable
public void testQueryExpressionWithLimit() throws Exception {
EventDrivenConsumer consumer = context.getBean("gatewayQueryExpressionLimit", EventDrivenConsumer.class);
PollableChannel outChannel = context.getBean("out", PollableChannel.class);
Message<String> message = MessageBuilder
.withPayload("")
.setHeader("collectionName", "data")
.build();
consumer.getHandler().handleMessage(message);
Message<?> result = outChannel.receive(10000);
List<Person> persons = getPersons(result);
assertEquals(2, persons.size());
}
private Person getPerson(Message<?> message) {
return (Person) message.getPayload();
}
@SuppressWarnings("unchecked")
private List<Person> getPersons(Message<?> message) {
return (List<Person>) message.getPayload();
}
}

View File

@@ -22,23 +22,23 @@ To connect to MongoDB you can use an implementation of the `MongoDbFactory` inte
----
public interface MongoDbFactory {
/**
* Creates a default {@link DB} instance.
*
* @return the DB instance
* @throws DataAccessException
*/
DB getDb() throws DataAccessException;
/**
* Creates a default {@link DB} instance.
*
* @return the DB instance
* @throws DataAccessException
*/
DB getDb() throws DataAccessException;
/**
* Creates a {@link DB} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
*
* @return the DB instance
* @throws DataAccessException
*/
DB getDb(String dbName) throws DataAccessException;
/**
* Creates a {@link DB} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
*
* @return the DB instance
* @throws DataAccessException
*/
DB getDb(String dbName) throws DataAccessException;
}
----
@@ -290,3 +290,120 @@ and other attributes that are common across all other inbound adapters (e.g., 'c
The example above is relatively simple and static since it has a literal value for the `collection-name`.
Sometimes you may need to change this value at runtime based on some condition.
To do that, simply use `collection-name-expression` where the provided expression can be any valid SpEL expression.
[[mongodb-outbound-gateway]]
=== MongoDB Outbound Gateway
Starting with _version 5.0_, the MongoDb Outbound Gateway is provided and it allows you to query a database by sending a Message to its request channel.
The gateway will then send the response to the reply channel.
The Message payload and headers can be used to specify the query, as well as collection name.
[source,xml]
----
<int-mongodb:outbound-gateway id="gatewayQuery"
mongodb-factory="mongoDbFactory"
mongo-converter="mongoConverter"
query="{firstName: 'Bob'}"
collection-name="foo"
request-channel="in"
reply-channel="out"
entity-class="org.springframework.integration.mongodb.test.entity$Person"/>
----
* `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` to assist 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 have both mongo-template and mongodb-factory set)
* `entity-class` - the fully qualified name of the entity class to be passed to `find(..)` or `findOne(..)` method in MongoTemplate.
If this attribute is not provided the default value is `org.bson.Document`;
* `query` or `query-expression` - specifies the MongoDb query.
Please refer to http://www.mongodb.org/display/DOCS/Querying[MongoDB documentation] for more query samples.
==== Configuring with Java Configuration
The following Spring Boot application provides an example of configuring the outbound gateway using Java configuration:
[source, java]
----
@SpringBootApplication
public class MongoDbJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MongoDbJavaApplication.class)
.web(false)
.run(args);
}
@Autowired
private MongoDbFactory mongoDbFactory;
@Bean
public MessageChannel requestChannel() {
return new DirectChannel();
}
@Bean
public MessageChannel replyChannel() {
return new QueueChannel(5);
}
@Bean
@ServiceActivator(inputChannel = "requestChannel")
public MessageHandler mongoDbOutboundGateway() {
MongoDbOutboundGateway gateway = new MongoDbOutboundGateway(this.mongoDbFactory);
gateway.setCollectionNameExpressionString("'foo'");
gateway.setQueryExpressionString("'{''name'' : ''Bob''}'");
gateway.setExpectSingleResult(true);
gateway.setEntityClass(Person.class);
gateway.setOutputChannelName("replyChannel");
return gateway;
}
@Bean
@ServiceActivator(inputChannel = "replyChannel")
public MessageHandler handler() {
return message -> System.out.println(message.getPayload());
}
}
----
==== Configuring with the Java DSL
The following Spring Boot application provides an example of configuring the Outbound Gateway using the Java DSL:
[source, java]
----
@SpringBootApplication
public class MongoDbJavaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MongoDbJavaApplication.class)
.web(false)
.run(args);
}
@Autowired
private MongoDbFactory;
@Autowired
private MongoConverter;
@Bean
public IntegrationFlow gatewaySingleQueryFlow() {
return f -> f
.handle(queryOutboundGateway())
.channel(c -> c.queue("retrieveResults"));
}
private MongoDbOutboundGatewaySpec queryOutboundGateway() {
return MongoDb.outboundGateway(this.mongoDbFactory, this.mongoConverter)
.query("{name : 'Bob'}")
.collectionNameFunction(m -> m.getHeaders().get("collection"))
.expectSingleResult(true)
.entityClass(Person.class);
}
}
----

View File

@@ -9,6 +9,10 @@ development process.
[[x5.0-new-components]]
=== New Components
==== MongoDB Outbound Gateway
The new `MongoDbOutboundGateway` allows you to make queries to the database on demand by sending a message to its request channel.
See <<mongodb-outbound-gateway>> for more information.
[[x5.0-general]]
=== General Changes