diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 45ef4defb2..ff941faba6 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -299,6 +299,16 @@ jboss-transaction-spi true + + org.mongodb + mongodb-driver-async + true + + + org.mongodb + mongodb-driver-reactivestreams + true + org.springframework spring-jdbc diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfiguration.java new file mode 100644 index 0000000000..f5371feda2 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import com.mongodb.reactivestreams.client.MongoClient; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.mongo.MongoProperties; +import org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; +import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory; +import org.springframework.data.mongodb.core.convert.MongoConverter; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's reactive mongo + * support. + *

+ * Registers a {@link ReactiveMongoTemplate} bean if no other bean of the same type is + * configured. + *

+ * Honors the {@literal spring.data.mongodb.database} property if set, otherwise connects + * to the {@literal test} database. + * + * @author Mark Paluch + * @since 2.0.0 + */ +@Configuration +@ConditionalOnClass({ MongoClient.class, ReactiveMongoTemplate.class }) +@EnableConfigurationProperties(MongoProperties.class) +@AutoConfigureAfter(ReactiveMongoAutoConfiguration.class) +public class ReactiveMongoDataAutoConfiguration { + + private final MongoProperties properties; + + public ReactiveMongoDataAutoConfiguration(MongoProperties properties) { + this.properties = properties; + } + + @Bean + @ConditionalOnMissingBean(ReactiveMongoDatabaseFactory.class) + public SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory( + MongoClient mongo) throws Exception { + String database = this.properties.getMongoClientDatabase(); + return new SimpleReactiveMongoDatabaseFactory(mongo, database); + } + + @Bean + @ConditionalOnMissingBean + public ReactiveMongoTemplate reactiveMongoTemplate( + ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory, + MongoConverter converter) { + return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory, converter); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfiguration.java new file mode 100644 index 0000000000..5b97327c85 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import com.mongodb.reactivestreams.client.MongoClient; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; +import org.springframework.data.mongodb.repository.config.ReactiveMongoRepositoryConfigurationExtension; +import org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Mongo Reactive + * Repositories. + *

+ * Activates when there is no bean of type + * {@link org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactoryBean} + * configured in the context, the Spring Data Mongo {@link ReactiveMongoRepository} type + * is on the classpath, the ReactiveStreams Mongo client driver API is on the classpath, + * and there is no other configured {@link ReactiveMongoRepository}. + *

+ * Once in effect, the auto-configuration is the equivalent of enabling Mongo repositories + * using the {@link EnableReactiveMongoRepositories} annotation. + * + * @author Mark Paluch + * @since 2.0.0 + * @see EnableReactiveMongoRepositories + */ +@Configuration +@ConditionalOnClass({ MongoClient.class, ReactiveMongoRepository.class }) +@ConditionalOnMissingBean({ MongoRepositoryFactoryBean.class, + ReactiveMongoRepositoryConfigurationExtension.class }) +@ConditionalOnProperty(prefix = "spring.data.mongodb.reactive-repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@Import(ReactiveMongoRepositoriesAutoConfigureRegistrar.class) +@AutoConfigureAfter(ReactiveMongoDataAutoConfiguration.class) +public class ReactiveMongoRepositoriesAutoConfiguration { + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigureRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigureRegistrar.java new file mode 100644 index 0000000000..08a53b6338 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigureRegistrar.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import java.lang.annotation.Annotation; + +import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; +import org.springframework.data.mongodb.repository.config.ReactiveMongoRepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +/** + * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Mongo Reactive + * Repositories. + * + * @author Mark Paluch + * @since 2.0.0 + */ +class ReactiveMongoRepositoriesAutoConfigureRegistrar extends + AbstractRepositoryConfigurationSourceSupport { + + @Override + protected Class getAnnotation() { + return EnableReactiveMongoRepositories.class; + } + + @Override + protected Class getConfiguration() { + return EnableReactiveMongoRepositoriesConfiguration.class; + } + + @Override + protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { + return new ReactiveMongoRepositoryConfigurationExtension(); + } + + @EnableReactiveMongoRepositories + private static class EnableReactiveMongoRepositoriesConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor.java new file mode 100644 index 0000000000..318e194a0c --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import com.mongodb.reactivestreams.client.MongoClient; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.data.mongodb.core.ReactiveMongoClientFactoryBean; + +/** + * {@link BeanFactoryPostProcessor} to automatically set up the recommended + * {@link BeanDefinition#setDependsOn(String[]) dependsOn} configuration for Mongo clients + * when used embedded Mongo. + * + * @author Mark Paluch + * @since 2.0.0 + */ +@Order(Ordered.LOWEST_PRECEDENCE) +public class ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor extends + AbstractDependsOnBeanFactoryPostProcessor { + + public ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor( + String... dependsOn) { + super(MongoClient.class, ReactiveMongoClientFactoryBean.class, dependsOn); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java index 83fb675354..2774a3d3c5 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -38,6 +38,8 @@ import org.springframework.core.env.Environment; * @author Dave Syer * @author Oliver Gierke * @author Phillip Webb + * @author Mark Paluch + * @author Stephane Nicoll */ @Configuration @ConditionalOnClass(MongoClient.class) @@ -45,19 +47,16 @@ import org.springframework.core.env.Environment; @ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDbFactory") public class MongoAutoConfiguration { - private final MongoProperties properties; - private final MongoClientOptions options; - private final Environment environment; + private final MongoClientFactory factory; private MongoClient mongo; public MongoAutoConfiguration(MongoProperties properties, ObjectProvider options, Environment environment) { - this.properties = properties; this.options = options.getIfAvailable(); - this.environment = environment; + this.factory = new MongoClientFactory(properties, environment); } @PreDestroy @@ -70,7 +69,7 @@ public class MongoAutoConfiguration { @Bean @ConditionalOnMissingBean public MongoClient mongo() throws UnknownHostException { - this.mongo = this.properties.createMongoClient(this.options, this.environment); + this.mongo = this.factory.createMongoClient(this.options); return this.mongo; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java new file mode 100644 index 0000000000..4acd20772b --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactory.java @@ -0,0 +1,130 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.mongodb.MongoClient; +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoClientOptions.Builder; +import com.mongodb.MongoClientURI; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; + +import org.springframework.core.env.Environment; + +/** + * A factory for a blocking {@link MongoClient} that applies {@link MongoProperties}. + * + * @author Dave Syer + * @author Phillip Webb + * @author Josh Long + * @author Andy Wilkinson + * @author Eddú Meléndez + * @author Stephane Nicoll + * @author Nasko Vasilev + * @author Mark Paluch + * @since 2.0.0 + */ +public class MongoClientFactory { + + private final MongoProperties properties; + private final Environment environment; + + public MongoClientFactory(MongoProperties properties, Environment environment) { + this.properties = properties; + this.environment = environment; + } + + /** + * Creates a {@link MongoClient} using the given {@code options}. If the configured + * port is zero, the value of the {@code local.mongo.port} property is used to + * configure the client. + * @param options the options + * @return the Mongo client + * @throws UnknownHostException if the configured host is unknown + */ + public MongoClient createMongoClient(MongoClientOptions options) + throws UnknownHostException { + + if (hasCustomAddress() || hasCustomCredentials()) { + if (this.properties.getUri() != null) { + throw new IllegalStateException("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + } + if (options == null) { + options = MongoClientOptions.builder().build(); + } + List credentials = new ArrayList(); + if (hasCustomCredentials()) { + String database = this.properties.getAuthenticationDatabase() == null ? this.properties + .getMongoClientDatabase() : this.properties + .getAuthenticationDatabase(); + credentials.add(MongoCredential.createCredential( + this.properties.getUsername(), database, + this.properties.getPassword())); + } + String host = this.properties.getHost() == null ? "localhost" + : this.properties.getHost(); + int port = determinePort(); + return new MongoClient( + Collections.singletonList(new ServerAddress(host, port)), + credentials, options); + } + // The options and credentials are in the URI + return new MongoClient(new MongoClientURI(this.properties.determineUri(), + builder(options))); + } + + private boolean hasCustomAddress() { + return this.properties.getHost() != null || this.properties.getPort() != null; + } + + private boolean hasCustomCredentials() { + return this.properties.getUsername() != null + && this.properties.getPassword() != null; + } + + private int determinePort() { + if (this.properties.getPort() == null) { + return MongoProperties.DEFAULT_PORT; + } + if (this.properties.getPort() == 0) { + if (this.environment != null) { + String localPort = this.environment.getProperty("local.mongo.port"); + if (localPort != null) { + return Integer.valueOf(localPort); + } + } + throw new IllegalStateException( + "spring.data.mongodb.port=0 and no local mongo port configuration " + + "is available"); + } + return this.properties.getPort(); + } + + private Builder builder(MongoClientOptions options) { + if (options != null) { + return MongoClientOptions.builder(options); + } + return MongoClientOptions.builder(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java index fa69691ae6..b47cabe29c 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -16,20 +16,9 @@ package org.springframework.boot.autoconfigure.mongo; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoClientOptions.Builder; import com.mongodb.MongoClientURI; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.core.env.Environment; /** * Configuration properties for Mongo. @@ -41,6 +30,7 @@ import org.springframework.core.env.Environment; * @author Eddú Meléndez * @author Stephane Nicoll * @author Nasko Vasilev + * @author Mark Paluch */ @ConfigurationProperties(prefix = "spring.data.mongodb") public class MongoProperties { @@ -145,15 +135,6 @@ public class MongoProperties { this.fieldNamingStrategy = fieldNamingStrategy; } - public void clearPassword() { - if (this.password == null) { - return; - } - for (int i = 0; i < this.password.length; i++) { - this.password[i] = 0; - } - } - public String getUri() { return this.uri; } @@ -189,79 +170,4 @@ public class MongoProperties { return new MongoClientURI(determineUri()).getDatabase(); } - /** - * Creates a {@link MongoClient} using the given {@code options} and - * {@code environment}. If the configured port is zero, the value of the - * {@code local.mongo.port} property retrieved from the {@code environment} is used to - * configure the client. - * @param options the options - * @param environment the environment - * @return the Mongo client - * @throws UnknownHostException if the configured host is unknown - */ - public MongoClient createMongoClient(MongoClientOptions options, - Environment environment) throws UnknownHostException { - try { - if (hasCustomAddress() || hasCustomCredentials()) { - if (this.uri != null) { - throw new IllegalStateException("Invalid mongo configuration, " - + "either uri or host/port/credentials must be specified"); - } - if (options == null) { - options = MongoClientOptions.builder().build(); - } - List credentials = new ArrayList(); - if (hasCustomCredentials()) { - String database = this.authenticationDatabase == null - ? getMongoClientDatabase() : this.authenticationDatabase; - credentials.add(MongoCredential.createCredential(this.username, - database, this.password)); - } - String host = this.host == null ? "localhost" : this.host; - int port = determinePort(environment); - return new MongoClient( - Collections.singletonList(new ServerAddress(host, port)), - credentials, options); - } - // The options and credentials are in the URI - return new MongoClient(new MongoClientURI(determineUri(), builder(options))); - } - finally { - clearPassword(); - } - } - - private boolean hasCustomAddress() { - return this.host != null || this.port != null; - } - - private boolean hasCustomCredentials() { - return this.username != null && this.password != null; - } - - private int determinePort(Environment environment) { - if (this.port == null) { - return DEFAULT_PORT; - } - if (this.port == 0) { - if (environment != null) { - String localPort = environment.getProperty("local.mongo.port"); - if (localPort != null) { - return Integer.valueOf(localPort); - } - } - throw new IllegalStateException( - "spring.data.mongodb.port=0 and no local mongo port configuration " - + "is available"); - } - return this.port; - } - - private Builder builder(MongoClientOptions options) { - if (options != null) { - return MongoClientOptions.builder(options); - } - return MongoClientOptions.builder(); - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfiguration.java new file mode 100644 index 0000000000..cfcd64a82a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfiguration.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import javax.annotation.PreDestroy; + +import com.mongodb.async.client.MongoClientSettings; +import com.mongodb.reactivestreams.client.MongoClient; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Reactive Mongo. + * + * @author Mark Paluch + * @author Stephane Nicoll + * @since 2.0.0 + */ +@Configuration +@ConditionalOnClass(MongoClient.class) +@EnableConfigurationProperties(MongoProperties.class) +public class ReactiveMongoAutoConfiguration { + + private final MongoClientSettings settings; + + private final ReactiveMongoClientFactory factory; + + private MongoClient mongo; + + public ReactiveMongoAutoConfiguration(MongoProperties properties, + ObjectProvider settings, Environment environment) { + this.settings = settings.getIfAvailable(); + this.factory = new ReactiveMongoClientFactory(properties, environment); + } + + @PreDestroy + public void close() { + if (this.mongo != null) { + this.mongo.close(); + } + } + + @Bean + @ConditionalOnMissingBean + public MongoClient reactiveStreamsMongoClient() { + this.mongo = this.factory.createMongoClient(this.settings); + return this.mongo; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java new file mode 100644 index 0000000000..1a38ce47e4 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactory.java @@ -0,0 +1,165 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.mongodb.ConnectionString; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.async.client.MongoClientSettings; +import com.mongodb.async.client.MongoClientSettings.Builder; +import com.mongodb.connection.ClusterSettings; +import com.mongodb.connection.ConnectionPoolSettings; +import com.mongodb.connection.ServerSettings; +import com.mongodb.connection.SocketSettings; +import com.mongodb.connection.SslSettings; +import com.mongodb.reactivestreams.client.MongoClient; +import com.mongodb.reactivestreams.client.MongoClients; + +import org.springframework.core.env.Environment; + +/** + * A factory for a reactive {@link MongoClient} that applies {@link MongoProperties}. + * + * @author Mark Paluch + * @author Stephane Nicoll + * @since 2.0.0 + */ +public class ReactiveMongoClientFactory { + + private final MongoProperties properties; + + private final Environment environment; + + public ReactiveMongoClientFactory(MongoProperties properties, + Environment environment) { + this.properties = properties; + this.environment = environment; + } + + /** + * Creates a {@link MongoClient} using the given {@code options}. If the configured + * port is zero, the value of the {@code local.mongo.port} property is used to + * configure the client. + * @param settings the settings + * @return the Mongo client + */ + public MongoClient createMongoClient(MongoClientSettings settings) { + if (hasCustomAddress() || hasCustomCredentials()) { + if (this.properties.getUri() != null) { + throw new IllegalStateException("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + } + + Builder builder = builder(settings); + if (hasCustomCredentials()) { + List credentials = new ArrayList(); + String database = this.properties.getAuthenticationDatabase() == null ? this.properties + .getMongoClientDatabase() : this.properties + .getAuthenticationDatabase(); + credentials.add(MongoCredential.createCredential( + this.properties.getUsername(), database, + this.properties.getPassword())); + builder.credentialList(credentials); + } + String host = this.properties.getHost() == null ? "localhost" + : this.properties.getHost(); + int port = determinePort(); + ClusterSettings clusterSettings = ClusterSettings.builder() + .hosts(Collections.singletonList(new ServerAddress(host, port))) + .build(); + builder.clusterSettings(clusterSettings); + return MongoClients.create(builder.build()); + } + ConnectionString connectionString = new ConnectionString( + this.properties.determineUri()); + return MongoClients.create(createBuilder(settings, connectionString).build()); + } + + private Builder createBuilder(MongoClientSettings settings, + ConnectionString connectionString) { + Builder builder = builder(settings) + .clusterSettings( + ClusterSettings.builder().applyConnectionString(connectionString) + .build()) + .connectionPoolSettings( + ConnectionPoolSettings.builder() + .applyConnectionString(connectionString).build()) + .serverSettings( + ServerSettings.builder().applyConnectionString(connectionString) + .build()) + .credentialList(connectionString.getCredentialList()) + .sslSettings( + SslSettings.builder().applyConnectionString(connectionString) + .build()) + .socketSettings( + SocketSettings.builder().applyConnectionString(connectionString) + .build()); + if (connectionString.getReadPreference() != null) { + builder.readPreference(connectionString.getReadPreference()); + } + if (connectionString.getReadConcern() != null) { + builder.readConcern(connectionString.getReadConcern()); + } + if (connectionString.getWriteConcern() != null) { + builder.writeConcern(connectionString.getWriteConcern()); + } + if (connectionString.getApplicationName() != null) { + builder.applicationName(connectionString.getApplicationName()); + } + return builder; + } + + private boolean hasCustomAddress() { + return this.properties.getHost() != null || this.properties.getPort() != null; + } + + private boolean hasCustomCredentials() { + return this.properties.getUsername() != null + && this.properties.getPassword() != null; + } + + private int determinePort() { + if (this.properties.getPort() == null) { + return MongoProperties.DEFAULT_PORT; + } + if (this.properties.getPort() == 0) { + if (this.environment != null) { + String localPort = this.environment.getProperty("local.mongo.port"); + if (localPort != null) { + return Integer.valueOf(localPort); + } + } + throw new IllegalStateException( + "spring.data.mongodb.port=0 and no local mongo port configuration " + + "is available"); + } + return this.properties.getPort(); + } + + private Builder builder(MongoClientSettings settings) { + if (settings == null) { + return MongoClientSettings.builder(); + } + + return MongoClientSettings.builder(settings); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.java index d63391cdde..d576c1d93d 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfiguration.java @@ -53,6 +53,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.mongo.MongoClientDependsOnBeanFactoryPostProcessor; +import org.springframework.boot.autoconfigure.data.mongo.ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -64,6 +65,7 @@ import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.data.mongodb.core.MongoClientFactoryBean; +import org.springframework.data.mongodb.core.ReactiveMongoClientFactoryBean; import org.springframework.util.Assert; /** @@ -72,6 +74,7 @@ import org.springframework.util.Assert; * @author Henryk Konsek * @author Andy Wilkinson * @author Yogesh Lonkar + * @author Mark Paluch * @since 1.3.0 */ @Configuration @@ -226,6 +229,22 @@ public class EmbeddedMongoAutoConfiguration { } + /** + * Additional configuration to ensure that {@link MongoClient} beans depend on the + * {@code embeddedMongoServer} bean. + */ + @Configuration + @ConditionalOnClass({ com.mongodb.reactivestreams.client.MongoClient.class, + ReactiveMongoClientFactoryBean.class }) + protected static class EmbeddedReactiveMongoDependencyConfiguration extends + ReactiveStreamsMongoClientDependsOnBeanFactoryPostProcessor { + + public EmbeddedReactiveMongoDependencyConfiguration() { + super("embeddedMongoServer"); + } + + } + /** * A workaround for the lack of a {@code toString} implementation on * {@code GenericFeatureAwareVersion}. diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index e097896216..4d7ab3e894 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -112,6 +112,12 @@ "description": "Enable LDAP repositories.", "defaultValue": true }, + { + "name": "spring.data.mongodb.reactive-repositories.enabled", + "type": "java.lang.Boolean", + "description": "Enable Mongo reactive repositories.", + "defaultValue": true + }, { "name": "spring.data.mongodb.repositories.enabled", "type": "java.lang.Boolean", diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index e4c64257fb..415bc5c874 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -41,6 +41,8 @@ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoDataAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.mongo.ReactiveMongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ @@ -83,6 +85,7 @@ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoCo org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ +org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/mongo/ReactiveCityMongoDbRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/mongo/ReactiveCityMongoDbRepository.java new file mode 100644 index 0000000000..5ac5d2ec3b --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/mongo/ReactiveCityMongoDbRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.alt.mongo; + +import org.springframework.boot.autoconfigure.data.mongo.city.City; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; + +public interface ReactiveCityMongoDbRepository + extends ReactiveCrudRepository { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveAndBlockingMongoRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveAndBlockingMongoRepositoriesAutoConfigurationTests.java new file mode 100644 index 0000000000..a32235908b --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveAndBlockingMongoRepositoriesAutoConfigurationTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.mongo.city.CityRepository; +import org.springframework.boot.autoconfigure.data.mongo.city.ReactiveCityRepository; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfigurationTests; +import org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MongoRepositoriesAutoConfiguration} and + * {@link ReactiveMongoRepositoriesAutoConfiguration}. + * + * @author Mark Paluch + */ +public class ReactiveAndBlockingMongoRepositoriesAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + this.context.close(); + } + + @Test + public void shouldCreateInstancesForReactiveAndBlockingRepositories() + throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.initialize:false"); + this.context.register(BlockingAndReactiveConfiguration.class, + BaseConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + assertThat(this.context.getBean(ReactiveCityRepository.class)).isNotNull(); + } + + @Configuration + @TestAutoConfigurationPackage(MongoAutoConfigurationTests.class) + @EnableMongoRepositories(basePackageClasses = ReactiveCityRepository.class) + @EnableReactiveMongoRepositories(basePackageClasses = ReactiveCityRepository.class) + protected static class BlockingAndReactiveConfiguration { + + } + + @Configuration + @Import(Registrar.class) + protected static class BaseConfiguration { + + } + + protected static class Registrar implements ImportSelector { + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + List names = new ArrayList(); + for (Class type : new Class[] { MongoAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, + MongoRepositoriesAutoConfiguration.class, + ReactiveMongoDataAutoConfiguration.class, + ReactiveMongoRepositoriesAutoConfiguration.class }) { + names.add(type.getName()); + } + return names.toArray(new String[0]); + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfigurationTests.java new file mode 100644 index 0000000000..b9efde7d62 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoDataAutoConfigurationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.mongodb.core.ReactiveMongoTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ReactiveMongoDataAutoConfiguration}. + * + * @author Mark Paluch + */ +public class ReactiveMongoDataAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void templateExists() { + this.context = new AnnotationConfigApplicationContext( + PropertyPlaceholderAutoConfiguration.class, MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, ReactiveMongoAutoConfiguration.class, + ReactiveMongoDataAutoConfiguration.class); + assertThat(this.context.getBeanNamesForType(ReactiveMongoTemplate.class)) + .hasSize(1); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigurationTests.java new file mode 100644 index 0000000000..ea24dad649 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/ReactiveMongoRepositoriesAutoConfigurationTests.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo; + +import java.util.Set; + +import com.mongodb.reactivestreams.client.MongoClient; +import org.junit.After; +import org.junit.Test; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.alt.mongo.CityMongoDbRepository; +import org.springframework.boot.autoconfigure.data.alt.mongo.ReactiveCityMongoDbRepository; +import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; +import org.springframework.boot.autoconfigure.data.mongo.city.City; +import org.springframework.boot.autoconfigure.data.mongo.city.ReactiveCityRepository; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.ReactiveMongoAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ReactiveMongoRepositoriesAutoConfiguration}. + * + * @author Mark Paluch + */ +public class ReactiveMongoRepositoriesAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testDefaultRepositoryConfiguration() throws Exception { + prepareApplicationContext(TestConfiguration.class); + + assertThat(this.context.getBean(ReactiveCityRepository.class)).isNotNull(); + MongoClient client = this.context.getBean(MongoClient.class); + assertThat(client).isInstanceOf(MongoClient.class); + MongoMappingContext mappingContext = this.context + .getBean(MongoMappingContext.class); + @SuppressWarnings("unchecked") + Set> entities = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(entities).hasSize(1); + } + + @Test + public void testNoRepositoryConfiguration() throws Exception { + prepareApplicationContext(EmptyConfiguration.class); + + MongoClient client = this.context.getBean(MongoClient.class); + assertThat(client).isInstanceOf(MongoClient.class); + } + + @Test + public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { + prepareApplicationContext(CustomizedConfiguration.class); + + assertThat(this.context.getBeansOfType(ReactiveCityMongoDbRepository.class)).isEmpty(); + } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { + prepareApplicationContext(SortOfInvalidCustomConfiguration.class); + + this.context.getBean(ReactiveCityRepository.class); + } + + private void prepareApplicationContext(Class... configurationClasses) { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(configurationClasses); + this.context.register(MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class, + ReactiveMongoDataAutoConfiguration.class, + ReactiveMongoRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } + + @Configuration + @TestAutoConfigurationPackage(City.class) + protected static class TestConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(EmptyDataPackage.class) + protected static class EmptyConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(ReactiveMongoRepositoriesAutoConfigurationTests.class) + @EnableMongoRepositories(basePackageClasses = CityMongoDbRepository.class) + protected static class CustomizedConfiguration { + + } + + @Configuration + // To not find any repositories + @EnableReactiveMongoRepositories("foo.bar") + @TestAutoConfigurationPackage(ReactiveMongoRepositoriesAutoConfigurationTests.class) + protected static class SortOfInvalidCustomConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/city/ReactiveCityRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/city/ReactiveCityRepository.java new file mode 100644 index 0000000000..4c4e3f0cc2 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/city/ReactiveCityRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.data.mongo.city; + +import reactor.core.publisher.Flux; + +import org.springframework.data.repository.Repository; + +public interface ReactiveCityRepository extends Repository { + + Flux findAll(); + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java new file mode 100644 index 0000000000..6126d68474 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoClientFactoryTests.java @@ -0,0 +1,173 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import java.net.UnknownHostException; +import java.util.List; + +import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.connection.Cluster; +import com.mongodb.connection.ClusterSettings; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MongoClientFactory}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Stephane Nicoll + * @author Mark Paluch + */ +public class MongoClientFactoryTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void portCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setPort(12345); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 12345); + } + + @Test + public void hostCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setHost("mongo.example.com"); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "mongo.example.com", 27017); + } + + @Test + public void credentialsCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", + "test"); + } + + @Test + public void databaseCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setDatabase("foo"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", + "foo"); + } + + @Test + public void authenticationDatabaseCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setAuthenticationDatabase("foo"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", + "foo"); + } + + @Test + public void uriCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://user:secret@mongo1.example.com:12345," + + "mongo2.example.com:23456/test"); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(2); + assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); + assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); + List credentialsList = client.getCredentialsList(); + assertThat(credentialsList).hasSize(1); + assertMongoCredential(credentialsList.get(0), "user", "secret", "test"); + } + + @Test + public void uriCannotBeSetWithCredentials() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://127.0.0.1:1234/mydb"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + createMongoClient(properties); + } + + @Test + public void uriCannotBeSetWithHostPort() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://127.0.0.1:1234/mydb"); + properties.setHost("localhost"); + properties.setPort(4567); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + createMongoClient(properties); + } + + private MongoClient createMongoClient(MongoProperties properties) + throws UnknownHostException { + return new MongoClientFactory(properties, null).createMongoClient(null); + } + + private List extractServerAddresses(MongoClient client) { + Cluster cluster = (Cluster) ReflectionTestUtils.getField(client, "cluster"); + ClusterSettings clusterSettings = (ClusterSettings) ReflectionTestUtils + .getField(cluster, "settings"); + List allAddresses = clusterSettings.getHosts(); + return allAddresses; + } + + private void assertServerAddress(ServerAddress serverAddress, String expectedHost, + int expectedPort) { + assertThat(serverAddress.getHost()).isEqualTo(expectedHost); + assertThat(serverAddress.getPort()).isEqualTo(expectedPort); + } + + private void assertMongoCredential(MongoCredential credentials, + String expectedUsername, String expectedPassword, String expectedSource) { + assertThat(credentials.getUserName()).isEqualTo(expectedUsername); + assertThat(credentials.getPassword()).isEqualTo(expectedPassword.toCharArray()); + assertThat(credentials.getSource()).isEqualTo(expectedSource); + } + + @Configuration + @EnableConfigurationProperties(MongoProperties.class) + static class Config { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java index cd79c5e3b0..504e807f8b 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -17,14 +17,9 @@ package org.springframework.boot.autoconfigure.mongo; import java.net.UnknownHostException; -import java.util.List; import com.mongodb.MongoClient; import com.mongodb.MongoClientOptions; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; -import com.mongodb.connection.Cluster; -import com.mongodb.connection.ClusterSettings; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -33,7 +28,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; -import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -43,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Phillip Webb * @author Andy Wilkinson * @author Stephane Nicoll + * @author Mark Paluch */ public class MongoPropertiesTests { @@ -60,97 +55,6 @@ public class MongoPropertiesTests { assertThat(properties.getPassword()).isEqualTo("word".toCharArray()); } - @Test - public void portCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setPort(12345); - MongoClient client = properties.createMongoClient(null, null); - List allAddresses = extractServerAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "localhost", 12345); - } - - @Test - public void hostCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setHost("mongo.example.com"); - MongoClient client = properties.createMongoClient(null, null); - List allAddresses = extractServerAddresses(client); - assertThat(allAddresses).hasSize(1); - assertServerAddress(allAddresses.get(0), "mongo.example.com", 27017); - } - - @Test - public void credentialsCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setUsername("user"); - properties.setPassword("secret".toCharArray()); - MongoClient client = properties.createMongoClient(null, null); - assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", - "test"); - } - - @Test - public void databaseCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setDatabase("foo"); - properties.setUsername("user"); - properties.setPassword("secret".toCharArray()); - MongoClient client = properties.createMongoClient(null, null); - assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", - "foo"); - } - - @Test - public void authenticationDatabaseCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setAuthenticationDatabase("foo"); - properties.setUsername("user"); - properties.setPassword("secret".toCharArray()); - MongoClient client = properties.createMongoClient(null, null); - assertMongoCredential(client.getCredentialsList().get(0), "user", "secret", - "foo"); - } - - @Test - public void uriCanBeCustomized() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setUri("mongodb://user:secret@mongo1.example.com:12345," - + "mongo2.example.com:23456/test"); - MongoClient client = properties.createMongoClient(null, null); - List allAddresses = extractServerAddresses(client); - assertThat(allAddresses).hasSize(2); - assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); - assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); - List credentialsList = client.getCredentialsList(); - assertThat(credentialsList).hasSize(1); - assertMongoCredential(credentialsList.get(0), "user", "secret", "test"); - } - - @Test - public void uriCannotBeSetWithCredentials() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setUri("mongodb://127.0.0.1:1234/mydb"); - properties.setUsername("user"); - properties.setPassword("secret".toCharArray()); - this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Invalid mongo configuration, " - + "either uri or host/port/credentials must be specified"); - properties.createMongoClient(null, null); - } - - @Test - public void uriCannotBeSetWithHostPort() throws UnknownHostException { - MongoProperties properties = new MongoProperties(); - properties.setUri("mongodb://127.0.0.1:1234/mydb"); - properties.setHost("localhost"); - properties.setPort(4567); - this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Invalid mongo configuration, " - + "either uri or host/port/credentials must be specified"); - properties.createMongoClient(null, null); - } - @Test public void allMongoClientOptionsCanBeSet() throws UnknownHostException { MongoClientOptions.Builder builder = MongoClientOptions.builder(); @@ -174,7 +78,8 @@ public class MongoPropertiesTests { builder.requiredReplicaSetName("testReplicaSetName"); MongoClientOptions options = builder.build(); MongoProperties properties = new MongoProperties(); - MongoClient client = properties.createMongoClient(options, null); + MongoClient client = new MongoClientFactory(properties, null) + .createMongoClient(options); MongoClientOptions wrapped = client.getMongoClientOptions(); assertThat(wrapped.isAlwaysUseMBeans()).isEqualTo(options.isAlwaysUseMBeans()); assertThat(wrapped.getConnectionsPerHost()) @@ -207,27 +112,6 @@ public class MongoPropertiesTests { .isEqualTo(options.getRequiredReplicaSetName()); } - private List extractServerAddresses(MongoClient client) { - Cluster cluster = (Cluster) ReflectionTestUtils.getField(client, "cluster"); - ClusterSettings clusterSettings = (ClusterSettings) ReflectionTestUtils - .getField(cluster, "settings"); - List allAddresses = clusterSettings.getHosts(); - return allAddresses; - } - - private void assertServerAddress(ServerAddress serverAddress, String expectedHost, - int expectedPort) { - assertThat(serverAddress.getHost()).isEqualTo(expectedHost); - assertThat(serverAddress.getPort()).isEqualTo(expectedPort); - } - - private void assertMongoCredential(MongoCredential credentials, - String expectedUsername, String expectedPassword, String expectedSource) { - assertThat(credentials.getUserName()).isEqualTo(expectedUsername); - assertThat(credentials.getPassword()).isEqualTo(expectedPassword.toCharArray()); - assertThat(credentials.getSource()).isEqualTo(expectedSource); - } - @Configuration @EnableConfigurationProperties(MongoProperties.class) static class Config { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfigurationTests.java new file mode 100644 index 0000000000..bbc8360012 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoAutoConfigurationTests.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import java.util.concurrent.TimeUnit; + +import com.mongodb.ReadPreference; +import com.mongodb.async.client.MongoClientSettings; +import com.mongodb.connection.SocketSettings; +import com.mongodb.connection.StreamFactory; +import com.mongodb.connection.StreamFactoryFactory; +import com.mongodb.reactivestreams.client.MongoClient; +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link ReactiveMongoAutoConfiguration}. + * + * @author Mark Paluch + */ +public class ReactiveMongoAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void clientExists() { + this.context = new AnnotationConfigApplicationContext( + PropertyPlaceholderAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class); + assertThat(this.context.getBeanNamesForType(MongoClient.class).length).isEqualTo(1); + } + + @Test + public void optionsAdded() { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.data.mongodb.host:localhost"); + this.context.register(OptionsConfig.class, + PropertyPlaceholderAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(MongoClient.class).getSettings() + .getSocketSettings().getReadTimeout(TimeUnit.SECONDS)).isEqualTo(300); + } + + @Test + public void optionsAddedButNoHost() { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.data.mongodb.uri:mongodb://localhost/test"); + this.context.register(OptionsConfig.class, + PropertyPlaceholderAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(MongoClient.class).getSettings() + .getReadPreference()).isEqualTo(ReadPreference.nearest()); + } + + @Test + public void optionsSslConfig() { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.data.mongodb.uri:mongodb://localhost/test"); + this.context.register(SslOptionsConfig.class, + PropertyPlaceholderAutoConfiguration.class, + ReactiveMongoAutoConfiguration.class); + this.context.refresh(); + MongoClient mongo = this.context.getBean(MongoClient.class); + MongoClientSettings settings = mongo.getSettings(); + assertThat(settings.getApplicationName()).isEqualTo("test-config"); + assertThat(settings.getStreamFactoryFactory()) + .isSameAs(this.context.getBean("myStreamFactoryFactory")); + } + + @Configuration + static class OptionsConfig { + + @Bean + public MongoClientSettings mongoClientSettings() { + return MongoClientSettings + .builder() + .readPreference(ReadPreference.nearest()) + .socketSettings( + SocketSettings.builder().readTimeout(300, TimeUnit.SECONDS) + .build()).build(); + } + + } + + @Configuration + static class SslOptionsConfig { + + @Bean + public MongoClientSettings mongoClientSettings() { + return MongoClientSettings.builder().applicationName("test-config") + .streamFactoryFactory(myStreamFactoryFactory()).build(); + } + + @Bean + public StreamFactoryFactory myStreamFactoryFactory() { + StreamFactoryFactory streamFactoryFactory = mock(StreamFactoryFactory.class); + given(streamFactoryFactory.create(any(), any())).willReturn(mock(StreamFactory.class)); + return streamFactoryFactory; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java new file mode 100644 index 0000000000..651d733ef2 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/ReactiveMongoClientFactoryTests.java @@ -0,0 +1,163 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigure.mongo; + +import java.net.UnknownHostException; +import java.util.List; + +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import com.mongodb.async.client.MongoClientSettings; +import com.mongodb.connection.ClusterSettings; +import com.mongodb.reactivestreams.client.MongoClient; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ReactiveMongoClientFactory}. + * + * @author Mark Paluch + */ +public class ReactiveMongoClientFactoryTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void portCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setPort(12345); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "localhost", 12345); + } + + @Test + public void hostCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setHost("mongo.example.com"); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(1); + assertServerAddress(allAddresses.get(0), "mongo.example.com", 27017); + } + + @Test + public void credentialsCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(extractMongoCredentials(client).get(0), "user", "secret", + "test"); + } + + @Test + public void databaseCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setDatabase("foo"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(extractMongoCredentials(client).get(0), "user", "secret", + "foo"); + } + + @Test + public void authenticationDatabaseCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setAuthenticationDatabase("foo"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + MongoClient client = createMongoClient(properties); + assertMongoCredential(extractMongoCredentials(client).get(0), "user", "secret", + "foo"); + } + + @Test + public void uriCanBeCustomized() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://user:secret@mongo1.example.com:12345," + + "mongo2.example.com:23456/test"); + MongoClient client = createMongoClient(properties); + List allAddresses = extractServerAddresses(client); + assertThat(allAddresses).hasSize(2); + assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); + assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); + List credentialsList = extractMongoCredentials(client); + assertThat(credentialsList).hasSize(1); + assertMongoCredential(credentialsList.get(0), "user", "secret", "test"); + } + + @Test + public void uriCannotBeSetWithCredentials() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://127.0.0.1:1234/mydb"); + properties.setUsername("user"); + properties.setPassword("secret".toCharArray()); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + createMongoClient(properties); + } + + @Test + public void uriCannotBeSetWithHostPort() throws UnknownHostException { + MongoProperties properties = new MongoProperties(); + properties.setUri("mongodb://127.0.0.1:1234/mydb"); + properties.setHost("localhost"); + properties.setPort(4567); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Invalid mongo configuration, " + + "either uri or host/port/credentials must be specified"); + createMongoClient(properties); + } + + private MongoClient createMongoClient(MongoProperties properties) { + return new ReactiveMongoClientFactory(properties, null).createMongoClient(null); + } + + private List extractServerAddresses(MongoClient client) { + MongoClientSettings settings = client.getSettings(); + ClusterSettings clusterSettings = settings.getClusterSettings(); + List allAddresses = clusterSettings.getHosts(); + return allAddresses; + } + + private List extractMongoCredentials(MongoClient client) { + MongoClientSettings settings = client.getSettings(); + return settings.getCredentialList(); + } + + private void assertServerAddress(ServerAddress serverAddress, String expectedHost, + int expectedPort) { + assertThat(serverAddress.getHost()).isEqualTo(expectedHost); + assertThat(serverAddress.getPort()).isEqualTo(expectedPort); + } + + private void assertMongoCredential(MongoCredential credentials, + String expectedUsername, String expectedPassword, String expectedSource) { + assertThat(credentials.getUserName()).isEqualTo(expectedUsername); + assertThat(credentials.getPassword()).isEqualTo(expectedPassword.toCharArray()); + assertThat(credentials.getSource()).isEqualTo(expectedSource); + } + +} diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index cb061fd130..fba3daff26 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -376,6 +376,11 @@ spring-boot-starter-data-mongodb 2.0.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + 2.0.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-starter-data-redis diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 842c9b33e0..f2e5cf28be 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -589,6 +589,7 @@ content into your application; rather pick only the properties that you need. spring.data.mongodb.host=localhost # Mongo server host. Cannot be set with uri. spring.data.mongodb.password= # Login password of the mongo server. Cannot be set with uri. spring.data.mongodb.port=27017 # Mongo server port. Cannot be set with uri. + spring.data.mongodb.reactive-repositories.enabled=true # Enable Mongo reactive repositories. spring.data.mongodb.repositories.enabled=true # Enable Mongo repositories. spring.data.mongodb.uri=mongodb://localhost/test # Mongo database URI. Cannot be set with host, port and credentials. spring.data.mongodb.username= # Login user of the mongo server. Cannot be set with uri. diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index c76b67afb6..33b22b2cb1 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3299,7 +3299,8 @@ pooled connection factory by default. http://www.mongodb.com/[MongoDB] is an open-source NoSQL document database that uses a JSON-like schema instead of traditional table-based relational data. Spring Boot offers several conveniences for working with MongoDB, including the -`spring-boot-starter-data-mongodb` '`Starter`'. +`spring-boot-starter-data-mongodb` and `spring-boot-starter-data-mongodb-reactive` +'`Starters`'. diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index cac5277543..b49990d3b4 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -34,6 +34,7 @@ spring-boot-starter-data-jpa spring-boot-starter-data-ldap spring-boot-starter-data-mongodb + spring-boot-starter-data-mongodb-reactive spring-boot-starter-data-neo4j spring-boot-starter-data-redis spring-boot-starter-data-rest diff --git a/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/pom.xml b/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/pom.xml new file mode 100644 index 0000000000..258f03e19b --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/pom.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 2.0.0.BUILD-SNAPSHOT + + spring-boot-starter-data-mongodb-reactive + Spring Boot Data MongoDB Reactive Starter + Starter for using MongoDB document-oriented database and Spring Data + MongoDB Reactive + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.data + spring-data-mongodb + + + org.mongodb + mongo-java-driver + + + + + org.mongodb + mongodb-driver + + + org.mongodb + mongodb-driver-async + + + org.mongodb + mongodb-driver-reactivestreams + + + io.projectreactor + reactor-core + + + diff --git a/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/src/main/resources/META-INF/spring.provides new file mode 100644 index 0000000000..60fce3513b --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-mongodb-reactive/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: spring-data-mongodb,mongodb-driver-async,mongodb-driver-reactivestreams \ No newline at end of file diff --git a/spring-boot-starters/spring-boot-starter-data-mongodb/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-data-mongodb/src/main/resources/META-INF/spring.provides index c7406f4ec6..14fde4c65b 100644 --- a/spring-boot-starters/spring-boot-starter-data-mongodb/src/main/resources/META-INF/spring.provides +++ b/spring-boot-starters/spring-boot-starter-data-mongodb/src/main/resources/META-INF/spring.provides @@ -1 +1 @@ -provides: spring-data-mongodb \ No newline at end of file +provides: spring-data-mongodb,mongodb-driver \ No newline at end of file