diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java index 8e57f0964..e8d0545e9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoDbFactoryParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 by the original author(s). + * Copyright 2011 by the original author(s). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.config; import static org.springframework.data.mongodb.config.BeanNames.*; +import static org.springframework.data.mongodb.config.ParsingUtils.*; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; @@ -33,6 +34,7 @@ import org.springframework.util.StringUtils; import org.w3c.dom.Element; import com.mongodb.Mongo; +import com.mongodb.MongoURI; /** * {@link BeanDefinitionParser} to parse {@code db-factory} elements into {@link BeanDefinition}s. @@ -54,31 +56,37 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { @Override protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { - + + String uri = element.getAttribute("uri"); String mongoRef = element.getAttribute("mongo-ref"); - if (!StringUtils.hasText(mongoRef)) { - mongoRef = registerMongoBeanDefinition(element, parserContext); - } - - // Database name String dbname = element.getAttribute("dbname"); - if (!StringUtils.hasText(dbname)) { - dbname = "db"; - } + BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element, parserContext); + // Common setup BeanDefinitionBuilder dbFactoryBuilder = BeanDefinitionBuilder.genericBeanDefinition(SimpleMongoDbFactory.class); + ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern"); + + if (StringUtils.hasText(uri)) { + if(StringUtils.hasText(mongoRef) || StringUtils.hasText(dbname) || userCredentials != null) { + parserContext.getReaderContext().error("Configure either Mongo URI or details individually!", parserContext.extractSource(element)); + } + + dbFactoryBuilder.addConstructorArgValue(getMongoUri(uri)); + return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element); + } + + // Defaulting + mongoRef = StringUtils.hasText(mongoRef) ? mongoRef : registerMongoBeanDefinition(element, parserContext); + dbname = StringUtils.hasText(dbname) ? dbname : "db"; + dbFactoryBuilder.addConstructorArgValue(new RuntimeBeanReference(mongoRef)); dbFactoryBuilder.addConstructorArgValue(dbname); - BeanDefinition userCredentials = getUserCredentialsBeanDefinition(element); if (userCredentials != null) { dbFactoryBuilder.addConstructorArgValue(userCredentials); } - ParsingUtils.setPropertyValue(element, dbFactoryBuilder, "write-concern", "writeConcern"); - - return dbFactoryBuilder.getBeanDefinition(); - + return getSourceBeanDefinition(dbFactoryBuilder, parserContext, element); } /** @@ -105,7 +113,7 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { * @param element * @return the {@link BeanDefinition} or {@literal null} if neither username nor password given. */ - private BeanDefinition getUserCredentialsBeanDefinition(Element element) { + private BeanDefinition getUserCredentialsBeanDefinition(Element element, ParserContext context) { String username = element.getAttribute("username"); String password = element.getAttribute("password"); @@ -118,6 +126,20 @@ public class MongoDbFactoryParser extends AbstractBeanDefinitionParser { userCredentialsBuilder.addConstructorArgValue(StringUtils.hasText(username) ? username : null); userCredentialsBuilder.addConstructorArgValue(StringUtils.hasText(password) ? password : null); - return userCredentialsBuilder.getBeanDefinition(); + return getSourceBeanDefinition(userCredentialsBuilder, context, element); + } + + /** + * Creates a {@link BeanDefinition} for a {@link MongoURI}. + * + * @param uri + * @return + */ + private BeanDefinition getMongoUri(String uri) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MongoURI.class); + builder.addConstructorArgValue(uri); + + return builder.getBeanDefinition(); } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java index 2f646d74c..1f3ef877b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/ParsingUtils.java @@ -16,6 +16,8 @@ package org.springframework.data.mongodb.config; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.ParserContext; @@ -110,4 +112,19 @@ abstract class ParsingUtils { builder.addPropertyValue(attrName, attr); } } + + /** + * Returns the {@link BeanDefinition} built by the given {@link BeanDefinitionBuilder} enriched with source + * information derived from the given {@link Element}. + * + * @param builder must not be {@literal null}. + * @param context must not be {@literal null}. + * @param element must not be {@literal null}. + * @return + */ + static AbstractBeanDefinition getSourceBeanDefinition(BeanDefinitionBuilder builder, ParserContext context, Element element) { + AbstractBeanDefinition definition = builder.getBeanDefinition(); + definition.setSource(context.extractSource(element)); + return definition; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java index 978a787b2..4fc4856c1 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/SimpleMongoDbFactory.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core; +import java.net.UnknownHostException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.DisposableBean; @@ -25,6 +27,8 @@ import org.springframework.util.Assert; import com.mongodb.DB; import com.mongodb.Mongo; +import com.mongodb.MongoException; +import com.mongodb.MongoURI; import com.mongodb.WriteConcern; /** @@ -69,6 +73,19 @@ public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory { this.username = userCredentials.getUsername(); this.password = userCredentials.getPassword(); } + + /** + * Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoURI}. + * + * @param uri must not be {@literal null}. + * @throws MongoException + * @throws UnknownHostException + * @see MongoURI + */ + public SimpleMongoDbFactory(MongoURI uri) throws MongoException, UnknownHostException { + + this(new Mongo(uri), uri.getDatabase(), new UserCredentials(uri.getUsername(), String.valueOf(uri.getPassword()))); + } /** * Configures the {@link WriteConcern} to be used on the {@link DB} instance being created. diff --git a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd index 51b0b84a3..058066e01 100644 --- a/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd +++ b/spring-data-mongodb/src/main/resources/org/springframework/data/mongodb/config/spring-mongo-1.0.xsd @@ -87,6 +87,12 @@ The password to use when connecting to a MongoDB server. ]]> + + + + + diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java index c5f629efe..52848a247 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoDbFactoryParserIntegrationTests.java @@ -23,10 +23,14 @@ import java.util.List; import org.junit.Test; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import com.mongodb.Mongo; +import com.mongodb.MongoURI; /** * Integration tests for {@link MongoDbFactoryParser}. @@ -60,4 +64,27 @@ public class MongoDbFactoryParserIntegrationTests { Mongo mongo = factory.getBean(Mongo.class); assertThat(mongo.getMongoOptions().maxAutoConnectRetryTime, is(27L)); } + + /** + * @see DATADOC-295 + */ + @Test + public void setsUpMongoDbFactoryUsingAMongoUri() { + + XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("namespace/mongo-uri.xml")); + BeanDefinition definition = factory.getBeanDefinition("mongoDbFactory"); + ConstructorArgumentValues constructorArguments = definition.getConstructorArgumentValues(); + + assertThat(constructorArguments.getArgumentCount(), is(1)); + ValueHolder argument = constructorArguments.getArgumentValue(0, MongoURI.class); + assertThat(argument, is(notNullValue())); + } + + /** + * @see DATADOC-295 + */ + @Test(expected = BeanDefinitionParsingException.class) + public void rejectsUriPlusDetailedConfiguration() { + new XmlBeanFactory(new ClassPathResource("namespace/mongo-uri-and-details.xml")); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java index 609f264b9..282f1076c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/SimpleMongoDbFactoryUnitTests.java @@ -16,12 +16,19 @@ package org.springframework.data.mongodb.core; import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import java.net.UnknownHostException; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mongodb.MongoDbFactory; +import org.springframework.test.util.ReflectionTestUtils; import com.mongodb.Mongo; +import com.mongodb.MongoURI; /** * Unit tests for {@link SimpleMongoDbFactory}. @@ -53,6 +60,22 @@ public class SimpleMongoDbFactoryUnitTests { new SimpleMongoDbFactory(mongo, "foo01231bar"); } + /** + * @see DATADOC-295 + * @throws UnknownHostException + */ + @Test + public void mongoUriConstructor() throws UnknownHostException { + + MongoURI mongoURI = new MongoURI("mongodb://myUsername:myPassword@localhost/myDatabase.myCollection"); + MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoURI); + + assertThat(ReflectionTestUtils.getField(mongoDbFactory, "username").toString(), is("myUsername")); + assertThat(ReflectionTestUtils.getField(mongoDbFactory, "password").toString(), is("myPassword")); + assertThat(ReflectionTestUtils.getField(mongoDbFactory, "databaseName").toString(), is("myDatabase")); + assertThat(ReflectionTestUtils.getField(mongoDbFactory, "databaseName").toString(), is("myDatabase")); + } + private void rejectsDatabaseName(String databaseName) { try { diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-details.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-details.xml new file mode 100644 index 000000000..7239228e3 --- /dev/null +++ b/spring-data-mongodb/src/test/resources/namespace/mongo-uri-and-details.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/spring-data-mongodb/src/test/resources/namespace/mongo-uri.xml b/spring-data-mongodb/src/test/resources/namespace/mongo-uri.xml new file mode 100644 index 000000000..51175ecba --- /dev/null +++ b/spring-data-mongodb/src/test/resources/namespace/mongo-uri.xml @@ -0,0 +1,10 @@ + + + + + +