Provide a starter for reactive Spring Data MongoDB

Add autoconfiguration to bootstrap MongoDB Reactive Streams driver
components, reactive Spring Data MongoDB and reactive repositories. Add
bean dependency processor for flapdoodle so embedded MongoDB instances
are configured before bootstraping the reactive MongoDB client.

Add Spring Data MongoDB Reactive starter with blocking and non-blocking
dependencies. MongoDB requires a separate driver that is used in the
`ReactiveMongoTemplate` while `MappingMongoConverter` (shared amongst
blocking/reactive Template API) requires the blocking driver to resolve
DBRefs.

See gh-8230
This commit is contained in:
Mark Paluch 2017-02-08 09:42:41 +01:00 committed by Stephane Nicoll
parent 13791cad53
commit 48b0f1577b
24 changed files with 1511 additions and 209 deletions

View File

@ -100,6 +100,16 @@
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>

View File

@ -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.
* <p>
* Registers a {@link ReactiveMongoTemplate} bean if no other bean of the same type is
* configured.
* <P>
* 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);
}
}

View File

@ -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.
* <p>
* 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}.
* <p>
* 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 {
}

View File

@ -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<? extends Annotation> getAnnotation() {
return EnableReactiveMongoRepositories.class;
}
@Override
protected Class<?> getConfiguration() {
return EnableReactiveMongoRepositoriesConfiguration.class;
}
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
return new ReactiveMongoRepositoryConfigurationExtension();
}
@EnableReactiveMongoRepositories
private static class EnableReactiveMongoRepositoriesConfiguration {
}
}

View File

@ -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);
}
}

View File

@ -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,7 @@ import org.springframework.core.env.Environment;
* @author Dave Syer
* @author Oliver Gierke
* @author Phillip Webb
* @author Mark Paluch
*/
@Configuration
@ConditionalOnClass(MongoClient.class)
@ -51,6 +52,8 @@ public class MongoAutoConfiguration {
private final Environment environment;
private final MongoClientFactory factory;
private MongoClient mongo;
public MongoAutoConfiguration(MongoProperties properties,
@ -58,6 +61,7 @@ public class MongoAutoConfiguration {
this.properties = properties;
this.options = options.getIfAvailable();
this.environment = environment;
this.factory = new MongoClientFactory(properties);
}
@PreDestroy
@ -70,7 +74,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, this.environment);
return this.mongo;
}

View File

@ -0,0 +1,121 @@
/*
* 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 Mark Paluch
* @since 2.0.0
*/
public class MongoClientFactory {
private final MongoProperties properties;
public MongoClientFactory(MongoProperties properties) {
this.properties = properties;
}
/**
* 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 {
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<MongoCredential> credentials = new ArrayList<MongoCredential>();
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(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(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(Environment environment) {
if (this.properties.getPort() == null) {
return MongoProperties.DEFAULT_PORT;
}
if (this.properties.getPort() == 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.properties.getPort();
}
private Builder builder(MongoClientOptions options) {
if (options != null) {
return MongoClientOptions.builder(options);
}
return MongoClientOptions.builder();
}
}

View File

@ -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 {
@ -189,79 +179,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<MongoCredential> credentials = new ArrayList<MongoCredential>();
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();
}
}

View File

@ -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.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
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(MongoClient.class)
@EnableConfigurationProperties(MongoProperties.class)
public class ReactiveMongoAutoConfiguration {
private final MongoProperties properties;
private final MongoClientSettings settings;
private final Environment environment;
private final ReactiveMongoClientFactory factory;
private MongoClient mongo;
public ReactiveMongoAutoConfiguration(MongoProperties properties,
ObjectProvider<MongoClientSettings> settings, Environment environment) {
this.properties = properties;
this.settings = settings.getIfAvailable();
this.environment = environment;
this.factory = new ReactiveMongoClientFactory(properties);
}
@PreDestroy
public void close() {
if (this.mongo != null) {
this.mongo.close();
}
}
@Bean
@ConditionalOnMissingBean
public MongoClient reactiveStreamsMongoClient() {
this.mongo = this.factory.createMongoClient(this.settings, this.environment);
return this.mongo;
}
}

View File

@ -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.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
* @since 2.0.0
*/
public class ReactiveMongoClientFactory {
private final MongoProperties properties;
public ReactiveMongoClientFactory(MongoProperties properties) {
this.properties = properties;
}
/**
* 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 settings the settings
* @param environment the environment
* @return the Mongo client
*/
public MongoClient createMongoClient(MongoClientSettings settings,
Environment environment) {
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<MongoCredential> credentials = new ArrayList<MongoCredential>();
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(environment);
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(Environment environment) {
if (this.properties.getPort() == null) {
return MongoProperties.DEFAULT_PORT;
}
if (this.properties.getPort() == 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.properties.getPort();
}
private Builder builder(MongoClientSettings settings) {
if (settings == null) {
return MongoClientSettings.builder();
}
return MongoClientSettings.builder(settings);
}
}

View File

@ -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,21 @@ 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}.

View File

@ -0,0 +1,24 @@
/*
* 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<City, Long> {
}

View File

@ -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<String> names = new ArrayList<String>();
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]);
}
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
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 {
@Rule
public final ExpectedException thrown = ExpectedException.none();
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).length)
.isEqualTo(1);
}
}

View File

@ -0,0 +1,134 @@
/*
* 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() {
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<? extends Class<?>> entities = (Set<? extends Class<?>>) 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 {
}
}

View File

@ -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<City, Long> {
Flux<City> findAll();
}

View File

@ -0,0 +1,168 @@
/*
* 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} via {@link MongoProperties}.
*
* @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 = new MongoClientFactory(properties).createMongoClient(null, null);
List<ServerAddress> 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 = new MongoClientFactory(properties).createMongoClient(null, null);
List<ServerAddress> 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 = new MongoClientFactory(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 = new MongoClientFactory(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 = new MongoClientFactory(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 = new MongoClientFactory(properties).createMongoClient(null, null);
List<ServerAddress> allAddresses = extractServerAddresses(client);
assertThat(allAddresses).hasSize(2);
assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345);
assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456);
List<MongoCredential> 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");
new MongoClientFactory(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");
new MongoClientFactory(properties).createMongoClient(null, null);
}
private List<ServerAddress> extractServerAddresses(MongoClient client) {
Cluster cluster = (Cluster) ReflectionTestUtils.getField(client, "cluster");
ClusterSettings clusterSettings = (ClusterSettings) ReflectionTestUtils
.getField(cluster, "settings");
List<ServerAddress> 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 {
}
}

View File

@ -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<ServerAddress> 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<ServerAddress> 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<ServerAddress> allAddresses = extractServerAddresses(client);
assertThat(allAddresses).hasSize(2);
assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345);
assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456);
List<MongoCredential> 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,7 @@ 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).createMongoClient(options, null);
MongoClientOptions wrapped = client.getMongoClientOptions();
assertThat(wrapped.isAlwaysUseMBeans()).isEqualTo(options.isAlwaysUseMBeans());
assertThat(wrapped.getConnectionsPerHost())
@ -207,27 +111,6 @@ public class MongoPropertiesTests {
.isEqualTo(options.getRequiredReplicaSetName());
}
private List<ServerAddress> extractServerAddresses(MongoClient client) {
Cluster cluster = (Cluster) ReflectionTestUtils.getField(client, "cluster");
ClusterSettings clusterSettings = (ClusterSettings) ReflectionTestUtils
.getField(cluster, "settings");
List<ServerAddress> 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 {

View File

@ -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.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;
}
}
}

View File

@ -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} via {@link MongoProperties}.
*
* @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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
List<ServerAddress> 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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
List<ServerAddress> 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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
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 = new ReactiveMongoClientFactory(properties).createMongoClient(null,
null);
List<ServerAddress> allAddresses = extractServerAddresses(client);
assertThat(allAddresses).hasSize(2);
assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345);
assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456);
List<MongoCredential> 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");
new MongoClientFactory(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");
new MongoClientFactory(properties).createMongoClient(null, null);
}
private List<ServerAddress> extractServerAddresses(MongoClient client) {
MongoClientSettings settings = client.getSettings();
ClusterSettings clusterSettings = settings.getClusterSettings();
List<ServerAddress> allAddresses = clusterSettings.getHosts();
return allAddresses;
}
private List<MongoCredential> 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);
}
}

View File

@ -376,6 +376,11 @@
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>

View File

@ -34,6 +34,7 @@
<module>spring-boot-starter-data-jpa</module>
<module>spring-boot-starter-data-ldap</module>
<module>spring-boot-starter-data-mongodb</module>
<module>spring-boot-starter-data-mongodb-reactive</module>
<module>spring-boot-starter-data-neo4j</module>
<module>spring-boot-starter-data-redis</module>
<module>spring-boot-starter-data-rest</module>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
<name>Spring Boot Data MongoDB Reactive Starter</name>
<description>Starter for using MongoDB document-oriented database and Spring Data
MongoDB Reactive</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-async</artifactId>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<exclusions>
<exclusion>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1 @@
provides: spring-data-mongodb-reactive