Add ConnectionDetail support to Couchbase auto-configuration
Update Couchbase auto-configuration so that `CouchbaseConnectionDetails` beans may be optionally used to provide connection details. See gh-34657 Co-Authored-By: Mortitz Halbritter <mkammerer@vmware.com> Co-Authored-By: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
@@ -34,14 +34,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.CouchbaseCondition;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties.Timeouts;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
@@ -52,34 +56,49 @@ import org.springframework.util.ResourceUtils;
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Yulin Qin
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@AutoConfiguration(after = JacksonAutoConfiguration.class)
|
||||
@ConditionalOnClass(Cluster.class)
|
||||
@ConditionalOnProperty("spring.couchbase.connection-string")
|
||||
@Conditional(CouchbaseCondition.class)
|
||||
@EnableConfigurationProperties(CouchbaseProperties.class)
|
||||
public class CouchbaseAutoConfiguration {
|
||||
|
||||
private final CouchbaseProperties properties;
|
||||
|
||||
private final CouchbaseConnectionDetails connectionDetails;
|
||||
|
||||
CouchbaseAutoConfiguration(CouchbaseProperties properties,
|
||||
ObjectProvider<CouchbaseConnectionDetails> connectionDetails) {
|
||||
this.properties = properties;
|
||||
this.connectionDetails = connectionDetails
|
||||
.getIfAvailable(() -> new PropertiesCouchbaseConnectionDetails(properties));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClusterEnvironment couchbaseClusterEnvironment(CouchbaseProperties properties,
|
||||
public ClusterEnvironment couchbaseClusterEnvironment(
|
||||
ObjectProvider<ClusterEnvironmentBuilderCustomizer> customizers) {
|
||||
Builder builder = initializeEnvironmentBuilder(properties);
|
||||
Builder builder = initializeEnvironmentBuilder();
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "disconnect")
|
||||
@ConditionalOnMissingBean
|
||||
public Cluster couchbaseCluster(CouchbaseProperties properties, ClusterEnvironment couchbaseClusterEnvironment) {
|
||||
ClusterOptions options = ClusterOptions.clusterOptions(properties.getUsername(), properties.getPassword())
|
||||
public Cluster couchbaseCluster(ClusterEnvironment couchbaseClusterEnvironment) {
|
||||
ClusterOptions options = ClusterOptions
|
||||
.clusterOptions(this.connectionDetails.getUsername(), this.connectionDetails.getPassword())
|
||||
.environment(couchbaseClusterEnvironment);
|
||||
return Cluster.connect(properties.getConnectionString(), options);
|
||||
return Cluster.connect(this.connectionDetails.getConnectionString(), options);
|
||||
}
|
||||
|
||||
private ClusterEnvironment.Builder initializeEnvironmentBuilder(CouchbaseProperties properties) {
|
||||
private ClusterEnvironment.Builder initializeEnvironmentBuilder() {
|
||||
ClusterEnvironment.Builder builder = ClusterEnvironment.builder();
|
||||
Timeouts timeouts = properties.getEnv().getTimeouts();
|
||||
Timeouts timeouts = this.properties.getEnv().getTimeouts();
|
||||
builder.timeoutConfig((config) -> config.kvTimeout(timeouts.getKeyValue())
|
||||
.analyticsTimeout(timeouts.getAnalytics())
|
||||
.kvDurableTimeout(timeouts.getKeyValueDurable())
|
||||
@@ -89,13 +108,14 @@ public class CouchbaseAutoConfiguration {
|
||||
.managementTimeout(timeouts.getManagement())
|
||||
.connectTimeout(timeouts.getConnect())
|
||||
.disconnectTimeout(timeouts.getDisconnect()));
|
||||
CouchbaseProperties.Io io = properties.getEnv().getIo();
|
||||
CouchbaseProperties.Io io = this.properties.getEnv().getIo();
|
||||
builder.ioConfig((config) -> config.maxHttpConnections(io.getMaxEndpoints())
|
||||
.numKvConnections(io.getMinEndpoints())
|
||||
.idleHttpConnectionTimeout(io.getIdleHttpConnectionTimeout()));
|
||||
if (properties.getEnv().getSsl().getEnabled()) {
|
||||
if ((this.connectionDetails instanceof PropertiesCouchbaseConnectionDetails)
|
||||
&& this.properties.getEnv().getSsl().getEnabled()) {
|
||||
builder.securityConfig((config) -> config.enableTls(true)
|
||||
.trustManagerFactory(getTrustManagerFactory(properties.getEnv().getSsl())));
|
||||
.trustManagerFactory(getTrustManagerFactory(this.properties.getEnv().getSsl())));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
@@ -157,4 +177,54 @@ public class CouchbaseAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition that matches when {@code spring.couchbase.connection-string} has been
|
||||
* configured or there is a {@link CouchbaseConnectionDetails} bean.
|
||||
*/
|
||||
static final class CouchbaseCondition extends AnyNestedCondition {
|
||||
|
||||
CouchbaseCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.couchbase", name = "connection-string")
|
||||
private static final class CouchbaseUrlCondition {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnBean(CouchbaseConnectionDetails.class)
|
||||
private static final class CouchbaseConnectionDetailsCondition {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts {@link CouchbaseProperties} to {@link CouchbaseConnectionDetails}.
|
||||
*/
|
||||
static final class PropertiesCouchbaseConnectionDetails implements CouchbaseConnectionDetails {
|
||||
|
||||
private final CouchbaseProperties properties;
|
||||
|
||||
PropertiesCouchbaseConnectionDetails(CouchbaseProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConnectionString() {
|
||||
return this.properties.getConnectionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.properties.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return this.properties.getPassword();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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
|
||||
*
|
||||
* https://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.couchbase;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Couchbase service.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public interface CouchbaseConnectionDetails extends ConnectionDetails {
|
||||
|
||||
/**
|
||||
* Connection string used to locate the Couchbase cluster.
|
||||
* @return the connection string used to locate the Couchbase cluster
|
||||
*/
|
||||
String getConnectionString();
|
||||
|
||||
/**
|
||||
* Cluster username.
|
||||
* @return the cluster username
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* Cluster password.
|
||||
* @return the cluster password
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
}
|
||||
@@ -48,6 +48,9 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Moritz Halbritter
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CouchbaseAutoConfigurationTests {
|
||||
|
||||
@@ -60,6 +63,19 @@ class CouchbaseAutoConfigurationTests {
|
||||
.doesNotHaveBean(Cluster.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseConnectionDetails() {
|
||||
this.contextRunner.withBean(CouchbaseConnectionDetails.class, this::couchbaseConnectionDetails)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ClusterEnvironment.class).hasSingleBean(Cluster.class);
|
||||
Cluster cluster = context.getBean(Cluster.class);
|
||||
assertThat(cluster.core()).extracting("connectionString.hosts")
|
||||
.asList()
|
||||
.extractingResultOf("host")
|
||||
.containsExactly("couchbase.example.com");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionStringCreateEnvironmentAndCluster() {
|
||||
this.contextRunner.withUserConfiguration(CouchbaseTestConfiguration.class)
|
||||
@@ -71,6 +87,21 @@ class CouchbaseAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectionDetailsShouldOverrideProperties() {
|
||||
this.contextRunner.withBean(CouchbaseConnectionDetails.class, this::couchbaseConnectionDetails)
|
||||
.withPropertyValues("spring.couchbase.connection-string=localhost", "spring.couchbase.username=a-user",
|
||||
"spring.couchbase.password=a-password")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(ClusterEnvironment.class).hasSingleBean(Cluster.class);
|
||||
Cluster cluster = context.getBean(Cluster.class);
|
||||
assertThat(cluster.core()).extracting("connectionString.hosts")
|
||||
.asList()
|
||||
.extractingResultOf("host")
|
||||
.containsExactly("couchbase.example.com");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenObjectMapperBeanIsDefinedThenClusterEnvironmentObjectMapperIsDerivedFromIt() {
|
||||
this.contextRunner.withUserConfiguration(CouchbaseTestConfiguration.class)
|
||||
@@ -176,6 +207,27 @@ class CouchbaseAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
private CouchbaseConnectionDetails couchbaseConnectionDetails() {
|
||||
return new CouchbaseConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public String getConnectionString() {
|
||||
return "couchbase.example.com";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return "user-1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "password-1";
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ClusterEnvironmentCustomizerConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user