001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.mongo; 018 019import java.util.Collections; 020import java.util.List; 021 022import com.mongodb.ConnectionString; 023import com.mongodb.MongoClientSettings; 024import com.mongodb.MongoClientSettings.Builder; 025import com.mongodb.MongoCredential; 026import com.mongodb.ServerAddress; 027import com.mongodb.reactivestreams.client.MongoClient; 028import com.mongodb.reactivestreams.client.MongoClients; 029 030import org.springframework.core.env.Environment; 031import org.springframework.util.Assert; 032 033/** 034 * A factory for a reactive {@link MongoClient} that applies {@link MongoProperties}. 035 * 036 * @author Mark Paluch 037 * @author Stephane Nicoll 038 * @since 2.0.0 039 */ 040public class ReactiveMongoClientFactory { 041 042 private final MongoProperties properties; 043 044 private final Environment environment; 045 046 private final List<MongoClientSettingsBuilderCustomizer> builderCustomizers; 047 048 public ReactiveMongoClientFactory(MongoProperties properties, Environment environment, 049 List<MongoClientSettingsBuilderCustomizer> builderCustomizers) { 050 this.properties = properties; 051 this.environment = environment; 052 this.builderCustomizers = (builderCustomizers != null) ? builderCustomizers 053 : Collections.emptyList(); 054 } 055 056 /** 057 * Creates a {@link MongoClient} using the given {@code settings}. If the environment 058 * contains a {@code local.mongo.port} property, it is used to configure a client to 059 * an embedded MongoDB instance. 060 * @param settings the settings 061 * @return the Mongo client 062 */ 063 public MongoClient createMongoClient(MongoClientSettings settings) { 064 Integer embeddedPort = getEmbeddedPort(); 065 if (embeddedPort != null) { 066 return createEmbeddedMongoClient(settings, embeddedPort); 067 } 068 return createNetworkMongoClient(settings); 069 } 070 071 private Integer getEmbeddedPort() { 072 if (this.environment != null) { 073 String localPort = this.environment.getProperty("local.mongo.port"); 074 if (localPort != null) { 075 return Integer.valueOf(localPort); 076 } 077 } 078 return null; 079 } 080 081 private MongoClient createEmbeddedMongoClient(MongoClientSettings settings, 082 int port) { 083 Builder builder = builder(settings); 084 String host = (this.properties.getHost() != null) ? this.properties.getHost() 085 : "localhost"; 086 builder.applyToClusterSettings((cluster) -> cluster 087 .hosts(Collections.singletonList(new ServerAddress(host, port)))); 088 return createMongoClient(builder); 089 } 090 091 private MongoClient createNetworkMongoClient(MongoClientSettings settings) { 092 if (hasCustomAddress() || hasCustomCredentials()) { 093 return createCredentialNetworkMongoClient(settings); 094 } 095 ConnectionString connectionString = new ConnectionString( 096 this.properties.determineUri()); 097 return createMongoClient(createBuilder(settings, connectionString)); 098 } 099 100 private MongoClient createCredentialNetworkMongoClient(MongoClientSettings settings) { 101 Assert.state(this.properties.getUri() == null, "Invalid mongo configuration, " 102 + "either uri or host/port/credentials must be specified"); 103 Builder builder = builder(settings); 104 if (hasCustomCredentials()) { 105 applyCredentials(builder); 106 } 107 String host = getOrDefault(this.properties.getHost(), "localhost"); 108 int port = getOrDefault(this.properties.getPort(), MongoProperties.DEFAULT_PORT); 109 ServerAddress serverAddress = new ServerAddress(host, port); 110 builder.applyToClusterSettings( 111 (cluster) -> cluster.hosts(Collections.singletonList(serverAddress))); 112 return createMongoClient(builder); 113 } 114 115 private void applyCredentials(Builder builder) { 116 String database = (this.properties.getAuthenticationDatabase() != null) 117 ? this.properties.getAuthenticationDatabase() 118 : this.properties.getMongoClientDatabase(); 119 builder.credential((MongoCredential.createCredential( 120 this.properties.getUsername(), database, this.properties.getPassword()))); 121 } 122 123 private <T> T getOrDefault(T value, T defaultValue) { 124 return (value != null) ? value : defaultValue; 125 } 126 127 private MongoClient createMongoClient(Builder builder) { 128 customize(builder); 129 return MongoClients.create(builder.build()); 130 } 131 132 private Builder createBuilder(MongoClientSettings settings, 133 ConnectionString connection) { 134 return builder(settings).applyConnectionString(connection); 135 } 136 137 private void customize(MongoClientSettings.Builder builder) { 138 for (MongoClientSettingsBuilderCustomizer customizer : this.builderCustomizers) { 139 customizer.customize(builder); 140 } 141 } 142 143 private boolean hasCustomAddress() { 144 return this.properties.getHost() != null || this.properties.getPort() != null; 145 } 146 147 private boolean hasCustomCredentials() { 148 return this.properties.getUsername() != null 149 && this.properties.getPassword() != null; 150 } 151 152 private Builder builder(MongoClientSettings settings) { 153 if (settings == null) { 154 return MongoClientSettings.builder(); 155 } 156 return MongoClientSettings.builder(settings); 157 } 158 159}